在低内存地址分配
Allocate at low memory address
为了在极限条件下测试软件,我尝试创建一个测试用例,其中提供的用户缓冲区分配在某个非常低的内存地址。非常接近 NULL
的东西,例如 0x1000h
.
事实证明这是一个很难创造的条件。
实际上,我无法在 Linux、BSD、Windows 或 OS-X 上使用 malloc()
生成它。
我相信这种情况可能发生在其他类型的设备上,但我需要一个可以插入到 CI 测试套件中的可重现测试用例。
是否有任何具有适度复杂性(和依赖性)的已知方法来生成此类条件?
编辑 : 选择解决方案, using mmap()
. Note that, ,首先需要降低最低地址限制,在/proc/sys/vm/mmap_min_addr
可读(在Linux ).
sudo sysctl -w vm.mmap_min_addr="4096"
然后示例代码:
#include <stdio.h> /* printf */
#include <sys/mman.h> /* mmap */
int main(int argc, const char** argv)
{
void* lowBuff = mmap((void*)(0x1000), 64<<10,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
printf("lowBuff starts at %p \n", lowBuff);
}
结果:
lowBuff starts at 0x1000
你只能在 C 中分配虚拟内存(据我所知,malloc 是 Windows 上 VirtualAlloc 的包装器),虚拟内存由你的操作系统管理,所以几乎不可能预测你会得到什么地址。
但它可能永远不会接近 0x00。除非你正在编写一个自定义的内存分配系统,否则你真的不必处理它,即使那样它也不应该关心提供的地址。
在 POSIX/Unix 系统上,您可以使用 mmap
在特定的页面对齐地址请求内存。您可以获得的最低价格取决于您的特定系统和环境。
在Windows,内存的最低部分(64KB?)被系统保留,专门用于捕获通过无效指针的访问。例如,如果您尝试取消引用空指针(可能带有偏移量),您将触发访问冲突。所以 0x1000 不在 table 内,因为它在前 64KB 内。
超出初始范围,您可以尝试使用 VirtualAlloc 保留和提交内存,指定一个基地址(参数名为 lpAddress)。基地址将向下舍入到分配粒度(我认为也是 64KB)。如果该内存可用,则一切就绪,但它可能不可用。
LPVOID desired_address = (LPVOID) 0x00010000;
LPVOID actual_address =
VirtualAlloc(desired_address, desired_size,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (actual_address == NULL) {
// we couldn't get the address we wanted.
} else if (actual_address != desired_address) {
// something very weird happened
} else {
// we got the low memory we wanted
}
从历史上看,典型的 32 位用户进程的 executable 代码从 0x00400000 开始加载,而 DLL 通常从更高的位置开始(如 0x10000000),因此您 可能 得到幸运地在第二个 64KB 块中拍摄了一些东西。但是地址 space 布局随机化 (ASLR) 会更改这些默认值,谁知道在您有机会之前其他库会抢走哪些奇怪的地址。
所以你可以尝试,但你必须准备好尝试使用 VirtualAlloc 获取低内存失败的可能性。
为了在极限条件下测试软件,我尝试创建一个测试用例,其中提供的用户缓冲区分配在某个非常低的内存地址。非常接近 NULL
的东西,例如 0x1000h
.
事实证明这是一个很难创造的条件。
实际上,我无法在 Linux、BSD、Windows 或 OS-X 上使用 malloc()
生成它。
我相信这种情况可能发生在其他类型的设备上,但我需要一个可以插入到 CI 测试套件中的可重现测试用例。
是否有任何具有适度复杂性(和依赖性)的已知方法来生成此类条件?
编辑 : 选择解决方案mmap()
. Note that, /proc/sys/vm/mmap_min_addr
可读(在Linux ).
sudo sysctl -w vm.mmap_min_addr="4096"
然后示例代码:
#include <stdio.h> /* printf */
#include <sys/mman.h> /* mmap */
int main(int argc, const char** argv)
{
void* lowBuff = mmap((void*)(0x1000), 64<<10,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
printf("lowBuff starts at %p \n", lowBuff);
}
结果:
lowBuff starts at 0x1000
你只能在 C 中分配虚拟内存(据我所知,malloc 是 Windows 上 VirtualAlloc 的包装器),虚拟内存由你的操作系统管理,所以几乎不可能预测你会得到什么地址。 但它可能永远不会接近 0x00。除非你正在编写一个自定义的内存分配系统,否则你真的不必处理它,即使那样它也不应该关心提供的地址。
在 POSIX/Unix 系统上,您可以使用 mmap
在特定的页面对齐地址请求内存。您可以获得的最低价格取决于您的特定系统和环境。
在Windows,内存的最低部分(64KB?)被系统保留,专门用于捕获通过无效指针的访问。例如,如果您尝试取消引用空指针(可能带有偏移量),您将触发访问冲突。所以 0x1000 不在 table 内,因为它在前 64KB 内。
超出初始范围,您可以尝试使用 VirtualAlloc 保留和提交内存,指定一个基地址(参数名为 lpAddress)。基地址将向下舍入到分配粒度(我认为也是 64KB)。如果该内存可用,则一切就绪,但它可能不可用。
LPVOID desired_address = (LPVOID) 0x00010000;
LPVOID actual_address =
VirtualAlloc(desired_address, desired_size,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (actual_address == NULL) {
// we couldn't get the address we wanted.
} else if (actual_address != desired_address) {
// something very weird happened
} else {
// we got the low memory we wanted
}
从历史上看,典型的 32 位用户进程的 executable 代码从 0x00400000 开始加载,而 DLL 通常从更高的位置开始(如 0x10000000),因此您 可能 得到幸运地在第二个 64KB 块中拍摄了一些东西。但是地址 space 布局随机化 (ASLR) 会更改这些默认值,谁知道在您有机会之前其他库会抢走哪些奇怪的地址。
所以你可以尝试,但你必须准备好尝试使用 VirtualAlloc 获取低内存失败的可能性。