如何创建低内存地址(近NULL)分配的可复现CI测试用例?
这个问题我之前做嵌入式软件极限测试时也遇到过——通用系统的malloc()出于安全考虑,会刻意避开接近NULL的低地址区域,但要在CI测试套件里复现这个场景,其实有几个实用的方法:
1. 自定义内存分配器替换malloc()
通过钩子(hook)或者动态库注入的方式,在测试环境中替换系统默认的malloc(),直接返回你需要的低地址。
Linux/BSD 下的实现思路
用LD_PRELOAD加载一个自定义的共享库,重写malloc()函数:
#include <stdlib.h> #include <dlfcn.h> // 保存真实的malloc函数指针 static void* (*real_malloc)(size_t) = NULL; void* malloc(size_t size) { // 只在测试模式下返回低地址,否则调用真实malloc if (getenv("TEST_LOW_ADDR") != NULL) { return (void*)0x1000; } if (!real_malloc) { real_malloc = dlsym(RTLD_NEXT, "malloc"); } return real_malloc(size); }
编译成共享库:gcc -shared -fPIC -o low_addr_malloc.so low_addr_malloc.c -ldl
CI运行测试时,设置环境变量加载这个库:export LD_PRELOAD=./low_addr_malloc.so; export TEST_LOW_ADDR=1; ./your_test_program
Windows 下的实现思路
可以使用_malloc_dbg或者通过Detours库来hookmalloc(),原理类似——测试时返回指定低地址,正常逻辑调用原函数。
2. 直接用内存映射(mmap)申请低地址
通用系统的malloc()不会碰低地址,但mmap()允许你指定地址分配内存,只要该地址未被系统占用。
Linux/BSD 示例
#include <sys/mman.h> #include <stdio.h> void* get_low_addr_buffer(size_t size) { void* addr = (void*)0x1000; void* buf = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); if (buf == MAP_FAILED) { // 如果0x1000被占用,可以尝试0x2000等邻近地址 addr = (void*)0x2000; buf = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); } return buf; }
MAP_FIXED参数会强制让内核使用你指定的地址(如果可用),CI环境通常是干净的,低地址大概率未被占用。测试完成后记得用munmap()释放内存。
Windows 示例
用VirtualAlloc指定地址分配:
#include <windows.h> void* get_low_addr_buffer(size_t size) { void* addr = (void*)0x1000; void* buf = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!buf) { addr = (void*)0x2000; buf = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); } return buf; }
3. 模拟低地址场景(无需真实内存)
如果你的测试只需要验证指针边界检查逻辑,不需要实际读写内存,可以直接用宏或编译选项替换malloc()的返回值:
在测试代码中添加:
#ifdef TEST_LOW_ADDR #define malloc(size) ((void*)0x1000) #endif
编译测试程序时加上-DTEST_LOW_ADDR,这样所有malloc()调用都会返回0x1000。这种方式最简单,适合快速验证空指针附近的逻辑漏洞。
4. 修改系统配置让malloc()分配低地址
部分系统允许调整内存分配的最低地址限制,让malloc()有可能返回接近NULL的地址:
Linux 下调整vm.mmap_min_addr
# 临时设置最低内存映射地址为0(需要root权限) sysctl -w vm.mmap_min_addr=0 # 或者永久修改,编辑/etc/sysctl.conf添加:vm.mmap_min_addr=0
调整后,malloc()可能会分配到低地址区域。这个方法适合有root权限的CI环境(比如Docker容器),但要注意部分系统可能不允许设置为0,可尝试设为0x1000。
注意事项
- 不要直接使用
0x0(NULL地址),因为几乎所有系统都会把0页标记为不可访问,读写会直接崩溃;选0x1000、0x2000这类地址更安全。 - 用
mmap或系统配置的方法时,要先检查地址是否可用,避免分配失败影响测试。 - CI环境要确保测试程序有足够权限(比如
mmap需要普通权限,调整sysctl需要root)。
内容的提问来源于stack exchange,提问作者Cyan

