【问题标题】:Allocate at low memory address分配到低内存地址
【发布时间】:2018-02-05 20:47:16
【问题描述】:

为了在限制条件下测试软件,我正在尝试创建一个测试用例,其中提供的用户缓冲区分配在某个非常低的内存地址。非常接近NULL 的东西,例如0x1000h

事实证明,这是一个艰难的条件。 实际上,我无法在 Linux、BSD、Windows 或 OS-X 上使用 malloc() 生成它。 我确信这种情况可能发生在其他类型的设备上,但我需要一个可以插入到 CI 测试套件中的可重现测试用例。

是否有任何已知的具有中等复杂性(和依赖性)的方法来生成此类条件?

编辑:选择解决方案proposed by Eric Postpischil,使用mmap()。请注意,as underlined by R.,首先需要降低最低地址限制,在/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

【问题讨论】:

  • 虚拟内存不是这样工作的。程序中的物理地址和虚拟地址没有联系。哎呀,内存甚至可能被分页到磁盘而不是实际的 RAM。
  • 至于在内存不足的情况下进行测试,有一些工具可以帮助您解决这个问题(但在 SO 上询问它们是题外话)。但是,当您运行不足时会发生什么取决于。您的进程可能会终止,其他一些随机进程可能会终止,或者malloc 可能只是返回一个空指针(您应该始终检查并处理它)。
  • @Someprogrammerdude:这个问题问的是低值地址,而不是低内存。
  • @Cyan 你可能有一个XY Problem。你的问题是你想测试一个如果给定低内存地址可能有错误的程序。您的解决方案是尝试并实际分配该低内存地址。你在问你的解决方案。或许您应该问一下您的问题:我如何测试这种情况? 甚至更好:我如何消除这类问题? i>
  • @Someprogrammerdude:这个问题没有提到物理或虚拟寻址。是什么让您认为 OP 想要一个低物理地址?对于测试软件处理低地址的错误,虚拟地址是相关的,而不是物理地址

标签: c memory-address


【解决方案1】:

在 POSIX/Unix 系统上,您可以使用 mmap 在特定的页面对齐地址请求内存。您可以获得的最低值取决于您的特定系统和环境。

【讨论】:

  • 注意:这是非常危险的; mmapMAP_FIXED 不在乎是否已经映射了相关地址,它很乐意丢弃现有的映射。如果您需要保留一个连续的地址块(使用MAP_NORESERVEPROT_NONE 分配一个巨大的块,但没有MAP_FIXED)并延迟分配它的一部分(使用正常保护,在特定偏移量内保留块使用MAP_FIXED),但是当您没有分配块时,使用MAP_FIXED 可能会丢弃程序其他部分正在使用的映射。
  • @ShadowRanger:是的。我现在在手机上,有一段时间无法写出详细的答案。如果其他人想这样做,那可能很好。您可以请求没有MAP_FIXED 的起始地址,mmap 应该开始在该地址搜索空缺。
  • @ShadowRanger 的评论完全正确。请求地址的正确方法是mmap,而不是MAP_FIXED,但使用请求的地址而不是0 作为地址参数,而不是“映射”已知位置的某些东西。请注意,尽管 grsec 内核在没有任何理由的情况下故意破坏此功能
  • The POSIX spec isn't quite so specific;它只是说“实现以实现定义的方式使用 addr 来达到”返回值。也就是说,我怀疑您是正确的,大多数系统会尽可能尝试返回附近的未分配块,因为这是解释提示的最简单方法。
  • 另请注意,在 Linux 上,/proc/sys/vm/mmap_min_addr 控制您可以映射的最低地址。默认通常是64k左右;如果你想映射0x1000,你必须降低它。
【解决方案2】:

您只能在 C 中分配虚拟内存(据我所知,malloc 是 Windows 上 VirtualAlloc 的包装器),并且虚拟内存由您的操作系统管理,因此几乎不可能预测您将获得什么地址. 不过,它可能永远不会接近 0x00。除非您正在编写自定义的内存分配系统,否则您实际上不必处理它,即使那样它也不应该关心提供的地址。

【讨论】:

  • OP 没有表明他们在谈论物理地址。
【解决方案3】:

在 Windows 上,内存的最低部分(64KB?)由系统保留,专门用于捕获通过无效指针的访问。例如,如果您尝试取消引用空指针,可能带有偏移量,您将触发访问冲突。所以 0x1000 不在讨论范围内,因为它在前 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 位用户进程的可执行代码从 0x00400000 开始加载,而 DLL 通常开始于更高的位置(如 0x10000000),因此您可能幸运地在第二个 64KB 块中拍摄某些东西.但是地址空间布局随机化 (ASLR) 改变了这些默认设置,谁知道在你有机会之前其他库会抓取哪些奇怪的地址。

所以你可以尝试,但是你必须为尝试使用 VirtualAlloc 获取低内存失败的可能性做好准备。

【讨论】:

    猜你喜欢
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    • 2012-10-05
    • 1970-01-01
    • 1970-01-01
    • 2017-01-06
    • 2015-01-22
    • 2012-10-29
    相关资源
    最近更新 更多