Using Silicon Secured Memory and Oracle Developer Studio to Find and Fix Memory Access Errors

Version 7

    by Liang Chen, Raj Prakash, and Ikroop Dhillon

     

    This article provides example code that shows how to use Silicon Secured Memory with Oracle Developer Studio (previously known as Oracle Solaris Studio) to quickly find and fix a variety of memory access errors.

     

    About Silicon Secured Memory

     

    Oracle's new SPARC M7 processor offers new coengineered hardware and software capabilities that enable applications to run with the highest levels of security, reliability, and speed. This functionality is known as Oracle's "Software in Silicon."

     

    One key Software in Silicon feature is called Silicon Secured Memory. Silicon Secured Memory detects common memory access errors, thereby limiting runtime data corruption due to such errors. It can be used to detect the following types of memory access errors:

     

    • Buffer overflows
    • Unallocated memory or freed memory access errors
    • "Double free" memory access errors
    • Stale pointer memory access errors

     

    Such memory access errors can be caused by errant code or by a malicious attack on a server's memory. Buffer overflows are known to be a major source of security exploits, and in-memory databases increase an application's exposure to such errors because they can have terabytes of critical in-memory data.

     

    Silicon Secured Memory stops memory corruptions in optimized production code by adding extra "color" bits to the application's memory pointers and the memory they point to. The pointer color must match the content's color; otherwise, the memory access is aborted.

     

    It is important to understand that there is not a single color for the application. There have been solutions in the past from hardware vendors, such as IBM, that protect applications from each other by assigning each application a unique color. These types of solutions enable multiple applications to run on the same machine. However, they do not help detect the types of critical memory access errors detected by SSM within an application. With SSM, each memory block allocated by an application gets a different color, preventing errors—such as buffer overflows in a particular data structure of the application—from being exploited.

     

    Silicon Secured Memory works with applications written in systems-level programming languages (for example, C and C++), which are more vulnerable to memory corruption caused by software errors. Oracle provides updated malloc() library routines that will ensure adjacent data structures are given different colors. This allows SSM to establish and enforce the proper memory boundary for each allocated memory block and to ensure that each memory block is used for the purpose for which it was allocated, thereby preventing buffer overflows and other memory access vulnerabilities. It also allows SSM to prevent stale pointer accesses by detecting that memory content colors are changed when memory structures are freed.

     

    Traditionally, improvements in security have come with a trade-off of reduced performance.  However, since SSM is a hardware implementation, it incurs near-zero overhead and can be used in production to detect potential memory corruption issues. Also, it can be used during application development to ensure such errors are caught during application testing and certification.

     

    Memory corruption bugs are extremely difficult to find, because applications typically encounter corrupted data long after the corruption happens. Even after a memory error is detected, it is quite difficult to locate the cause of the problem in order to fix it.  The Oracle Developer Studio development tool suite has been updated to understand runtime-related memory access errors identified by SSM, and it provides root-cause analysis to help solve the challenging problem of protecting applications from memory leaks.

     

    In most cases, when a program is written with standard memory allocation routines, the program executable can run with SSM directly. In some rare cases when a program uses a custom memory allocator to allocate runtime memory extensively, the code needs to be modified slightly in order to catch any potential errors in the memory space allocated by the custom memory allocator.  The "Converting a Custom Memory Allocator" section of this article describes how to modify custom memory allocator code, and the "Example Code for a Custom Memory Allocator" section provides an example that uses pseudo code.

     

    SSM and Oracle Developer Studio

     

    Oracle Developer Studio is an optimized tool suite for the development of C, C++, and Fortran applications.  It includes record-setting compilers for the highest application performance and advanced performance, memory, and thread analysis tools.  The Code Analyzer is one of the advanced analysis tools in Oracle Developer Studio.  The Code Analyzer detects coding errors using static analysis when you compile your application, and it protects your application from memory errors with runtime dynamic analysis.  In addition, it dramatically increases code coverage with patented technology that ranks untested functions.

     

    The runtime dynamic analysis functionality of the Code Analyzer is called Discover.  Discover includes a library called libdiscoverADI.so, which enables Discover to understand and detect runtime-related memory errors identified by SSM.  Any C or C++ application can run with SSM memory error checking enabled by just preloading this library. Then, when a memory error is detected, Discover will print a comprehensive error analysis report.

     

    Furthermore, you can bring up the Code Analyzer GUI to browse and analyze the errors detected by SSM and by static analysis. The GUI also shows code coverage data, allowing you to understand which portions of code are exercised by SSM.

     

    f1.gif

    Figure 1: Code Analyzer GUI displaying a summary of runtime-related errors identified by SSM

     

    Using Discover and SSM to Find Memory Access Errors

     

    Discover and SSM can be used in two ways. You can preload a library by using the LD_PRELOAD_64 environment variable and then run your application. This will run all 64-bit binaries in the application in SSM mode, which might or might not be desirable.

     

    % LD_PRELOAD_64=<compiler>/lib/compilers/sparcv9/libdiscoverADI.so a.out

     

    LD_PRELOAD_64 is recommended over LD_PRELOAD, especially if the application involves a mix of 32-bit and 64-bit binaries.

     

    Alternatively, you can use the discover command with the -i adi flag to enable SSM on a specific binary. For example, the following commands will create a report in a.out.html.

     

    % discover -i adi a.out
    % a.out

     

    Errors Caught by Discover and SSM

     

    There are four kinds of errors the Discover library catches:

     

    1. Buffer overflow errors.

      This type of error happens when a program allocates memory using malloc(), say, 64 bytes, and then tries to read beyond that. Here is an example:

       

      int *area1 = malloc(sizeof(int)*16); for (int i = 0; i <= 16; i++)          area1[i] = 0;     // Array Out of Bounds

       

      See the code sample for a complete program that you can try.

       

    2. Freed memory access errors.

      This type of error happens when a program frees previously allocated memory and then tries to access it. Here is an example:

       

      free(area1); area1[0] = 0; // Freed memory access error

       

      See the code sample for a complete program that you can try.

       

    3. Stale pointer memory access errors.

      Stale pointer access errors are reported as freed memory access errors, because they are a special form of freed memory access errors. As with freed memory access errors, this type of error happens when a program frees previously allocated memory and then tries to access it, but in addition, an intervening allocation uses the memory for another purpose. Here is an example:

       

      int *area1 = malloc(sizeof(int)*16); free(area1); char *area3 = malloc(sizeof(char)*64); // area3 gets the memory area just  freed by area1 area1[0] = 0; // Stale Pointer Access

       

      See the code sample for a complete program that you can try.

       

    4. Double free memory access errors.

      This type of error happens when free() is called more than once for allocated memory, and it can result in errors that are difficult to diagnose. Here is an example:

       

      free(area3); free(area3); // double free

       

     

    Relevant Discover Flags

     

    The following flags will work with the discover command or with the SUNW_DISCOVER_OPTIONS runtime environment variable:

     

     

    FlagDescription
    -i adiSpecifies that memory checking be done using the SPARC M7 chip's SSM feature.
    -A [on|off]Specifies to turn on or off allocation/free stack traces. The default is on.
    -P [on|off]Specifies to turn on or off the precise SSM mode. The default is on.
    -w <file>Generates a text report in <file>. -w - will use stderr.
    -H <file>Generates an HTML report in <file>. (This is the default report style.)
    -aGenerates a report in <binary>.analyze in a format that is readable by Code Analyzer (and the equivalent codean command line tool).

     

    Code Sample and Sample Discover Output

     

    Assume the following sample test code resides in a file named t.c:

     

      1  #include <stdlib.h>   2  #include <stdio.h>   3  int main() {   4    int *area1 = malloc(sizeof(int)*16);   5    int *area2 = malloc(sizeof(int)*100);   6     7    for (int i = 0; i <= 16; i++)   8      area1[i] = 0;     // Array Out of Bounds   9    10    free(area1);  11    area1[0] = 0;       // Freed Memory Access  12      13    char *area3 = malloc(sizeof(char)*64);  14    if ((void *)area1 == (void *)area3)  15     printf("New area3 is same as old area1\n");  16    area1[0] = 0;       // Stale Pointer Access  17    18    free(area3);  19    free(area3);  20  21    return 0;  22  }

     

    You can build this test code by using the following command:

     

    $ cc t.c -g -m64

     

    You can then execute the sample test code with the SSM feature enabled by using the following command:

     

    $ discover -i adi -w - a.out
    $ a.out

     

    Doing so will generate the following output:

     

    ERROR 1 (ABW): writing to memory beyond array bounds at address  0x200000021047e040:         main() + 0x38  <t.c:8>                  5:      int *area2 = malloc(sizeof(int)*100);                  6:                      7:      for (int i = 0; i <= 16; i++)                  8:=>      area1[i] = 0;     // Array Out of Bounds                  9:                     10:      free(area1);                 11:      area1[0] = 0;    // Freed Memory Access Error         _start() + 0x108     was allocated at (64 bytes):         main() + 0x8  <t.c:4>                 1:    #include <stdlib.h>                 2:    #include <stdio.h>                 3:    int main() {                 4:=>    int *area1 = malloc(sizeof(int)*16);                 5:      int *area2 = malloc(sizeof(int)*100);                 6:                     7:      for (int i = 0; i <= 16; i++)         _start() + 0x108 ERROR 2 (FMW): writing to freed memory at address 0x200000021047e000:         main() + 0x6c  <t.c:11>                  8:        area1[i] = 0;     // Array Out of Bounds                  9:                     10:      free(area1);                 11:=>    area1[0] = 0;    // Freed Memory Access Error                 12:                     13:      char *area3 = malloc(sizeof(char)*64);                 14:      if ((void *)area1 == (void *)area3)         _start() + 0x108     was allocated at (64 bytes):         main() + 0x8  <t.c:4>                 1:    #include <stdlib.h>                 2:    #include <stdio.h>                 3:    int main() {                 4:=>    int *area1 = malloc(sizeof(int)*16);                 5:      int *area2 = malloc(sizeof(int)*100);                 6:                     7:      for (int i = 0; i <= 16; i++)         _start() + 0x108     freed at:         main() + 0x5c  <t.c:10>                  7:      for (int i = 0; i <= 16; i++)                  8:        area1[i] = 0;     // Array Out of Bounds                  9:                     10:=>    free(area1);                 11:      area1[0] = 0;    // Freed Memory Access Error                 12:                     13:      char *area3 = malloc(sizeof(char)*64);         _start() + 0x108 ERROR 3 (FMW): writing to freed memory at address 0x200000021047e000:         main() + 0xb4  <t.c:16>                 13:      char *area3 = malloc(sizeof(char)*64);                 14:      if ((void *)area1 == (void *)area3)                 15:       printf("New area3 is same as old area1\n");                 16:=>    area1[0] = 0;       // Stale pointer access                 17:                     18:      free(area3);                 19:      free(area3);         _start() + 0x108     was allocated at (64 bytes):         main() + 0x8  <t.c:4>                 1:    #include <stdlib.h>                 2:    #include <stdio.h>                 3:    int main() {                 4:=>    int *area1 = malloc(sizeof(int)*16);                 5:      int *area2 = malloc(sizeof(int)*100);                 6:                     7:      for (int i = 0; i <= 16; i++)         _start() + 0x108     freed at:         main() + 0x5c  <t.c:10>                  7:      for (int i = 0; i <= 16; i++)                  8:        area1[i] = 0;     // Array Out of Bounds                  9:                     10:=>    free(area1);                 11:      area1[0] = 0;    // Freed Memory Access Error                 12:                     13:      char *area3 = malloc(sizeof(char)*64);         _start() + 0x108 ERROR 4 (DFM): double freeing memory at address 0x300000021047e000:         main() + 0xc8  <t.c:19>                 16:      area1[0] = 0;       // Stale pointer access                 17:                     18:      free(area3);                 19:=>    free(area3);                 20:                     21:      return 0;                 22:    }         _start() + 0x108     was allocated at (64 bytes):         main() + 0x74  <t.c:13>                 10:      free(area1);                 11:      area1[0] = 0;    // Freed Memory Access Error                 12:                     13:=>    char *area3 = malloc(sizeof(char)*64);                 14:      if ((void *)area1 == (void *)area3)                 15:       printf("New area3 is same as old area1\n");                 16:      area1[0] = 0;       // Stale pointer access         _start() + 0x108     freed at:         main() + 0xbc  <t.c:18>                 15:       printf("New area3 is same as old area1\n");                 16:      area1[0] = 0;       // Stale pointer access                 17:                     18:=>    free(area3);                 19:      free(area3);                 20:                     21:      return 0;         _start() + 0x108 DISCOVER SUMMARY:         unique errors   : 4 (4 total)

     

    Converting a Custom Memory Allocator

     

    When an application has its own custom memory allocator, you can still run Discover with SSM without modifying the source code to check the large memory blocks allocated by the standard memory allocator. If you also want to check the small memory chunks allocated by the application's custom memory allocator, you need to modify the application source code. The change in source code is relatively minor in most cases. This section discusses what changes are needed.

     

    Requirements

     

    An application needs to meet the following requirements for you to be able to check for memory access errors using Discover with SSM:

     

    • The application binary must be built in 64-bit mode.
    • The application needs to enable SSM on the target memory area.
    • The allocated memory needs to be 64-byte aligned and its size must be multiple of 64.
    • The allocated area should be set to a version number (using the color bits described earlier), and the corresponding pointer value must be adjusted with the version number.

     

    Let's explore these requirements in a little more detail.

     

    The Application Binary Must Be Built in 64-Bit Mode

     

    SSM requires the memory pointer to be in 64 bits, because it embeds the version number in the high-order bits of the pointer. Therefore, the application must be built in 64-bit mode. Add the compilation flag -m64 when you compile your application using the C or C++ compilers of Oracle Developer Studio. All the linked libraries must be in 64-bit mode also.

     

    The Application Needs to Enable SSM on the Target Memory Area

     

    The program needs to call memcntl(address, size, MC_ENABLE_ADI, NULL, 0,0) to enable SSM on the memory area starting at the specified address with the specified size. Please note that both the address and the size must be PAGESIZE (8K) aligned. Here is an example:

     

    large_block_ptr = (large_block*) memalign(8192, 64 * 1024); if (memcntl(large_block_ptr, 64 * 1024, MC_ENABLE_ADI,NULL,0,0)!=0) {          error("memcntl call for enabling ADI failed"); }

     

    The Allocated Memory Needs to Be 64-Byte Aligned and Its Size Must Be a Multiple of 64

     

    SSM is implemented in hardware with the granularity of a 64-byte cache line. Therefore, any allocated memory chunk needs to be 64-byte aligned and its size must be multiple of 64, for example:

     

    object_ptr = (my_object*) my_malloc(sizeof(my_object)); needs to be changed to: adjusted_size = (sizeof(my_object) + 63) & ~63; // adjust to multiple of 64 object_ptr = (my_object*) my_malloc(64,adjusted_size);   // my_malloc() needs to make sure the allocated object address is 64-byte aligned

     

    The Allocated Area Should Be Set to a Version Number, and the Corresponding Pointer Value Must Be Adjusted with the Version Number

     

    When a memory chunk is allocated, a version number should be assigned to the area. It is important to have different version numbers for the adjacent areas. SSM supports version numbers from 0 to 13. The number 0 is reserved for the system to use.  The application can use the numbers from 1 to 13.  Here is an example (which is a continuation of the previous example):

     

    adjusted_object_ptr = (my_object*) adi_set_version(object_ptr, adjusted_size,  new_version_number); if (adjusted_object_ptr == NULL) report_error();

     

    Catching the SSM Signal

     

    When an application encounters a memory access error error, the running process is sent a segmentation fault signal. By default, this kills the process and produces a core file. However, the application can create a signal handler that catches the signal to customize the SSM error handling behavior. Below is an example of how to catch the signal and print the instruction address when an SSM version number mismatch occurs.

     

    // set up the signal handler at the beginning of the application process.  #include <signal.h>  struct sigaction sa; sa.sa_sigaction = sig_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; if (sigaction(SIGSEGV, &sa, NULL) == (int)SIG_ERR) {     printf("ERROR : sigset failed on SIGSEGV\n");     exit(1); }  /* signal handler which prints the pc of version mismatch */ void sig_handler(int sig, siginfo_t *info, void *uap) {   if (info->si_signo != SIGSEGV)     return;    caddr_t instr_addr;   ucontext_t *ucp = (ucontext_t*)uap;     if (info->si_code == SEGV_ADIPERR) {  // precise error     instr_addr = si_pc;    } else if (info->si_code == SEGV_ADIDERR) { // disruptive error     instr_addr = info->si_addr;   } else {     return;   }    fprintf(stderr, "Version mismatch detected at PC 0x%lx\n", instr_addr); }

     

    Example Code for a Custom Memory Allocator

     

    This section provides a custom memory allocator example program with SSM changes in pseudo code.

     

    Description of Custom Memory Allocator Example

     

    Name Buffer is an application-specific memory allocator that manages and stores large name strings. It supports two main routines:

     

    • allocNameBuffer(char* name): Allocates a permanent location in which to store a new name or returns the location of an existing name
    • freeNameBuffer(char* name): Frees the target name location

     

    When the allocNameBuffer() routine is called, it will first use a hash map to decide whether the input name has occurred before. If the input name has been stored, it will return a pointer to the existing name location. Otherwise, it will allocate a char array from a block buffer to store the new name, record the name in a hash map, and return a pointer to the new location. When a new input name requires an allocated place, it will check the remaining area of the last block first. Next, it will search for holes (the previously deleted places) in all the blocks. If a place still cannot be found, it will create a new block and allocate a place from the beginning area of the new block.

     

    When the freeNameBuffer() routine is called, it will determine whether the name string has already been stored. If the name is not found in the hash map, it will return status -1. If the name is found and the place is freed already, it will also return status -1. If the name is found and the place remains valid, the stored location will be marked as a freed hole by storing integer value -1 in the first 4 bytes, followed by an integer value indicating the freed size, and another integer value pointing to the next hole in the current block. Therefore, the allocated size is at least 12 bytes, even if the name string length plus the NULL terminating char is less than 12. For integer alignment, the allocated size must be adjusted to be a multiple of 4 when the value is larger than 12. The routine returns status 0 when the target location is freed successfully.

     

    Name Buffer SSM, Data Structure, and Pseudo Code

     

    SSM

     

    void initialization(); // internal routine char* allocNameBuffer(char* name); int freeNameBuffer(char* name);

     

    Data Structure

     

    int block_count; vector block_array; // unlimited array of block pointer class name_buf_block contains the following fields:     int avail_offset; // pointing to the next available place in the block buffer     int hole_offset; // pointing to the start of hole list in the block buffer     char[BUF_SIZE] buffer; // BUF_SIZE is 256 * 1024 hashmap name_hash; // hash map of pair(name: key, ID: value);     integer ID consists of: block id in high 14 bits and offset      in low 18 bits - ID(bidx, offset);

     

    Name Buffer Pseudo Code

     

    Initialization() {     // internal routine to set up Name Buffer facility     block_count = 1;     current_block = new name_buf_block();     current_block->avail_offset = 0;     current_block->hole_offset = -1;     block_array[0] = current_block; } char* allocNameBuffer(input_name) {      // find if the name has existed before     if ((found_hash_pair_p = name_hash.find(input_name)) != NULL &&         found_hash_pair_p->ID != -1) {             //find the name stored place and the place is valid.             return found_hash_pair_p->name; // return the address of the name              stored location     }     // input_name is either new or has been deleted, need to allocate a new place      for it     // Find a place from the last block first     int name_size = strlen(input_name) + 1;     if (name_size > BUF_SIZE) { error{"extreme size %d error", name_size);        return NULL; }     name_size = (name_size <= 12) ? 12 : ((name_size+3) & ~3); // minimum size        is 12 and multiple of 4     current_block = block_array[block_count - 1];     if (name_size <= BUF_SIZE - current_block->avail_offset - 1) {         // found the place           found_block_idx = block_count -1;           found_offset = current_block->avail_offset;          go to FOUND_PLACE;     }      // Find a place from the holes in the blocks     for (int blk_idx = 0; blk_idx <  block_count; blk_idx++) {         current_block = block_array[blk_idx];         int current_hole_offset = current_block->hole_offset;         int previous_hole_offset = -1;         while (current_hole_offset  != -1) {              int mark = *( (int*) (current_block->buffer +        current_hole_offset));              if (mark != -1) error("Internal logic error.\n");              int hole_size = *(int*) (current_block->buffer +        current_hole_offset +4);              int next_hole_offset =  *(int*) (current_block->buffer +        current_hole_offset + 8);              if (name_size <= hole_size) { // hole is big enough                    // remove this hole from the hole list in current block                   if (previous_hole_offset == -1)                        current_block->hole_offset = next_hole_offset;                   else                        *(int*) (current_block->buffer + previous_hole_offset + 8) =       next_hole_offset;                       found_block_idx = blk_idx;                    found_offset = current_hole_offset;                    go to FOUND_PLACE;               }               previous_hole_offset = current_hole_offset;               current_hole_offset = next_hole_offset;         } // end of while loop     }  // end of for loop      // Cannot find a place so far, need to create a new block     new_block = new name_buf_block();     new_block->avail_offset = name_size;     new_block->hole_offset = -1;     block_array[block_count] = new_block;        found_block_idx = block_count ++;     found_offset = 0;  FOUND_PLACE:     new_name_loc = block_array[found_block_idx]->buffer + found_offset;     strcpy(new_name_loc, input_name);     new_ID(bidx, offset) = (found_block_idx, found_offset);     if ((found_hash_pair_p = name_hash.find(new_name_loc)) != NULL)            (*found_hash_pair_p)(name, ID)=pair(new_name_loc, new_ID);     else                    name_hash.insert(pair(new_name_loc, new_ID);          return new_name_loc;  } int freeNameBuffer(input_name) {      // find the name place     if ((found_hash_pair_p = name_hash.find(input_name)) == NULL ||         found_hash_pair_p->ID == -1) {         // the name stored place is not found or the place has been freed.         return -1; // return the failure status     }     // Mark the place as deleted and add it to the hole list of the holding block     int name_size = strlen(input_name) + 1;     name_size = (name_size <= 12) ? 12 : ((name_size+3) & ~3); // minimum size       is 12 and multiple of 4     int block_idx = found_hash_pair_p->ID(bidx);     int offset = found_hash_pair_p->ID(offset);     current_block = block_array[block_idx];     *(int*)(current_block->buffer + offset) = -1;     *(int*)(current_block->buffer + offset + 4) = name_size;     *(int*)(current_block->buffer + offset + 8) = current_block->hole_offset;      current_block->hole_offset = offset;      found_hash_pair_p->ID = -1;     return (0); }

     

    How to Modify the Name Buffer Memory Allocator for SSM

     

    Assign the Version Number

     

    The SSM version number is implemented with 4 bits in hardware. Application programs can use version numbers from 1 to 13. Numbers 0, 14, and 15 are reserved for system usage.

     

    In this Name Buffer allocator example, we will use the version numbers from 1 to 13 as follows:

     

    • 1: Used for the area of the block object including the block buffer
    • 2 through 7: Used for the allocated name locations inside the block buffer
    • 8 through 13: Used for the freed name locations inside the block buffer
    • Mapping: 2 through 8, 3 through 9, 4 through 10, 5 through 11, 6 through 12, and 7 through 13

     

    Change the Name Buffer Data Structure for SSM

     

    • A new integer, current_version, needs to be added to Name Buffer block object.
    • A buffer field of char[BUF_SIZE] needs to be reordered as the first data field in the block object to meet the 64-byte alignment requirement.
    • Now, class name_buf_block should contain the following fields in the following order:

       

      • char[BUF_SIZE] buffer; // buffer must be the first field in the object
      • int avail_offset;
      • int hole_offset;
      • int current_version; // new field for the version number assignment

     

    Change the Code of Name Buffer for SSM

     

    Change_Begin: add signal handling code at the beginning of application code #include <signal.h>  struct sigaction sa; sa.sa_sigaction = sig_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; if (sigaction(SIGSEGV, &sa, NULL) == (int)SIG_ERR) {     printf("ERROR : sigset failed on SIGSEGV\n");     exit(1); }  /* signal handler which prints the pc of version mismatch */ void sig_handler(int sig, siginfo_t *info, void *uap) {   if (info->si_signo != SIGSEGV)     return;    caddr_t instr_addr;   ucontext_t *ucp = (ucontext_t*)uap;     if (info->si_code == SEGV_ADIPERR) {  // precise error     instr_addr = si_pc;    } else if (info->si_code == SEGV_ADIDERR) { // disruptive error     instr_addr = info->si_addr;   } else {     return;   }    fprintf(stderr, "Version mismatch detected at PC 0x%lx\n", instr_addr); }Change_End
    Initialization() {     // internal routine to set up Name Buffer facility     block_count = 1;     current_block = new name_buf_block();
    Change_Begin: the above name_buf_block construction statement needs to be  replaced with the following:         // adjust allocated block size to multiple of 8192 (8K) bytes to enable ADI.     new_block_size = (sizeof(name_buf_block) + 8191) & ~8191;     // allocate and align the block address to 8192 byte boundary        tmp_ptr =  memalign(8192, new_block_size);       if (tmp_ptr == NULL) error("failed to align and allocate.\n");     // enable ADI on the new block area.     if (memcntl(tmp_ptr, new_block_size, MC_ENABLE_ADI, NULL, 0,0)!= 0) {         error("failed to enable ADI.\n"); }     // set version number 1 to the block area and save the adjusted pointer  value to current_block     current_block = (name_buf_block*) adi_set_version(tmp_ptr, new_block_size, 1);     if (current_block == NULL) error("failed to set version number.\n");     // initialize block current_version to 2     current_block->current_version = 2;Change_End
        current_block->avail_offset = 0;     current_block->hole_offset = -1;     block_array[0] = current_block; } char* allocNameBuffer(input_name) {      // find if the name has existed before     if ((found_hash_pair_p = name_hash.find(input_name)) != NULL &&         found_hash_pair_p->ID != -1) {             return found_hash_pair_p->name; // return the address of the name  stored location     }     // input_name is either new or has been deleted, need to allocate a new  place for it     // Find a place from the last block first     int name_size = strlen(input_name) + 1;     if (name_size > BUF_SIZE) { error{"extreme size %d error", name_size);  return NULL; }     name_size = (name_size <= 12) ? 12 : ((name_size+3) & ~3); // minimum size is 12 and multiple of 4
    Change_Begin: the above name_size statement needs to be replaced with the  following:     // name size needs to be adjusted to multiple of 64 bytes     name_size = (name_size + 63) & ~63;Change_End
        current_block = block_array[block_count - 1];     if (name_size <= BUF_SIZE - current_block->avail_offset - 1) {         // found the place           found_block_idx = block_count -1;           found_offset = current_block->avail_offset; Change_Begin: add the following:         // assign a new version for the found place         found_version = current_block->current_version;         // increase current version and rotate back to 2 when it reaches 8         if (++current_block->current-version == 8) current_block->current_version = 2;              // set the size of the new found place         found_size = name_size;Change_End
             go to FOUND_PLACE;     }     // Search for a place among the holes in the blocks     for (int blk_idx = 0; blk_idx <  block_count; blk_idx++) {         current_block = block_array[blk_idx];         int current_hole_offset = current_block->hole_offset;         int previous_hole_offset = -1;         while (current_hole_offset  != -1) {              int mark = *( (int*) (current_block->buffer + current_hole_offset));              if (mark != -1) error("Internal logic error.\n");              int hole_size = *(int*)(current_block->buffer + current_hole_offset +4);              int next_hole_offset =  *(int*) (current_block->buffer +  current_hole_offset + 8);              if (name_size <= hole_size) { // hole is big enough                    // remove this hole from the hole list in current block                    if (previous_hole_offset == -1)                        current_block->hole_offset = next_hole_offset;                    else                        *(int*)(current_block->buffer + previous_hole_offset + 8) =  next_hole_offset;                       found_block_idx = blk_idx;                    found_offset = current_hole_offset; Change_Begin: add the following:                   // get the version number from the current freed place.                   old_version = adi_get_version(current_block->buffer +  current_hole_offset);                   if (old_version < 8 || old_version > 13) error("failed to get  version number, bad number  %d returned.\n", old_version);                   // new number needs to be different from (old_version-6) and  the versions of neighboring chunks.                   // old dangling pointer could still be around with version  number of (old_version - 6)                   found_version = a new number which is different from  (old_version - 6) and the neighboring versions;                   // the found size is the entire hole size regardless of the  name size                   found_size = hole_size;Change_End
                      go to FOUND_PLACE;               }               previous_hole_offset = current_hole_offset;               current_hole_offset = next_hole_offset;         } // end of while loop     }  // end of for loop      // Cannot find a place so far, need to create a new block     new_block = new name_buf_block();
    Change_Begin: similar to Initialization routine, the above  name_buf_block construction statement needs to be replaced with the following:         // adjust allocated block size to multiple of 8192 (8K) bytes     new_block_size = (sizeof(name_buf_block) + 8191) & ~8191;     // allocate and align the block address to 8192 byte boundary        tmp_ptr =  memalign(8192, new_block_size);       if (tmp_ptr == NULL) error("failed to align and allocate memory.\n");     // enable ADI on the new block area.     if (memcntl(tmp_ptr, new_block_size, MC_ENABLE_ADI, NULL, 0,0)!= 0) {         error("failed to enable ADI.\n"); }     // set version number 1 to the block area and save the adjusted pointer  value to new_block     new_block = (name_buf_block*) adi_set_version(tmp_ptr, new_block_size, 1);     if (new_block == NULL) error("failed to set version number.\n");     // initialize block current_version to 2     new_block->current_version = 2;Change_End
        new_block->avail_offset = name_size;     new_block->hole_offset = -1;     block_array[block_count] = new_block;        found_block_idx = block_count ++;     found_offset = 0; Change_Begin: add the following:     found_version = 2;     found_size = name_size;     new_block->current_version = 3;Change_End
    FOUND_PLACE:
        new_name_loc = block_array[found_block_idx]->buffer + found_offset;
    Change_Begin: replace the above statement with the following:     found_place = block_array[found_block_idx]->buffer + found_offset;     // set the new area with the assigned version number and the size     new_name_loc = (char*) adi_set_version(found_place, found_size, found_version);     if (new_name_loc == NULL) error("failed to set version number.\n");Change_End
        strcpy(new_name_loc, input_name);     new_ID(bidx, offset) = (found_block_idx, found_offset);     if ((found_hash_pair_p = name_hash.find(new_name_loc)) != NULL)            *(found_hash_pair_p(name, ID)) = pair(new_name_loc, new_ID);     else                    name_hash.insert(pair(new_name_loc, new_ID);          return new_name_loc; }  int freeNameBuffer(input_name) {      // find the name place     if ((found_hash_pair_p = name_hash.find(input_name) == NULL ||         found_hash_pair_p->ID == -1) {         // the name stored place is not found or the place has been freed.         return -1; // return the failure status     }     // Mark the place as deleted and add it to the hole list of the holding block     int name_size = strlen(input_name) + 1;     name_size = (name_size <= 12) ?
    12 : ((name_size+3) & ~3); // minimum size is 12 and multiple of 4
    Change_Begin: the above name_size statement needs to be replaced with the following:     // name size needs to be adjusted to a multiple of 64 bytes     name_size = (name_size + 63) & ~63;Change_End
        int block_idx = found_hash_pair_p->ID(bidx);     int offset = found_hash_pair_p->ID(offset);     current_block = block_array[block_idx];     *(int*)(current_block->buffer + offset) = -1;     *(int*)(current_block->buffer + offset + 4) = name_size;     *(int*)(current_block->buffer + offset + 8) = current_block->hole_offset;     current_block->hole_offset = offset;     found_hash_pair_p->ID = -1; Change_Begin: add the following:     // get the version number from the current allocated place     old_version = adi_get_version(current_block->buffer + offset);     if (old_version < 2 || old_version > 7) error("failed to get version  number, bad number %d returned.\n", old_version);     // the new version number is mapped from the old version by adding 6     new_version = old_version + 6;     // set the freed area with the new version number     freed_name_loc = (char*) adi_set_version(current_block->buffer + offset,  name_size, new_version);     if (freed_name_loc == NULL) error("failed to set version number.\n");     found_hash_pair_p->name = freed_name_loc;Change_End
        return (0); }

     

    Conclusion

     

    Very subtle memory errors in code can lead to serious security vulnerabilities, costing companies significant losses in revenue, data, and good will.  Silicon Secured Memory is one of the revolutionary Software in Silicon features that will help you find and fix memory corruption errors during development and testing, and ensure the integrity of your data during deployment—all with near-zero performance overhead.

     

    In addition to SSM dynamic analysis, you can also use the other Code Analyzer features to minimize the possibility of memory errors in your program. Code Coverage analysis helps you dramatically increase code coverage with unique patented technology that ranks untested functions, allowing you to easily identify unexercised code.  The Code Analyzer also provides static analysis, which enables you to detect coding errors when compiling your application.  Static analysis performs exhaustive memory error checking throughout your entire program without the need for a test set

     

    See Also

     

    More information on Code Analyzer

     

    About the Authors

     

    liang.jpg

     

     

     

     

     

    Liang Chen is a distinguished engineer and architect for Oracle's Platform Developer Tools group. His focus is the architecture and future technology of Oracle Developer Studio developer tools. Liang has more than two decades of application development experience. His current main interest is to investigate parallel application development from tightly coupled OpenMP to loosely coupled SOA and Grid.

     

     

    raj.jpg

     

     

     

     

     

    Raj Prakash is a software architect and developer at Oracle. Currently he is the technical lead for several code analysis tools and global optimizers. His expertise is in the architecture, performance, and scalability of development tools and Java Virtual Machines. He also writes a blog.

     

     

     

     

    ikroop.jpeg

     

     

     

     

     

    Ikroop Dhillon is the principal product manager for Oracle Developer Studio, responsible for business development, product strategy, go-to-market plans, and sales enablement. Ikroop has an educational background in computer science and professional background in technical consulting, technical marketing, and product management.

     

     

     

     

     

     

    Revision 1.0, 03/16/2015

     

     

    Follow us:
    Blog | Facebook | Twitter | YouTube