【问题标题】:How to judge an address as legal or illegal by it‘s numerical value? [closed]如何通过数值判断地址合法还是非法? [关闭]
【发布时间】:2014-01-22 16:13:54
【问题描述】:

我的意思是地址本身的数值,而不是它指向的值。 比如一个地址是0x0,我们肯定知道它是非法的,但是如果是0xffffeeee234560,我怎么判断它是正常的还是异常的呢? 另外,如何知道这个地址是属于文本段,还是数据段,还是堆,还是栈段?

我用过pmap,cat /proc/id/smaps看看有没有明确的规则,但无法得到合理的方法,只知道堆地址大于文本段,栈地址大于堆。

【问题讨论】:

  • 出于好奇,你为什么想知道这个?
  • 你无法分辨,除非使用强烈的平台特定知识,并且在内核的最低级别之外可能无法获得这些信息。
  • 根据您的应用程序,您可能会发现这种在 Windows 中识别非法指针的体验很有趣:blogs.msdn.com/b/oldnewthing/archive/2006/09/27/773741.aspx
  • 你为什么问?是出于调试目的,还是因为您想在自己的内存分配器中耍花招?
  • 解释您的总体担忧您的问题的动机(是调试您的程序还是实现您的内存分配器);我们以不同的方式猜测您问题的动机及其背景。请编辑您的问题以改进它。

标签: c++ c linux memory


【解决方案1】:

如果您正在调试程序(使用gcc -Wall -g 编译),gdb 调试器会告诉您某个地址是否非法。 还可以使用valgrind 和 GCC 4.8 (gcc -fsanitize=address) 的地址清理程序...

如果你想在你的程序中知道某个特定地址是否在它的address space 中(process 运行你的程序)以及它在哪个段中,你可以做一个例程解析 /proc/self/maps 到去做。读取该文件(或来自proc(5) ...的其他文件)非常快(因为磁盘上不存在此类文件)。

通常没有单个文本,或单个数据,或单个堆栈(想想multi-threaded应用程序和dynamic linking)在一个给定的进程。您的地址空间(运行程序的进程的地址空间)中有几个段,由于ASLR,它们通常由内核“随机”布局 . (可以在系统范围内禁用 ASLR)

但是,如果地址值在运行时对您的应用程序很重要(有时在地址中编码一些类型信息很有趣,即在一个段中分配对,在另一个段中分配三元组,在其他地方分配更大的对象),您应该采用相反的方法:使用mmap(2)munmap(2)(由posix_memalign(3)...调用)由您自己显式管理大内存段(例如对齐到兆字节),当您保留段时,将它们注册到适当的容器中(例如 C++ 中的std::map)。然后你可以很容易地编写一个例程,给定一些任意地址(任何void*),获取包含它的段(或者nullptr)。不要忘记malloc 可以在许多库例程内部使用(包括printf 和C++ 标准容器...)。所以malloc 总是被使用,即使你不知道如何使用。您可能对mallinfo(3)malloc_info(3)mallopt(3) 感兴趣。另请阅读C dynamic memory allocationmemory management wikipages,如果需要,还可以研究malloc 的源代码(MUSL libc 内部的代码很容易阅读)。

考虑阅读Advanced Linux Programming;它应该对你有帮助。

【讨论】:

  • +1 用于 valgrind,假设 OP 想要调试。绝对的救星。
【解决方案2】:

无论它是否绝对非法,使用代码的任何特定部分都是非法的,除非你知道,作为你如何获得该地址的逻辑结果,它是有效的并且指向一个您可以合法修改的对象。

总之,如果你非要问,那是违法的。

【讨论】:

    【解决方案3】:

    对于运行在 32 位 Linux 上的用户进程,任何从0xc0000000 向上的虚拟地址都是非法的,因为它属于内核。对于没有/3G 引导开关的Windows 32 位,任何等于或大于0x80000000 的虚拟地址都是非法的,原因相同(如果使用/3G 并且进程可执行文件启用了LARGE_ADDRESS 标志,则边界与Linux中的地址相同)

    在不离开 32 位 Linux 的情况下,如果您能够遍历自己进程的页面目录,则可以获取任何虚拟地址并查看它是否映射到实际物理页面中的某个位置,如果是,哪个该页面是否具有权限。

    小心!因为malloc()分配的内存当然是合法的,但是如果你尝试通过剥离目录页来检查它的合法性,你会发现几乎所有分配的内存块都显示为“不存在”,因此你会推断地址是非法的。当对块内的有效地址执行内存访问,并且该地址属于仍然不存在的页面时,会触发页面错误,但不会成为段错误,但内核会静默分配实际内存来映射页面访问的地址属于哪个地址,并重新发出最先导致页面错误的指令。

    使用合适的设备驱动程序,进程可以了解自己的内存映射并遍历其页面目录。当然,这在很大程度上是特定于实现的。详情请见here

    【讨论】:

    • 一个地址可能属于某个段并且是free-d....
    • 至少在32位Linux中,属于分配了malloc(),然后free()-ed的块的地址变得非法,因为内核将其从物理内存中解映射,即进程内存映射将地址所属的页面标记为“不存在”(因此,非法)。
    • 不,你完全错了:我查看的所有malloc 实现都在重用小的free-d 区域,它们仍然停留在mmap-ed 堆段中。 malloc 努力避免 mmapmunmap 通过管理以前的 free-d 区域(让它们通过未来 malloc-s)。
    • 在对生成此内存映射 (stackoverflow.com/questions/20792158/…) 的代码进行一些实验时,我可以看到在 free() 之后,几乎所有分配的块都变成了“不存在”的内存。
    • 如果你 mallocfree(按 随机 顺序)小的内存区域(例如每个 80 字节),它们通常会在之后停留在 mmap-ed 段中free(不会调用munmap...)。
    【解决方案4】:

    您可以在汇编中编写一个程序,将一些值存储在堆、堆栈、文本和数据段中,然后获取这些值的地址并打印地址。

    但是,这仅对单个程序有效,并且在程序的不同运行中可能不一致。如果一个地址在第一个和最后一个堆栈项的地址之间,它应该是合法的,第一个和最后一个文本项之间,第一个和最后一个数据项之间,等等。

    【讨论】:

    • 这并没有真正回答所写的问题。充其量,它仅在被检查的地址恰好落在您提供的三个范围之一内时才有效,但其他地址可能在不同时间的特定执行中有效。
    • 我应该更清楚;通过“等等”,我的意思是它适用于所有描述的范围(堆、堆栈、文本、数据)。由于这些范围(和可以类似处理的 bss)通常涵盖分配给程序的全部内存量,如果它们都是已知的,那么可以说它们之外的任何地址都是无效的。当然,它仅在测量地址时有效;如果程序状态发生变化并且使用了更多/更少的堆栈/堆,那么显然需要在可能的情况下重新计算范围。
    • 不需要为此使用 assembly
    • 没错,但使用汇编对 C 的熟悉程度较低,而且更容易准确地查看数据的放置位置。
    猜你喜欢
    • 2012-07-30
    • 1970-01-01
    • 1970-01-01
    • 2021-02-21
    • 1970-01-01
    • 2015-05-11
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多