blob: 568a631ca1fac1bcce13fc888a5552ef11d05f74 [file] [log] [blame]
/*===- ExactCheck.cpp - Implementation of exactcheck functions ------------===*/
/* */
/* 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 implements the exactcheck family of functions. */
/* */
/*===----------------------------------------------------------------------===*/
#include "DebugReport.h"
#include "ConfigData.h"
#include "../include/BitmapAllocator.h"
#include "../include/CWE.h"
#include "PoolAllocator.h"
#include "RewritePtr.h"
#include <stdint.h>
#include <cstdio>
extern FILE * ReportLog;
using namespace llvm;
static void *
exactcheck_check (void * Source, void * ObjStart, void * ObjEnd,
const void * Dest, const char * SourceFile,
unsigned int lineno) __attribute__((noinline));
static void
failLSCheck (const char *base,
const char *result,
unsigned size,
const char * SourceFile,
unsigned int lineno) __attribute__((noinline));
void
failLSCheck (const char *base,
const char *result,
unsigned size,
const char * SourceFile,
unsigned int lineno) {
DebugViolationInfo v;
v.type = ViolationInfo::FAULT_LOAD_STORE,
v.faultPC = __builtin_return_address(0),
v.faultPtr = result,
v.CWE = CWEBufferOverflow,
v.PoolHandle = 0,
v.dbgMetaData = NULL,
v.SourceFile = SourceFile,
v.lineNo = lineno;
ReportMemoryViolation(&v);
}
/*
* Function: fastlscheck()
*
* Description:
* This function performs a fast load/store check. If the check fails, it
* will *not* attempt to do pointer rewriting.
*
* Inputs:
* base - The address of the first byte of a memory object.
* result - The pointer that is being checked.
* size - The size of the object in bytes.
* lslen - The length of the data accessed in memory.
*/
void
fastlscheck (const char *base, const char *result, unsigned size,
unsigned lslen) {
/*
* If the pointer is within the object, the check passes. Return the checked
* pointer.
*/
const char * end = result + lslen - 1;
if ((result >= base) && (result < (base + size))) {
if ((end >= base) && (end < (base + size))) {
return;
}
}
/*
* If the memory access accesses zero bytes, don't report an error. This can
* happen with load/store checks on memcpy()/memset() calls.
*/
if (!lslen)
return;
failLSCheck (base, result, size, "unknown", 0);
return;
}
/*
* Function: fastlscheck_debug()
*
* Description:
* This function performs a fast load/store check. If the check fails, it
* will *not* attempt to do pointer rewriting.
*
* Inputs:
* base - The address of the first byte of a memory object.
* result - The pointer that is being checked.
* size - The size of the object in bytes.
*/
void
fastlscheck_debug (const char *base, const char *result, unsigned size,
unsigned lslen,
unsigned tag,
const char * SourceFile,
unsigned lineno) {
/*
* If the pointer is within the object, the check passes. Return the checked
* pointer.
*/
const char * end = result + lslen - 1;
if ((result >= base) && (result < (base + size))) {
if ((end >= base) && (end < (base + size))) {
return;
}
}
/*
* If the memory access accesses zero bytes, don't report an error. This can
* happen with load/store checks on memcpy()/memset() calls.
*/
if (!lslen)
return;
failLSCheck (base, result, size, SourceFile, lineno);
return;
}
/*
* Function: exactcheck2()
*
* Description:
* Determine whether a pointer is within the specified bounds of an object.
*
* Inputs:
* source - The source pointer of the indexing operation (the GEP).
* base - The address of the first byte of a memory object.
* result - The pointer that is being checked.
* size - The size of the object in bytes.
*
* Return value:
* If there is no bounds check violation, the result pointer is returned.
* Otherwise, depending upon the configuration of the run-time, either an
* error is returned or a rewritten Out-of-Bounds (OOB) pointer is returned.
*/
void *
exactcheck2 (char * source, char *base, char *result, unsigned size) {
/*
* If the pointer is within the object, the check passes. Return the checked
* pointer.
*/
if ((result >= base) && (result < (base + size))) {
return (void*)result;
}
return exactcheck_check (source, base, base + size-1, result, NULL, 0);
}
/*
* Function: exactcheck2_debug()
*
* Description:
* This function is identical to exactcheck2(), but the caller provides more
* source level information about the run-time check for error reporting if
* the check fails.
*
* Inputs:
* source - The source pointer of the indexing operation (the GEP).
* base - The address of the first byte of a memory object.
* result - The pointer that is being checked.
* size - The size of the object in bytes.
*
* Return value:
* If there is no bounds check violation, the result pointer is returned.
* This forces the call to exactcheck() to be considered live (previous
* optimizations dead-code eliminated it).
*/
void *
exactcheck2_debug (char *source,
char *base,
char *result,
unsigned size,
unsigned tag,
const char * SourceFile,
unsigned lineno) {
/*
* If the pointer is within the object, the check passes. Return the checked
* pointer.
*/
if ((result >= base) && (result < (base + size))) {
return (void*) result;
}
return exactcheck_check (source, base, base + size - 1, result,
SourceFile, lineno);
}
/*
* Function: exactcheck_check()
*
* Description:
* This is the slow path for an exactcheck. It handles pointer rewriting
* and error reporting when an exactcheck fails.
*
* Inputs:
* source - The source pointer of the indexing operation (the GEP).
* ObjStart - The address of the first valid byte of the object.
* ObjEnd - The address of the last valid byte of the object.
* Dest - The result pointer of the indexing operation (the GEP).
* SourceFile - The name of the file in which the check occurs.
* lineno - The line number within the file in which the check occurs.
*/
void *
exactcheck_check (void * Source,
void * ObjStart,
void * ObjEnd,
const void * Dest,
const char * SourceFile,
unsigned int lineno) {
void * RealDest = const_cast<void*>(Dest);
void * RealObjStart = ObjStart;
void * RealObjEnd = ObjEnd;
/*
* On entry, we know that the supplied Dest pointer lies outside the
* bounds indicated by ObjStart and ObjEnd. However, it is possible
* that Dest, ObjStart, and ObjEnd were all computed from a rewrite
* pointer. Test to see if this is the case, and re-run the check
* if it is.
*
* Note that we define pool = NULL. This forces use of the global
* pool, which is the only pool that can be used at present. Change
* the function to pass in a pool pointer later if need be.
*/
if (isRewritePtr (Source)) {
/*
* Get the real pointer value (which must be outside the bounds
* of a valid object, as the pointer was re-written).
*/
DebugPoolTy * Pool = NULL;
void * RealSrc = pchk_getActualValue (Pool, Source);
/*
* Compute the real result pointer (the value the GEP would really have
* on the original pointer value).
*/
RealDest = (void *)((intptr_t) RealSrc +
((intptr_t) Dest - (intptr_t) Source));
/*
* Retrieve the original bounds of the object.
*/
getOOBObject(Source, RealObjStart, RealObjEnd);
/*
* Re-run the bounds check
*/
if (__builtin_expect (((RealObjStart <= RealDest) &&
((RealDest <= RealObjEnd))), 1)) {
if (logregs) {
fprintf (stderr, "exactcheck:unrewrite(1): %p -> %p, Dest: %p, Obj: %p - %p\n", Source, RealSrc, RealDest, RealObjStart, RealObjEnd);
fflush (stderr);
}
return(RealDest);
}
}
/*
* At this point, we have that the RealDest pointer is out of range,
* and that it was not computed from a re-written OOB source pointer.
*
* If we indexed off the beginning or end of a valid object,
* determine if we can rewrite the pointer into an OOB pointer.
* Whether we can or not depends upon the SAFECode configuration.
*/
if ((!(ConfigData.StrictIndexing)) ||
(((char *) RealDest) == (((char *)RealObjEnd)+1))) {
void * ptr = rewrite_ptr (0, RealDest, RealObjStart, RealObjEnd, SourceFile, lineno);
if (logregs) {
fprintf (ReportLog,
"exactcheck: rewrite(1): %p %p %p at pc=%p to %p: %s %d\n",
RealObjStart, RealObjEnd, RealDest, (void*)__builtin_return_address(0), ptr,
SourceFile, lineno);
fflush (ReportLog);
}
return ptr;
} else {
//
// Determine if this is a rewrite pointer that is being indexed.
//
if ((logregs) && (isRewritePtr ((void *)Dest))) {
fprintf (stderr, "Was a rewrite: %p\n", Dest);
fflush (stderr);
}
OutOfBoundsViolation v;
v.type = ViolationInfo::FAULT_OUT_OF_BOUNDS,
v.faultPC = __builtin_return_address(0),
v.faultPtr = RealDest,
v.CWE = CWEBufferOverflow,
v.PoolHandle = 0,
v.dbgMetaData = NULL,
v.SourceFile = SourceFile,
v.objStart = RealObjStart,
v.objLen = (unsigned)((const char*)ObjEnd - (const char*)ObjStart + 1),
v.lineNo = lineno;
ReportMemoryViolation(&v);
}
return const_cast<void*>(Dest);
}
#if 0
/// UNUSED CODE
/*
* Function: exactcheck()
*
* Description:
* Determine whether the index is within the specified bounds.
*
* Inputs:
* a - The index given as an integer.
* b - The index of one past the end of the array.
* result - The pointer that is being checked.
*
* Return value:
* If there is no bounds check violation, the result pointer is returned.
* This forces the call to exactcheck() to be considered live (previous
* optimizations dead-code eliminated it).
*/
void *
exactcheck (int a, int b, void * result) {
if ((0 > a) || (a >= b)) {
ReportExactCheck ((unsigned)0xbeefdeed,
(uintptr_t)result,
(uintptr_t)__builtin_return_address(0),
(unsigned)a,
(unsigned)0,
"<Unknown>",
0);
#if 0
poolcheckfail ("exact check failed", (a), (void*)__builtin_return_address(0));
poolcheckfail ("exact check failed", (b), (void*)__builtin_return_address(0));
#endif
}
return result;
}
void *
exactcheck3(signed char *base, signed char *result, signed char * end) {
if ((result < base) || (result > end )) {
ReportExactCheck ((unsigned)0xbeefbeef,
(uintptr_t)result,
(uintptr_t)__builtin_return_address(0),
(uintptr_t)base,
(unsigned)(end-base),
"<Unknown>",
0);
}
return result;
}
void *
exactcheck2a (signed char *base, signed char *result, unsigned size) {
if (result >= base + size ) {
ReportExactCheck ((unsigned)0xbeefdeed,
(uintptr_t)result,
(uintptr_t)__builtin_return_address(0),
(uintptr_t)base,
(unsigned)size,
"<Unknown>",
0);
}
return result;
}
#endif