c - glibc malloc 保护字节包装器

标签 c linux memory malloc glibc

我试图在每个分配 block 的末尾添加一个保护字符,以便如果找不到它,free() 可以abort()。为什么这些函数预加载不起作用?我意识到这不是一个可移植的方法,但我很好奇为什么它不起作用。

gcc -shared -fPIC -std=gnu99 -O2 -o wrapper.so wrapper.c

LD_PRELOAD=/path/to/wrapper.so programname

我对每个函数都有一个函数:vallocreallocpvallocposix_memalignaligned_alloc memalignmalloccalloc

#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
    }

您最终会通过保护字符部分覆盖下一个区域的长度。解决方案是减去长度字段的长度(以字节为单位),即使用 const s = (((size_t *)p)[-1] & (~(size_t)SIZE_BITS)) - sizeof (size_t);相反。

(我在 x86-64 上的嵌入式 GNU C 库 2.15-0ubuntu10.15 上验证了此方法适用于 64 位和 32 位代码(具有不同的 size_t 大小)。)

我建议您至少添加最少的抽象,这样将来将代码移植到不同的 C 库或更新版本的 GNU C 库就不会徒劳。 (版本检查会很好,但我懒得去找出哪些版本的 GNU C 库实际上使用了这个布局。)

#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);
    }
}

希望这有帮助。

关于c - glibc malloc 保护字节包装器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23108020/

相关文章:

c - insertAtTail 未在链表中添加节点

c - 当程序具有打开的数据库连接时,可以替换 sqlite 文件吗?

linux - Jenkins 中的 Maven 构建失败 : mvn: command not found

c++ - 双重自由或腐败(出)C++

C++函数返回指针,为什么这样做?

c - 使用 PortAudio 录音 : Pa_GetStreamReadAvailable not work?

c - 遍历末尾为零的字符串数组

python - 使用用户空间中的常规文件模仿 Linux 设备模型

linux - 树莓派 gpio 命令行 sysfs 不设置 gpio 输出高

C++,在for循环中分配空间,可能的内存泄漏验证