blob: 1c109944e204c1e24d01beb7a0ef8bfb6914d790 [file] [log] [blame]
//===- BaggyBoundsCheck.cpp - Implementation of poolallocator runtime -===//
//
// The SAFECode Compiler
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is one possible implementation of the LLVM pool allocator runtime
// library.
//
// Uses Baggy Bounds Checking
//
//===----------------------------------------------------------------------===//
// NOTES:
// 1) Some of the bounds checking code may appear strange. The reason is that
// it is manually inlined to squeeze out some more performance. Please
// don't change it.
//
//===----------------------------------------------------------------------===//
#include "ConfigData.h"
#include "DebugReport.h"
#include "safecode/Runtime/BBRuntime.h"
#include <cstring>
#include <cassert>
#include <cstdio>
#include <cstdarg>
// This must be defined for Snow Leopard to get the ucontext definitions
#if defined(__APPLE__)
#define _XOPEN_SOURCE 1
#endif
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <ucontext.h>
#include <sys/mman.h>
#define TAG unsigned tag
#define DEBUG(x)
NAMESPACE_SC_BEGIN
struct ConfigData ConfigData;
NAMESPACE_SC_END
using namespace NAMESPACE_SC;
/// UNUSED in production version
FILE * ReportLog;
// Configuration for C code; flags that we should stop on the first error
unsigned StopOnError;
// signal handler
static void bus_error_handler(int, siginfo_t *, void *);
unsigned SLOT_SIZE = 4;
unsigned WORD_SIZE = 64;
unsigned char * __baggybounds_size_table_begin;
const size_t table_size = 1L << 43;
//===----------------------------------------------------------------------===//
//
// Baggy Bounds Pool allocator library implementation
//
//===----------------------------------------------------------------------===//
void *
__sc_bb_poolinit(DebugPoolTy *Pool, unsigned NodeSize, unsigned) {
return Pool;
}
void
__sc_bb_pooldestroy(DebugPoolTy *Pool) {
return;
}
//
// Function: pool_init_runtime
//
// Description:
// This function is called to initialize the entire SAFECode run-time. It
// configures the various run-time options for SAFECode and performs other
// initialization tasks.
//
// Inputs:
// Dangling - Set to non-zero to enable dangling pointer detection
// RewriteOOB - Set to non-zero to enable Out-Of-Bounds pointer rewriting.
// Terminate - Set to non-zero to have SAFECode terminate when an error
// occurs.
//
void
pool_init_runtime(unsigned Dangling, unsigned RewriteOOB, unsigned Terminate) {
//
// Initialize the signal handlers for catching errors.
//
ConfigData.RemapObjects = Dangling;
ConfigData.StrictIndexing = !(RewriteOOB);
StopOnError = Terminate;
//
// Allocate a range of memory for rewrite pointers.
//
//
// Leave initialization of the Report logfile to the reporting routines.
// The libc stdio functions may have not been initialized by this point, so
// we cannot rely upon them working.
//
ReportLog = stderr;
//
// TODO:Install hooks for catching allocations outside the scope of SAFECode.
//
/*if (ConfigData.TrackExternalMallocs) {
installAllocHooks();
} */
//
// Initialize the signal handlers for catching errors.
//
struct sigaction sa;
bzero (&sa, sizeof (struct sigaction));
sa.sa_sigaction = bus_error_handler;
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGBUS, &sa, NULL) == -1) {
fprintf (stderr, "sigaction installer failed!");
fflush (stderr);
}
if (sigaction(SIGSEGV, &sa, NULL) == -1) {
fprintf (stderr, "sigaction installer failed!");
fflush (stderr);
}
// Initialize the baggy bounds table
__baggybounds_size_table_begin = NULL;
__baggybounds_size_table_begin =
(unsigned char*) mmap(0, table_size,
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_NORESERVE,
-1, 0);
if (__baggybounds_size_table_begin == MAP_FAILED) {
fprintf (stderr, "Baggy Bounds Table initialization failed!");
fflush (stderr);
assert(0 && "Table Init Failed");
}
return;
}
void
__internal_register(void *allocaptr, unsigned NumBytes) {
uintptr_t Source = (uintptr_t)allocaptr;
unsigned char size= 0;
while((unsigned)(1<<size) < NumBytes) {
size++;
}
size = (size < SLOT_SIZE) ? SLOT_SIZE : size;
uintptr_t Source1 = Source & ~((1<<size)-1);
if(Source1 != Source) {
printf("%p, %p, %u Not aligned\n", (void*)Source, (void*)Source1, NumBytes);
assert(0 && "Memory objects not aligned");
}
Source = Source & ~((1<<size)-1);
unsigned long index = Source >> SLOT_SIZE;
unsigned range = 1 << (size - SLOT_SIZE);
memset(__baggybounds_size_table_begin + index, size, range);
return;
}
//
// Function: sc_bb_poolargvregister()
//
// Description:
// Register all of the argv strings in the external object pool.
//
void *
__sc_bb_poolargvregister(int argc, char **argv) {
char ** argv_temp =
(char **)__sc_bb_src_poolalloc(NULL,(sizeof(char*)*(argc+1)),0,"main\n", 0);
for (int index=0; index < argc; ++index) {
char *argv_index_temp =
(char *)__sc_bb_src_poolalloc(NULL,(strlen(argv[index])+ 1)*sizeof(char),0,"main\n", 0);
argv_index_temp = strcpy(argv_index_temp, argv[index]);
__internal_register(argv_index_temp,(strlen (argv[index]) + 1)*sizeof(char));
argv_temp[index] = argv_index_temp;
}
argv_temp[argc] = NULL;
//
// Register the actual argv array as well. Note that the transform can
// do this, but it's easier to implement it here, and I doubt accessing argv
// strings is performance critical.
//
// Note that the argv array is supposed to end with a NULL pointer element.
//
__internal_register(argv_temp, sizeof(char*)*(argc+1) );
return (void*)argv_temp;
}
//
// Function: __sc_bb_src_poolregister()
//
// Description:
// This function is externally visible and is called by code to register
// a heap allocation.
//
void
__sc_bb_src_poolregister (DebugPoolTy *Pool,
void * allocaptr,
unsigned NumBytes, TAG,
const char* SourceFilep,
unsigned lineno) {
__internal_register(allocaptr, NumBytes);
return;
}
//
// Function: __sc_bb_src_poolregister_stack()
//
// Description:
// This function is externally visible and is called by code to register
// a stack allocation.
//
void
__sc_bb_src_poolregister_stack (DebugPoolTy *Pool,
void * allocaptr,
unsigned NumBytes, TAG,
const char* SourceFilep,
unsigned lineno) {
__internal_register(allocaptr, NumBytes);
return;
}
//
// Function: __sc_bb_poolregister_stack()
//
// Description:
// This function is externally visible and is called by code to register
// a stack allocation without debug information.
//
void
__sc_bb_poolregister_stack (DebugPoolTy *Pool,
void * allocaptr,
unsigned NumBytes) {
__sc_bb_src_poolregister_stack(Pool, allocaptr, NumBytes, 0, "<unknown>", 0);
return;
}
//
// Function: __sc_bb_src_poolregister_global()
//
// Description:
// This function is externally visible and is called by code to register
// a global variable.
//
void
__sc_bb_poolregister_global (DebugPoolTy *Pool,
void *allocaptr,
unsigned NumBytes) {
__sc_bb_src_poolregister_global_debug(Pool,
allocaptr, NumBytes, 0 , "<unknown>", 0);
return;
}
//
// Function: __sc_bb_src_poolregister_global_debug()
//
// Description:
// This function is externally visible and is called by code to register
// a global variable with debugging information attached.
//
void
__sc_bb_src_poolregister_global_debug (DebugPoolTy *Pool,
void *allocaptr,
unsigned NumBytes,TAG,
const char *SourceFilep,
unsigned lineno) {
__internal_register(allocaptr, NumBytes);
}
//
// Function: __sc_bb_poolregister()
//
// Description:
// Register the memory starting at the specified pointer of the specified size
// with the given Pool. This version will also record debug information about
// the object being registered.
//
void
__sc_bb_poolregister(DebugPoolTy *Pool,
void *allocaptr,
unsigned NumBytes) {
__sc_bb_src_poolregister(Pool, allocaptr, NumBytes, 0, "<unknown>", 0);
}
void
__sc_bb_poolunregister(DebugPoolTy *Pool, void *allocaptr) {
__sc_bb_poolunregister_debug(Pool, allocaptr, 0, "<unknown>", 0);
}
void
__sc_bb_poolunregister_debug (DebugPoolTy *Pool,
void *allocaptr,
TAG,
const char* SourceFilep,
unsigned lineno) {
uintptr_t Source = (uintptr_t)allocaptr;
unsigned e;
e = __baggybounds_size_table_begin[Source >> SLOT_SIZE];
if(e == 0 ) {
return;
}
uintptr_t size = 1 << e;
uintptr_t base = Source & ~(size -1);
unsigned long index = base >> SLOT_SIZE;
unsigned int slots = 1<<(e - SLOT_SIZE);
memset(__baggybounds_size_table_begin + index, 0, slots);
}
void
__sc_bb_poolunregister_stack(DebugPoolTy *Pool,
void *allocaptr) {
__sc_bb_poolunregister_stack_debug(Pool, allocaptr, 0, "<unknown>", 0);
}
void
__sc_bb_poolunregister_stack_debug (DebugPoolTy *Pool,
void *allocaptr,
TAG,
const char* SourceFilep,
unsigned lineno) {
uintptr_t Source = (uintptr_t)allocaptr;
unsigned e;
e = __baggybounds_size_table_begin[Source >> SLOT_SIZE];
if(e == 0 ) {
return;
}
uintptr_t size = 1 << e;
uintptr_t base = Source & ~(size -1);
unsigned long index = base >> SLOT_SIZE;
unsigned int slots = 1<<(e - SLOT_SIZE);
memset(__baggybounds_size_table_begin + index, 0, slots);
}
void *
__sc_bb_src_poolalloc(DebugPoolTy *Pool,
unsigned NumBytes, TAG,
const char * SourceFilep,
unsigned lineno) {
unsigned char size= 0;
while((unsigned)(1<<size) < NumBytes) {
size++;
}
if (size < SLOT_SIZE)
size = SLOT_SIZE;
unsigned int alloc = 1 << size;
void *p;
assert(!posix_memalign(&p, alloc, alloc) && "Memory allocation failed");
return p;
}
void*
__sc_bb_poolmemalign(DebugPoolTy *Pool,
unsigned Alignment,
unsigned NumBytes) {
unsigned char size= 0;
while((unsigned)(1<<size) < NumBytes) {
size++;
}
if (size < SLOT_SIZE)
size = SLOT_SIZE;
if (size < Alignment)
size = Alignment;
unsigned int alloc = 1 << size;
void *p;
assert(!posix_memalign(&p, alloc, alloc) && "Memory allocation failed");
__sc_bb_poolregister(Pool, p, NumBytes);
return p;
}
void *
__sc_bb_src_poolcalloc(DebugPoolTy *Pool,
unsigned Number,
unsigned NumBytes, TAG,
const char* SourceFilep,
unsigned lineno) {
unsigned char size= 0;
while((unsigned)(1<<size) < (NumBytes*Number)) {
size++;
}
if (size < SLOT_SIZE) size = SLOT_SIZE;
unsigned int alloc = 1<< size;
void *p;
assert(!posix_memalign(&p, alloc, alloc) && "Memory allocation failed");
__sc_bb_src_poolregister(Pool, p, (Number*NumBytes), tag, SourceFilep, lineno);
if (p) {
bzero(p, Number*NumBytes);
}
return p;
}
void *
__sc_bb_poolcalloc(DebugPoolTy *Pool,
unsigned Number,
unsigned NumBytes, TAG) {
return __sc_bb_src_poolcalloc(Pool,Number, NumBytes, 0, "<unknown>",0);
}
void *
__sc_bb_poolrealloc_debug (DebugPoolTy *Pool,
void *Node,
unsigned NumBytes, TAG,
const char * SourceFilep,
unsigned lineno) {
return __sc_bb_poolrealloc(Pool, Node, NumBytes);
}
void *
__sc_bb_poolrealloc(DebugPoolTy *Pool,
void *Node,
unsigned NumBytes) {
if (Node == 0) {
void *New = __sc_bb_poolalloc(Pool, NumBytes);
__sc_bb_poolregister(Pool, New, NumBytes);
return New;
}
if (NumBytes == 0) {
__sc_bb_poolunregister(Pool, Node);
__sc_bb_poolfree(Pool, Node);
return 0;
}
uintptr_t Source = (uintptr_t)Node;
if (Source & SET_MASK) {
return 0;
}
void *New = __sc_bb_poolalloc(Pool, NumBytes);
if(New == 0)
return 0;
__sc_bb_poolregister(Pool, New, NumBytes);
unsigned char e = __baggybounds_size_table_begin[Source >> SLOT_SIZE];
unsigned int size_old = 1 << e;
uintptr_t Source_new = (uintptr_t)New;
unsigned char e_new = __baggybounds_size_table_begin[Source_new >> SLOT_SIZE];
unsigned int size_new = 1 << e_new;
if(size_new > size_old)
memcpy(New, Node, size_old);
else
memcpy(New, Node, size_new);
__sc_bb_poolunregister(Pool, Node);
__sc_bb_poolfree(Pool, Node);
return New;
}
void *
__sc_bb_poolalloc(DebugPoolTy *Pool,
unsigned NumBytes) {
return __sc_bb_src_poolalloc(Pool, NumBytes, 0, "<unknown>",0);
}
void
__sc_bb_src_poolfree (DebugPoolTy *Pool,
void *Node,TAG,
const char* SourceFile,
unsigned lineno) {
free(Node);
}
void
__sc_bb_poolfree (DebugPoolTy *Pool,
void *Node) {
__sc_bb_src_poolfree(Pool, Node, 0, "<unknown>", 0);
}
//
// Function: getProgramCounter()
//
// Description:
// This function determines the program counter at which a fault was taken.
//
// Inputs:
// context - A pointer to the context in which the fault occurred. This is
// a paramter that is passed into signal handlers.
//
// Return value:
// 0 - The program counter could not be determined on this platform.
// ~0 - Otherwise, the program counter at which the fault occurred is
// returned.
//
static unsigned
getProgramCounter (void * context) {
#if defined(__APPLE__)
#if defined(i386) || defined(__i386__) || defined(__x86__)
// Cast parameters to the desired type
ucontext_t * mycontext = (ucontext_t *) context;
return (mycontext->uc_mcontext->__ss.__eip);
#endif
#endif
#if defined(__linux__)
// Cast parameters to the desired type
ucontext_t * mycontext = (ucontext_t *) context;
return (mycontext->uc_mcontext.gregs[14]);
#endif
return 0;
}
//
//
// Function: bus_error_handler()
//
// Description:
// This is the signal handler that catches bad memory references.
//
static void
bus_error_handler (int sig, siginfo_t * info, void * context) {
//
// Disable the signal handler for now. If this function does something
// wrong, we want the bus error to terminate the program.
//
signal(SIGBUS, NULL);
//
// Get the program counter for where the fault occurred.
//
unsigned program_counter = getProgramCounter (context);
//
// Get the address causing the fault.
//
void * faultAddr = info->si_addr;
//
// This is not a dangling pointer, uninitialized pointer, or a rewrite
// pointer. This is some load/store that has obviously gone wrong (even
// if we consider the possibility of incompletenes). Report it as a
// load/store error.
//
DebugViolationInfo v;
v.type = ViolationInfo::FAULT_LOAD_STORE,
v.faultPC = (const void*)program_counter,
v.faultPtr = faultAddr,
v.SourceFile = 0,
v.lineNo = 0;
ReportMemoryViolation(&v);
//
// Reinstall the signal handler for subsequent faults
//
struct sigaction sa;
sa.sa_sigaction = bus_error_handler;
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGBUS, &sa, NULL) == -1)
printf("sigaction installer failed!");
if (sigaction(SIGSEGV, &sa, NULL) == -1)
printf("sigaction installer failed!");
return;
}