/*
* FreeGuard: A Faster Secure Heap Allocator
* Copyright (C) 2017 Sam Silvestro, Hongyu Liu, Corey Crosser,
* Zhiqiang Lin, and Tongping Liu
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @file liblight.cpp: main file, includes memory interception functions.
* @author Tongping Liu
* @author Sam Silvestro
*/
#include
#include
#include
#include
#include "real.hh"
#include "xthread.hh"
#include "numaheap.hh"
#include "mm.hh"
#include "xdefines.hh"
#include "perthread.hh"
#define GET_TIME 0
#if GET_TIME
volatile unsigned long long allocs = 0;
volatile unsigned long long allocsfor48 = 0;
volatile unsigned long long origTime = 0;
volatile unsigned long long totalAllocCycles = 0;
volatile unsigned long long totalFreeCycles = 0;
#endif
inline unsigned long long rdtscp() {
unsigned int lo, hi;
asm volatile (
"rdtscp"
: "=a"(lo), "=d"(hi) /* outputs */
: "a"(0) /* inputs */
: "%ebx", "%ecx"); /* clobbers*/
unsigned long long retval = ((unsigned long long)lo) | (((unsigned long long)hi) startFrame = (char *)__builtin_frame_address(0);
return real_libc_start_main(main_fn, argc, argv, init, fini, rtld_fini, stack_end);
}
extern "C" {
void xxfree(void *);
void * xxmalloc(size_t);
///Jin
void * xxcalloc(size_t, size_t);
void * xxrealloc(void *, size_t);
void * xxvalloc(size_t);
void * xxaligned_alloc(size_t, size_t);
void * xxmemalign(size_t, size_t);
void * xxpvalloc(size_t);
void * xxalloca(size_t);
int xxposix_memalign(void **, size_t, size_t);
// Function aliases
void free(void *) __attribute__ ((weak, alias("xxfree")));
void * malloc(size_t) __attribute__ ((weak, alias("xxmalloc")));
///Jin
void * calloc(size_t, size_t) __attribute__ ((weak, alias("xxcalloc")));
void * realloc(void *, size_t) __attribute__ ((weak, alias("xxrealloc")));
void * valloc(size_t) __attribute__ ((weak, alias("xxvalloc")));
void * aligned_alloc(size_t, size_t) __attribute__ ((weak,
alias("xxaligned_alloc")));
void * memalign(size_t, size_t) __attribute__ ((weak, alias("xxmemalign")));
void * pvalloc(size_t) __attribute__ ((weak, alias("xxpvalloc")));
void * alloca(size_t) __attribute__ ((weak, alias("xxalloca")));
int posix_memalign(void **, size_t, size_t) __attribute__ ((weak,
alias("xxposix_memalign")));
}
void heapinitialize();
__attribute__((constructor)) void initializer() {
#if GET_TIME
origTime = rdtscp();
#endif
///Jin
if(localPtr == NULL) {
localPtr = localBuffer;
localPtrEnd = &localBuffer[4096];
Real::initializer();
heapInitStatus = E_HEAP_INIT_WORKING;
}
heapinitialize();
}
__attribute__((destructor)) void finalizer() {
#if GET_TIME
fprintf(stderr, "total alloc cycles %lld, free cycles %lld\n", totalAllocCycles, totalFreeCycles);
fprintf(stderr, "total allocs for 48 is %llx\n", allocsfor48);
unsigned long long lastTime = rdtscp() - origTime;
fprintf(stderr, "total runtime is %lld, percentage %lf", lastTime, ((double)lastTime)/((double)(allocsfor48)));
#endif
}
void * operator new (size_t sz) {
return xxmalloc(sz);
}
void * operator new (size_t sz, const std::nothrow_t&) throw() {
return xxmalloc(sz);
}
void operator delete (void * ptr) __THROW {
xxfree (ptr);
}
void * operator new[] (size_t sz) {
return xxmalloc(sz);
}
// Heap initialization function
void heapinitialize() {
if(heapInitStatus == E_HEAP_INIT_WORKING) {
// Including the number of nodes, and the size of freed memory if possible
NumaHeap::getInstance().initialize();
// Thread initialization, which can be occurred before or after numa heap initialization
xthread::getInstance().initialize();
// fprintf(stderr, "heap is initialized now\n");
heapInitStatus = E_HEAP_INIT_DONE;
}
// ///Jin
// else {
// while(heapInitStatus != E_HEAP_INIT_DONE);
// }
}
void * xxmalloc(size_t size) {
// If this is the first allocation before the initialization,
// then we will try to use a local buffer, since Real::initializer()
// will invoke malloc as well.
if(localPtr == NULL) {
localPtr = localBuffer;
localPtrEnd = &localBuffer[4096];
Real::initializer();
///Jin
heapInitStatus = E_HEAP_INIT_WORKING;
}
void * ptr = NULL;
switch(heapInitStatus) {
case E_HEAP_INIT_NOT:
ptr = localPtr;
localPtr += size;
if(localPtr > localPtrEnd) {
// fprintf(stderr, "Making the temporary buffer larger\n");
abort();
}
break;
case E_HEAP_INIT_WORKING:
ptr = Real::malloc(size);
break;
case E_HEAP_INIT_DONE:
#if GET_TIME
unsigned long long start = rdtscp();
#endif
// fprintf(stderr, "allocate right now with size %lx\n", size);
ptr = NumaHeap::getInstance().allocate(size);
#if GET_TIME
//if(size != 57) {
// fprintf(stderr, "malloc size %ld ret %p\n", size, ptr);
// }
unsigned long long length = rdtscp() - start;
totalAllocCycles += length;
if((allocs % 1000000) == 0) {
fprintf(stderr, "Runtime is %lld totalAllocCycles %lld, allocs %lld. current length %lld\n", rdtscp()-origTime, totalAllocCycles, allocs, length);
}
//fprintf(stderr, "totalAllocCycles %lld length %lld\n", totalAllocCycles, length);
allocs++;
if(allocs == 15000000) { exit(0); }
//fprintf(stderr, "malloc size %ld ptr %p\n", size, ptr);
#endif
break;
}
return ptr;
}
void xxfree(void * ptr) {
if(ptr == NULL) {
return;
}
if(heapInitStatus == E_HEAP_INIT_WORKING) {
Real::free(ptr);
}
else {
#if GET_TIME
unsigned long long start = rdtscp();
#endif
// Perform the free operation
NumaHeap::getInstance().deallocate(ptr);
#if GET_TIME
totalFreeCycles += rdtscp() - start;
#endif
}
}
///Jin
void * xxcalloc(size_t nelem, size_t elsize) {
void * ptr = NULL;
ptr = xxmalloc(nelem * elsize);
if(ptr != NULL) {
memset(ptr, 0, nelem * elsize);
}
return ptr;
}
void * xxrealloc(void * ptr, size_t sz) {
if(heapInitStatus == E_HEAP_INIT_WORKING) {
return Real::realloc(ptr, sz);
}
// If the pointer is null, call is equivalent to malloc(sz).
if(ptr == NULL) {
return xxmalloc(sz);
}
// If the pointer is non-null and size is zero, call is equivalent
// to free(ptr).
if(sz == 0) {
xxfree(ptr);
return NULL;
}
// If the object is unknown to us, return NULL to indicate error.
size_t oldSize = NumaHeap::getInstance().getSize(ptr);
///Jin
void * newObject;
if(oldSize == (size_t)-1) {
void * tmp = Real::realloc(ptr, sz);
newObject = xxmalloc(sz);
memcpy(newObject, tmp, sz);
Real::free(tmp);
} else {
newObject = xxmalloc(sz);
memcpy(newObject, ptr, oldSize < sz ? oldSize : sz);
xxfree(ptr);
}
return newObject;
}
void * xxalloca(size_t size) {
PRERR("%s CALLED", __FUNCTION__);
return NULL;
}
void * xxvalloc(size_t size) {
PRERR("%s CALLED", __FUNCTION__);
return NULL;
}
int xxposix_memalign(void **memptr, size_t alignment, size_t size) {
void * alignedObject = xxmemalign(alignment, size);
*memptr = alignedObject;
return 0;
}
void * xxaligned_alloc(size_t alignment, size_t size) {
PRERR("%s CALLED", __FUNCTION__);
return NULL;
}
void * xxmemalign(size_t alignment, size_t size) {
if(size == 0) {
return NULL;
}
size_t allocObjectSize = alignment + size;
void * object = xxmalloc(allocObjectSize);
unsigned long residualBytes = (unsigned long)object % alignment;
void * alignedObject = (void *)((char *)object + residualBytes);
return alignedObject;
}
void * xxpvalloc(size_t size) {
PRERR("%s CALLED", __FUNCTION__);
return NULL;
}
// Intercept thread creation
int pthread_create(pthread_t * tid, const pthread_attr_t * attr,
void *(*start_routine)(void *), void * arg) {
if(heapInitStatus != E_HEAP_INIT_DONE) {
heapinitialize();
}
return xthread::getInstance().thread_create(tid, attr, start_routine, arg);
}
int pthread_join(pthread_t tid, void** retval) {
return xthread::getInstance().thread_join(tid, retval);
}