Go to main content
Oracle Developer Studio 12.5 Man Pages

Exit Print View

Updated: June 2017
 
 

libadiplugin (3)

Name

libadiplugin - APIs for use by custom memory allocators to detect memory corruptions with Silicon Secured Memory (SSM)

Synopsis

#include <adiplugin.h>

Description

DESCRIPTION

Enterprise applications can often manage their own memory and do not use the system (libc) malloc(3C) library. For these cases, the normal usage of Oracle Solaris libadimalloc (3LIB) , which interposes on malloc(), free(), etc will not be able to catch memory corruptions. Oracle Developer Studio provides APIs so the enterprise applications can tell libadiplugin.so when a memory area is allocated or freed. libadiplugin.so manages the SSM versioning. The system will issue a SEGV when a corruption is detected. These APIs and libadiplugin.so only work for memory allocated by mmap(2) or shmget(2). These are idential to the APIs provided with the discover ADI tool, but are designed for use in production systems.

The following APIs are provided:

void *adi_mark_malloc(void *ptr, size_t size);

The user allocator passes a pointer ptr and the size size of the memory about to be returned to the client requesting memory. If the memory does not have ADI enabled, the library uses a memcntl(2) call. An SSM versioned pointer is returned, which the allocator must pass to the client.

void adi_mark_free(void *ptr, size_t size);

The allocator passes a pointer ptr and the size size to the memory area about to be freed.

void *adi_unversion(void *ptr);

Allocators can use pointer arithmetic to access a client memory's meta data. Client memory will be SSM versioned, but allocator meta data might not. This API should be used before any pointer arithmietic or comparison is done. The allocator passes any pointer ptr and returns an equivalent unversioned pointer.

void *adi_version(void *ptr);

Sometimes allocators lose track of versioned pointers. This API takes in any pointer ptr, and returns an equivalent pointer with the correct version on it. Any dereference with the return value will not cause an ADI SEGV.

void *adi_clear_version(void * ptr, size_t size);

If a memory area is being reused for a different purpose, the ADI version needs to be cleared. For example, if a memory area was versioned for use by an allocator client, and is now being used by the allocator for meta-data. This function takes in a versioned or unversioned pointer ptr and size size, sets the version for that area to zero, and returns an unversioned pointer.

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 (https://community.oracle.com/docs/DOC-912448) for information about how you can use the ADI versioning APIs to catch errors with your managed memory.

Examples

Using Custom Memory Allocators

To use these APIs, include the following header file in your sources:

#include <adiplugin.h>

Then, do one of the following:

  • Link your sources with install-dir/lib/compilers/sparcv9/libadiplugin.so

  • Set the environment variable LD_PRELOAD_64 to install-dir/lib/compilers/sparcV9/libadiplugin.so

The following is an example of using custom memory allocators.

Example 1 Using Custom Memory Allocators

The following is an example header file:

% cat allocator.h
#define GRANULARITY 32

void *mymalloc(size_t len);
void myfree(void *ptr);

The following is example source code, using custom memory allocators and the libadiplugin library:

% cat allocator.c
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <errno.h>
#include <dlfcn.h>
#include <unistd.h>
#include <assert.h>
 
#include "allocator.h"
 
#include "adiplugin.h"
 
#pragma init (setup_allocator)
#pragma fini (takedown_allocator)
 
#define MAX_ALLOCATIONS 1024
#define START_ADDRESS 0x200000000
 
uint64_t next_available_address = 0;
size_t allocation_table[MAX_ALLOCATIONS/GRANULARITY];
static void setup_allocator() {
  // mmap with a specific address
  void *addr = mmap((void *) START_ADDRESS, MAX_ALLOCATIONS, 
                    PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
  if (addr == MAP_FAILED) {
    fprintf(stderr, "mmap failed {%d}\n", errno);
    exit(1);
  }

  next_available_address = (uint64_t) addr;
}

static void takedown_allocator() {
  if (munmap((void *) START_ADDRESS, MAX_ALLOCATIONS) != 0) {
    fprintf(stderr, "munmap failed {%d}\n", errno);
    exit(1);
  }
}
// Simple malloc
void *mymalloc(size_t size) {

  void *vaddr = (void *) next_available_address;
  next_available_address += size;

  assert(next_available_address < (START_ADDRESS+MAX_ALLOCATIONS));

  int index = ((uint64_t)vaddr-START_ADDRESS)/GRANULARITY;
  allocation_table[index] = size;

  // Tell libadiplugin.so about the allocation, get a versioned 
  // pointer
  vaddr = adi_mark_malloc(vaddr, size);

  // Return the versioned pointer
  return vaddr;
}

// Simple free
void myfree(void *ptr) {
  int index = ((uint64_t)ptr-START_ADDRESS)/GRANULARITY;
  size_t size = allocation_table[index];

  // Tell libadiplugin.so about the free().
  adi_mark_free(ptr, size);
}
% cat simple.c
#include <stdio.h>
#include <stdlib.h>

#include "allocator.h"

int main() {
  // Allocate
  char *buf1 = (char *) mymalloc(GRANULARITY*2);
  buf1[0] = 'x';

  // Read before free
  printf("Read buf1[0] before free: [0x%p] %c\n", buf1, buf1[0]);

  // Allocate
  char *buf2 = (char *) mymalloc(GRANULARITY*2);

  // Buf1fer overflow buf1
  printf("Read buf1[%d] past end: [0x%p] %c\n", GRANULARITY*2, 
	 buf1+GRANULARITY*2, buf1[GRANULARITY*2]);

  // Free
  myfree(buf1);

  // Read after free
  printf("Read buf1[0] after free: [0x%p] %c\n", buf1, buf1[0]);

  return 0;
}

To compile and run this example:

% cc –m64 –g simple.c allocator.c install-dir/lib/compilers/sparcv9/libadiplugin.so –o simple
% ./simple

Usage

Requirements and Limitations

You can use libadiplugin.so only with 64-bit applications on a SPARC M7 chip running at least Oracle Solaris 11.2.8 or Oracle Solaris 11.3.

Similar to instrumenting for memory checking, preloaded libraries might conflict if functions of libadiplugin.so interpose on the same allocation functions.

Other limitations for checking your code with .so 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 libadiplugin.so 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.

  • Resolution for buffer overflow is 64 bytes. For allocations that are 64-byte-aligned, libadiplugin.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 libadiplugin.so places the allocation in the cache line.

  • Memory allocations that are not 64-byte aligned or not multiples of 64-bytes will not be SSM versioned. This could lead to memory corruptions not being detected.

  • 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 SSM errors and as a result also lead to performance degradation due to trap handling.

See Also

libadimalloc (3LIB) , Oracle Developer Studio 12.5: Overview, Oracle Developer Studio 12.5: Discover and Uncover User’s Guide, discover (1)