我正试图在每个分配的块的末尾添加一个保护字符,以便free()
在找不到它的情况下可以abort()
。为什么这些函数预加载不起作用?我意识到这不是一个可移植的方法,但我很好奇为什么它不起作用。
gcc -shared -fPIC -std=gnu99 -O2 -o wrapper.so wrapper.c
LD_PRELOAD=/path/to/wrapper.so programname
我每个都有一个函数:valloc
、realloc
、pvalloc
、posix_memalign
、aligned_alloc
、memalign
、malloc
和calloc
。
#define PREV_INUSE 0x1
#define IS_MMAPPED 0x2
#define NON_MAIN_ARENA 0x4
#define SIZE_BITS (PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA)
extern void *__libc_malloc(size_t size);
void *malloc(size_t size){
size = size + 1; // add char for guard byte '@'
void *p = __libc_malloc(size);
if(p != NULL){
size_t *q = p;
q--;
size_t s = *q & ~(SIZE_BITS); // get allocated bytes subtracting info bits
char *z = p;
memset(z, 0, s); // zero memory
z[s - 1] = '@'; // place guard char
}
return p;
}
extern void *__libc_free(void *ptr);
void free(void *ptr){
if(ptr != NULL){
size_t *p = ptr;
p--;
size_t s = *p & ~(SIZE_BITS);
char *z = ptr;
if(z[s - 1] != '@') // if guard char not found, abort()
{
abort();
}
memset(z, 0, s); // zero memory
}
__libc_free(ptr);
}
您正在使用位于分配区域之前的size_t
作为可用长度。然而,它包括size_t
本身。因此,这里:
if (p != NULL) {
size_t *q = p;
q--;
size_t s = *q & ~(SIZE_BITS); // get allocated bytes subtracting info bits
char *z = p;
memset(z, 0, s); // zero memory
z[s - 1] = '@'; // place guard char
}
您最终会覆盖下一个区域的长度,部分由您的guard-char覆盖。解决方案是用字节减去长度字段的长度,即使用const s = (((size_t *)p)[-1] & (~(size_t)SIZE_BITS)) - sizeof (size_t);
。
(我在x86-64上的嵌入式GNU C库2.15-0ubuntu1015上验证了这一点,适用于64位和32位代码(具有不同的size_t
大小)。)
我建议您至少添加最低限度的抽象,这样将来将代码移植到不同的C库或GNU C库的新版本中就不会是徒劳的。(版本检查会很好,但我太懒了,没有发现GNUC库的哪些版本真正使用了这种布局。)
#include <string.h>
#include <limits.h>
#ifdef __GLIBC__
/* GLIBC stuffs the length just prior to the returned pointer,
* with flags in the least significant three bits. It includes
* the length field itself. */
#define USER_LEN(ptr) ( ( ((size_t *)(ptr))[-1] & (~((size_t)7)) ) - sizeof (size_t))
#else
#error This C library is not supported (yet).
#endif
extern void abort(void);
extern void *__libc_malloc(size_t);
extern void *__libc_realloc(void *, size_t);
extern void __libc_free(void *);
#define CANARY_LEN 1
static void canary_set(void *const ptr, const size_t len)
{
((unsigned char *)ptr)[len - CANARY_LEN] = '@';
}
static int canary_ok(const void *const ptr, const size_t len)
{
return ((const unsigned char *)ptr)[len - CANARY_LEN] == '@';
}
void *malloc(size_t size)
{
void *ptr;
ptr = __libc_malloc(size + CANARY_LEN);
if (ptr) {
const size_t len = USER_LEN(ptr);
memset(ptr, 0, len);
canary_set(ptr, len);
}
return ptr;
}
void *realloc(void *ptr, size_t size)
{
void *newptr;
if (!ptr)
return malloc(size);
if (!canary_ok(ptr, USER_LEN(ptr)))
abort();
newptr = __libc_realloc(ptr, size + CANARY_LEN);
if (!newptr)
return newptr;
canary_set(newptr, USER_LEN(ptr));
return newptr;
}
void free(void *ptr)
{
if (ptr) {
const size_t len = USER_LEN(ptr);
if (!canary_ok(ptr, len))
abort();
memset(ptr, 0, len);
__libc_free(ptr);
}
}
希望这能有所帮助。