Oracle® Solaris Studio 12.4: Discover and Uncover User's Guide

Exit Print View

Updated: December 2015
 
 

Hardware-Assisted Checking Using Silicon Secured Memory (SSM)

The SPARC M7 processor from Oracle offers Software in Silicon, which enables software to run faster and more reliably. One Software in Silicon feature is Silicon Secured Memory (SSM), previously called Application Data Integrity (ADI), whose circuitry detects common memory access errors that can cause run-time data corruption.

These errors can be caused by errant code or a malicious attack on a server's memory. For example, buffer overflows are known to be a major source of security exploits. Further, in-memory databases increase an application's exposure to such errors due to having critical data in-memory.

Silicon Secured Memory stops memory corruptions in optimized production code by adding version numbers to the application's memory pointers and the memory they point to. If the pointer version number does not match the content version number, the memory access is aborted. Silicon Secured Memory works with applications written in systems-level programming languages such as C or C++, which are more vulnerable to memory corruption caused by software errors.

Oracle Solaris Studio 12.4 4/15 Platform Specific Enhancement (PSE) includes the libdiscoverADI.so library (also referred to as the discover ADI library), which provides updated malloc() library routines that ensure that adjacent data structures are given different version numbers. These version numbers enable the processor's SSM technology to detect buffer overflows. Memory content version numbers are changed when memory structures are freed to prevent stale pointer accesses. For more information about the errors caught by discover and libdiscoverADI.so, see Errors Caught by libdiscoverADI.

In addition to using Silicon Secured Memory in production to detect potential memory corruption issues, you can use it during application development to ensure that such errors are caught during application testing and certification. Memory corruption bugs are extremely hard to find because applications encounter corrupted data long after the corruption happens. The discover tool and the libdiscoverADI.so library, part of the Oracle Solaris Studio developer tool suite, provide you with additional application information that makes locating and fixing the errant code easier.

Using the libdiscoverADI Library to Find Memory Access Errors

The discover ADI library libdiscoverADI reports programming errors that result in invalid memory accesses. You can use in it two ways:

  • By preloading the discover ADI library into your application with the LD_PRELOAD_64. environment variable. This method runs all 64-bit binaries in the application in ADI mode, For example, if you normally run an application named server, the command would be as follows:

    $ LD_PRELOAD_64=install-dir/lib/compilers/sparcv9/libdiscoverADI.so server
  • By using ADI mode with the discover command with the –i adi option on a specific binary.

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

The errors are reported in an a.out.html file by default. For more information about discover reports, see Analyzing discover Reports and Output Options.

See Requirements and Limitations of Using libdiscoverADI.

Errors Caught by libdiscoverADI

The libdiscoverADI.so library catches the following errors:

  • Array out of Bounds Access (ABR/ABW)

  • Freed Memory Access (FMR/FMW)

  • Stale Pointer Access (A special type of FMR/FMW)

  • Unallocated Read/Write (UAR/UAW)

  • Double Free Memory (DFM)

For more information about each of these types of errors, see Memory Access Errors and Warnings.

Your application might manage its own memory allocation and free lists, for example by allocating large chunks of memory and subdividing it in your program. See Using Application Data Integrity and Oracle Solaris Studio to Find and Fix Memory Access Errors for information about how you can use the ADI versioning APIs to catch errors with your managed memory.

For a full example, see Example of Using discover ADI Mode.

Instrumentation Options for discover ADI mode

The following options determine the precision and amount of information generated in the discover report when instrumenting with ADI mode.

–A [on | off]

When this flag is set to on, the discover ADI library reports the location of the error and the error stack trace. This information is sufficient to catch the error, but it is not always sufficient to fix the error. This flag also generates information about where the offending memory area was allocated and freed. For example, the output might say that an error was an Array out of Bounds Access and where that array was allocated. If set to off, allocations and stack trace are not reported. The default is on.


Note -  Even if –A is set to on, it is possible that ABR/ABW might sometimes be reported as FMR/FMW or UAR/UAW, due to one of the following reasons:
  • If the buffer overflow access happens at a large offset after the end of the buffer or before the beginning of the buffer.

  • If libdiscoverADI.so hits a resource limit. In this case, discover might be able to keep the allocation stack trace that is needed to determine if the error is a buffer overflow.


–P [on | off]

When this flag is set to off, ADI is run in non-precise mode. In non-precise mode, memory write errors are caught a few instructions (source lines) after the exact instruction is executed. To enable Precise mode, set this flag to on, which is the default.

For better runtime performance, you can specify –A off, –P off, or both options can be set to off.

Requirements and Limitations of Using libdiscoverADI

You can use ADI mode of discover only with 64-bit applications on a SPARC M7 chip running at least Oracle Solaris 11.2.8 or Oracle Solaris 11.3 with the Oracle Solaris Studio 12.4 4/15 Platform Specific Enhancement (PSE) installed.

Similar to instrumenting for memory checking, preloaded libraries might conflict if functions of libdiscoverADI.so interpose on the same allocation functions. See Binaries That Use Preloading or Auditing Are Incompatible for more information.

Other limitations for checking your code with libdiscoverADI include the following:

  • Only heap-checking is available. There is no stack checking, no static array-out-of-bounds checking, and no leak detection.

  • Does not work with applications which use the unused bits in 64-bit addresses for storing meta data. Some 64-bit applications might use the currently unused high bits in 64-bit addresses for storing meta-data, for instance, locks. Such applications will not work with discover in ADI mode because the feature works by using the 4 highest bits in the 64-bit address to store version information.

  • Might not work for applications that do pointer arithmetic with assumptions about heap addresses, for example, the distance between two successive allocations.

  • Unlike in memcheck mode (instrumenting with –i memcheck), ADI mode does not catch errors if the application redefines standard memory allocation functions in the executable. If the application redefines the standard memory allocation functions in a library, then ADI mode works.

  • Resolution for buffer overflow is 64 bytes. For allocations that are 64-byte-aligned, libdiscoverADI.so will catch any overflow by 1 byte or more. For allocations that are not aligned at 64 bytes, it might miss the buffer overflow by a few bytes. In general, overflow by 1 to 63 bytes might not be caught depending on the alignment of the allocation and where libdiscoverADI.so places the allocation in the cache line.

  • There is a slight chance that binaries compiled with –xipo=2 might have a memory-optimized code that manipulates addresses in a way that will lead to false positive ADI errors and as a result also lead to performance degradation due to trap handling.

Example of Using discover ADI Mode

This section provides a code sample with Array-out-of-bounds errors, which are then caught and reported by discover using ADI mode.

Assume the following sample code resides in a file named testcode.c.

#include <stdio.h>
#include <stdlib.h>

int main() {

  char *x = (char*)malloc(512);
  int *y = (int*)malloc(20*sizeof(int));
  char *z = (char*)malloc(64);

  x[-14] = 0;
  y[-10] = 0;
  z[-4] = 0;
  x[16] = 0;
  y[20] = 0;
  z[64] = 0;
  x[20] = 0;
  y[26] = 0;
  z[120] = 0;

}

You would build the test code with the following command:

$ cc testcode.c -g -m64

To execute this sample application with ADI mode, use the following command:

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

This command generates the following output, in a discover report. For more information about reading and understanding these reports, see Analyzing discover Reports.

ERROR 1 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e877ff2:
        main() + 0x3c  <test-abrw.c:10>
                 7:      int *y = (int*)malloc(20*sizeof(int));
                 8:      char *z = (char*)malloc(64);
                 9:    
                10:=>    x[-14] = 0;
                11:      y[-10] = 0;
                12:      z[-4] = 0;
                13:      x[16] = 0;
        _start() + 0x108 
    was allocated at (512 bytes):
        main() + 0x8  <test-abrw.c:6>
                3:    
                4:    int main() {
                5:    
                6:=>    char *x = (char*)malloc(512);
                7:      int *y = (int*)malloc(20*sizeof(int));
                8:      char *z = (char*)malloc(64);
                9:    
        _start() + 0x108 
ERROR 2 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e873ffc:
        main() + 0x50  <test-abrw.c:12>
                 9:    
                10:      x[-14] = 0;
                11:      y[-10] = 0;
                12:=>    z[-4] = 0;
                13:      x[16] = 0;
                14:      y[20] = 0;
                15:      z[64] = 0;
        _start() + 0x108 
    was allocated at (64 bytes):
        main() + 0x28  <test-abrw.c:8>
                 5:    
                 6:      char *x = (char*)malloc(512);
                 7:      int *y = (int*)malloc(20*sizeof(int));
                 8:=>    char *z = (char*)malloc(64);
                 9:    
                10:      x[-14] = 0;
                11:      y[-10] = 0;
        _start() + 0x108 
ERROR 3 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e876080:
        main() + 0x64  <test-abrw.c:14>
                11:      y[-10] = 0;
                12:      z[-4] = 0;
                13:      x[16] = 0;
                14:=>    y[20] = 0;
                15:      z[64] = 0;
                16:      x[20] = 0;
                17:      y[26] = 0;
        _start() + 0x108 
    was allocated at (128 bytes):
        main() + 0x18  <test-abrw.c:7>
                 4:    int main() {
                 5:    
                 6:      char *x = (char*)malloc(512);
                 7:=>    int *y = (int*)malloc(20*sizeof(int));
                 8:      char *z = (char*)malloc(64);
                 9:    
                10:      x[-14] = 0;
        _start() + 0x108 
ERROR 4 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e874040:
        main() + 0x70  <test-abrw.c:15>
                12:      z[-4] = 0;
                13:      x[16] = 0;
                14:      y[20] = 0;
                15:=>    z[64] = 0;
                16:      x[20] = 0;
                17:      y[26] = 0;
                18:      z[120] = 0;
        _start() + 0x108 
    was allocated at (64 bytes):
        main() + 0x28  <test-abrw.c:8>
                 5:    
                 6:      char *x = (char*)malloc(512);
                 7:      int *y = (int*)malloc(20*sizeof(int));
                 8:=>    char *z = (char*)malloc(64);
                 9:    
                10:      x[-14] = 0;
                11:      y[-10] = 0;
        _start() + 0x108 
ERROR 5 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e876098:
        main() + 0x84  <test-abrw.c:17>
                14:      y[20] = 0;
                15:      z[64] = 0;
                16:      x[20] = 0;
                17:=>    y[26] = 0;
                18:      z[120] = 0;
                19:    
                20:    }
        _start() + 0x108 
    was allocated at (128 bytes):
        main() + 0x18  <test-abrw.c:7>
                 4:    int main() {
                 5:    
                 6:      char *x = (char*)malloc(512);
                 7:=>    int *y = (int*)malloc(20*sizeof(int));
                 8:      char *z = (char*)malloc(64);
                 9:    
                10:      x[-14] = 0;
        _start() + 0x108 
ERROR 6 (ABW): writing to memory beyond array bounds at address 0x2fffffff7e874078:
        main() + 0x90  <test-abrw.c:18>
                15:      z[64] = 0;
                16:      x[20] = 0;
                17:      y[26] = 0;
                18:=>    z[120] = 0;
                19:    
                20:    }
                21:    
        _start() + 0x108 
    was allocated at (64 bytes):
        main() + 0x28  <test-abrw.c:8>
                 5:    
                 6:      char *x = (char*)malloc(512);
                 7:      int *y = (int*)malloc(20*sizeof(int));
                 8:=>    char *z = (char*)malloc(64);
                 9:    
                10:      x[-14] = 0;
                11:      y[-10] = 0;
        _start() + 0x108 
DISCOVER SUMMARY:
        unique errors   : 6 (6 total)