Add some basic heap memory bounds protection.

This commit is contained in:
Jordan Bancino 2023-05-21 13:24:00 +00:00
parent d38ec7cb38
commit e71ffec164
6 changed files with 80 additions and 21 deletions

View file

@ -35,6 +35,11 @@ addprefix() {
: "${LD_EXTRA:=-flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections}" : "${LD_EXTRA:=-flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections}"
: "${LDFLAGS:=-lm -pthread}" : "${LDFLAGS:=-lm -pthread}"
if [ "${DEBUG}" = "1" ]; then
CFLAGS="${CFLAGS} -o0 -g"
LD_EXTRA=""
fi
if [ -n "${TLS_IMPL}" ]; then if [ -n "${TLS_IMPL}" ]; then
case "${TLS_IMPL}" in case "${TLS_IMPL}" in
"LIBRESSL") "LIBRESSL")
@ -130,6 +135,7 @@ recipe_build() {
mkdir -p "${BUILD}" "${OUT}/bin" "${OUT}/lib" mkdir -p "${BUILD}" "${OUT}/bin" "${OUT}/lib"
cd "${SRC}" cd "${SRC}"
echo "CC = ${CC}" echo "CC = ${CC}"
echo "CFLAGS = ${CFLAGS}" echo "CFLAGS = ${CFLAGS}"
echo "LDFLAGS = ${LDFLAGS}" echo "LDFLAGS = ${LDFLAGS}"
@ -171,7 +177,7 @@ recipe_build() {
fi fi
fi fi
cp "${BUILD}/${STUB}.o" "${OUT}/lib/${LIB_NAME}.o" cp "${BUILD}/${STUB}.o" "${OUT}/lib/${LIB_NAME}.o"
for src in $(find "${TOOLS}" -name '*.c'); do for src in $(find "${TOOLS}" -name '*.c'); do
out=$(basename "$src" .c) out=$(basename "$src" .c)

View file

@ -24,10 +24,13 @@
#include <Memory.h> #include <Memory.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <pthread.h> #include <pthread.h>
#include <Int.h>
#ifndef MEMORY_TABLE_CHUNK #ifndef MEMORY_TABLE_CHUNK
#define MEMORY_TABLE_CHUNK 256 #define MEMORY_TABLE_CHUNK 256
#endif #endif
@ -44,6 +47,13 @@ struct MemoryInfo
void *pointer; void *pointer;
}; };
#define MEM_BOUND_TYPE UInt16
#define MEM_BOUND 0xADDE
#define MEM_BOUND_LOWER(p) *((MEM_BOUND_TYPE *) p)
#define MEM_BOUND_UPPER(p, x) *(((MEM_BOUND_TYPE *) (((UInt8 *) p) + x)) + 1)
#define MEM_SIZE_ACTUAL(x) (((x) * sizeof(UInt8)) + (2 * sizeof(MEM_BOUND_TYPE)))
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static void (*hook) (MemoryAction, MemoryInfo *, void *) = NULL; static void (*hook) (MemoryAction, MemoryInfo *, void *) = NULL;
static void *hookArgs = NULL; static void *hookArgs = NULL;
@ -143,6 +153,21 @@ MemoryDelete(MemoryInfo * a)
} }
} }
static int
MemoryCheck(MemoryInfo *a)
{
if (MEM_BOUND_LOWER(a->pointer) != MEM_BOUND ||
MEM_BOUND_UPPER(a->pointer, a->size - (2 * sizeof(MEM_BOUND_TYPE))) != MEM_BOUND)
{
if (hook)
{
hook(MEMORY_CORRUPTED, a, hookArgs);
}
return 0;
}
return 1;
}
void * void *
MemoryAllocate(size_t size, const char *file, int line) MemoryAllocate(size_t size, const char *file, int line)
{ {
@ -151,22 +176,26 @@ MemoryAllocate(size_t size, const char *file, int line)
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
p = malloc(size);
if (!p)
{
pthread_mutex_unlock(&lock);
return NULL;
}
a = malloc(sizeof(MemoryInfo)); a = malloc(sizeof(MemoryInfo));
if (!a) if (!a)
{ {
free(p);
pthread_mutex_unlock(&lock); pthread_mutex_unlock(&lock);
return NULL; return NULL;
} }
a->size = size; p = malloc(MEM_SIZE_ACTUAL(size));
if (!p)
{
free(a);
pthread_mutex_unlock(&lock);
return NULL;
}
memset(p, 0, MEM_SIZE_ACTUAL(size));
MEM_BOUND_LOWER(p) = MEM_BOUND;
MEM_BOUND_UPPER(p, size) = MEM_BOUND;
a->size = MEM_SIZE_ACTUAL(size);
a->file = file; a->file = file;
a->line = line; a->line = line;
a->pointer = p; a->pointer = p;
@ -185,7 +214,7 @@ MemoryAllocate(size_t size, const char *file, int line)
} }
pthread_mutex_unlock(&lock); pthread_mutex_unlock(&lock);
return p; return ((MEM_BOUND_TYPE *) p) + 1;
} }
void * void *
@ -203,17 +232,20 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
if (a) if (a)
{ {
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
new = realloc(a->pointer, size); new = realloc(a->pointer, MEM_SIZE_ACTUAL(size));
if (new) if (new)
{ {
MemoryDelete(a); MemoryDelete(a);
a->size = size; a->size = MEM_SIZE_ACTUAL(size);
a->file = file; a->file = file;
a->line = line; a->line = line;
a->pointer = new; a->pointer = new;
MemoryInsert(a); MemoryInsert(a);
MEM_BOUND_LOWER(a->pointer) = MEM_BOUND;
MEM_BOUND_UPPER(a->pointer, size) = MEM_BOUND;
if (hook) if (hook)
{ {
hook(MEMORY_REALLOCATE, a, hookArgs); hook(MEMORY_REALLOCATE, a, hookArgs);
@ -236,8 +268,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
} }
} }
return ((MEM_BOUND_TYPE *) new) + 1;
return new;
} }
void void
@ -333,6 +364,7 @@ MemoryInfoGet(void *p)
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
p = ((MEM_BOUND_TYPE *) p) - 1;
hash = MemoryHash(p); hash = MemoryHash(p);
count = 0; count = 0;
@ -345,6 +377,7 @@ MemoryInfoGet(void *p)
} }
else else
{ {
MemoryCheck(allocations[hash]);
pthread_mutex_unlock(&lock); pthread_mutex_unlock(&lock);
return allocations[hash]; return allocations[hash];
} }
@ -362,7 +395,7 @@ MemoryInfoGetSize(MemoryInfo * a)
return 0; return 0;
} }
return a->size; return MEM_SIZE_ACTUAL(a->size);
} }
const char * const char *
@ -395,7 +428,7 @@ MemoryInfoGetPointer(MemoryInfo * a)
return NULL; return NULL;
} }
return a->pointer; return ((MEM_BOUND_TYPE *) a->pointer) + 1;
} }
void void
@ -441,7 +474,6 @@ void
} }
pc = MemoryInfoGetPointer(info); pc = MemoryInfoGetPointer(info);
for (pI = 0; pI < MemoryInfoGetSize(info); pI++) for (pI = 0; pI < MemoryInfoGetSize(info); pI++)
{ {
if (pI > 0 && pI % MEMORY_HEXDUMP_WIDTH == 0) if (pI > 0 && pI % MEMORY_HEXDUMP_WIDTH == 0)

View file

@ -86,7 +86,8 @@ typedef enum MemoryAction
MEMORY_ALLOCATE, MEMORY_ALLOCATE,
MEMORY_REALLOCATE, MEMORY_REALLOCATE,
MEMORY_FREE, MEMORY_FREE,
MEMORY_BAD_POINTER MEMORY_BAD_POINTER,
MEMORY_CORRUPTED
} MemoryAction; } MemoryAction;
#define Malloc(x) MemoryAllocate(x, __FILE__, __LINE__) #define Malloc(x) MemoryAllocate(x, __FILE__, __LINE__)

View file

@ -24,6 +24,7 @@ Milestone: v0.3.0
[x] Documentation [x] Documentation
[x] hdoc(1) and hdoc(5) [x] hdoc(1) and hdoc(5)
[x] Fix memory leaks in hdoc [x] Fix memory leaks in hdoc
[x] Detect memory write out of bounds
Milestone: v0.4.0 Milestone: v0.4.0
----------------- -----------------

View file

@ -175,6 +175,10 @@ start:
if (flags & ARG_VERBOSE) if (flags & ARG_VERBOSE)
{ {
LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG); LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG);
MemoryHook(TelodendriaMemoryHook, (void *) ARG_VERBOSE);
}
else
{
MemoryHook(TelodendriaMemoryHook, NULL); MemoryHook(TelodendriaMemoryHook, NULL);
} }

View file

@ -30,6 +30,7 @@
#include <Routes.h> #include <Routes.h>
#include <time.h> #include <time.h>
#include <signal.h>
const char const char
TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH] = { TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH] = {
@ -71,8 +72,12 @@ void
TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args) TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
{ {
char *action; char *action;
int warn = 0;
(void) args; if (!args && ((a == MEMORY_ALLOCATE) || (a == MEMORY_REALLOCATE) || (a == MEMORY_FREE)))
{
return;
}
switch (a) switch (a)
{ {
@ -86,18 +91,28 @@ TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
action = "Freed"; action = "Freed";
break; break;
case MEMORY_BAD_POINTER: case MEMORY_BAD_POINTER:
warn = 1;
action = "Bad pointer to"; action = "Bad pointer to";
break; break;
case MEMORY_CORRUPTED:
warn = 1;
action = "Corrupted block of";
break;
default: default:
action = "Unknown operation on"; action = "Unknown operation on";
break; break;
} }
Log(a == MEMORY_BAD_POINTER ? LOG_WARNING : LOG_DEBUG, Log(warn ? LOG_WARNING : LOG_DEBUG,
"%s:%d: %s %lu bytes of memory at %p.", "%s:%d: %s %lu bytes of memory at %p.",
MemoryInfoGetFile(i), MemoryInfoGetLine(i), MemoryInfoGetFile(i), MemoryInfoGetLine(i),
action, MemoryInfoGetSize(i), action, MemoryInfoGetSize(i),
MemoryInfoGetPointer(i)); MemoryInfoGetPointer(i));
if (a == MEMORY_CORRUPTED)
{
raise(SIGSEGV);
}
} }
static void static void