【问题标题】:Is unused memory in address space protected地址空间中未使用的内存是否受到保护
【发布时间】:2011-11-18 22:10:26
【问题描述】:

进程地址空间中未使用的内存是否仅通过读取权限来保护,例如,写入单元化指针指向的位置总是会导致页面错误被操作系统捕获?或者不是这样,除了代码之外的每个内存位置(当然是只读访问),都被赋予了写访问权限?

我问这个是因为我的朋友向我展示了他的代码,他没有初始化指针并写入指针指向的内存,但他的程序仍然没有因为 windows 的 mingw gcc 编译器而崩溃,但总是崩溃在 mac 或 linux 中使用 Visual c++。

我认为操作系统不保护未使用区域的内存并且导致崩溃是因为在 mingw 生成的代码中,随机指针值指向一些已使用的区域,例如堆栈、堆或代码,而在其他情况下,它指向一些空闲区域。但是如果操作系统真的不保护未使用的区域,那么这些类型的错误,例如未初始化的指针,是不是很难调试?

我想这就是为什么建议在调用deletefree 后始终将NULL 分配给指针,这样当使用它访问某些内容时,它确实会导致可见的崩溃。

【问题讨论】:

  • 是的,未初始化的指针错误很难调试。症状往往非常神秘。如果你发现自己说“代码不可能那样做……”,怀疑是未初始化的指针。
  • 观察到的编译器之间的差异可能与未初始化指针的初始化对象有关。从编译器的角度来看,一种“安全”的方法是将所有指针初始化为 NULL - 这样,如果您使用它,您将导致段错误而不是指向内存中的随机位置......但这是特定于实现的,可能会解释你观察到了什么。

标签: c linux operating-system


【解决方案1】:

未初始化的指针不一定指向未使用的地址空间。它们很可能是恰好指向可写内存的值。例如堆栈上的指针恰好是先前执行的函数存储有效地址的位置。

【讨论】:

  • 我在问他们指向某个空闲区域的情况。我知道当它指向已使用的区域(例如堆栈或代码内存)时,它肯定会崩溃。
  • 如果它指向堆栈,它可能根本不会崩溃,堆栈通常是读/写的。代码段通常是读取/执行的,在某些架构上可能只是执行
  • 如果它指向 freed 内存,它也几乎肯定不会(立即)崩溃,因为大多数实现不会取消映射或返回到 OS 释放最多的内存。跨度>
  • 关键是,如果指针未初始化,它可以指向任何东西——代码、堆栈、释放的内存、代码中其他地方分配的内存、未对齐的内存、NULL、操作系统内存、内存硬件映射到一个物理设备(受操作系统的限制),随便什么。
【解决方案2】:

在典型的当前服务器/桌面操作系统(以及相当多的小型系统,例如手机)中,您拥有虚拟内存。这意味着操作系统会构建一个表,将代码使用的虚拟地址转换为指定要寻址的实际内存的物理地址。这种映射通常在“页面”中完成——例如,一次 4KB 的内存块。

至少在通常情况下,根本不使用的地址空间部分根本不会被映射 - 即,操作系统不会在表中为该部分构建条目地址空间。但是请注意,分配的内存将(必须)四舍五入为页面大小的倍数,因此每个正在使用的内存块通常后面会跟着一些没有真正使用但仍然分配和“可用”。由于保护也(通常)在每页的基础上完成,如果该页面的其余部分(比如说)是只读的,则尾端的其余部分将是相同的。

【讨论】:

  • 杰瑞,我知道虚拟内存和页面等等,我只是想问一下,如果只提供读取访问权限,空闲区域是否受到保护。
  • @MetallicPriest:大多数未使用的页面都设置为没有页面描述符,因此它们不是只读的或其他任何东西。
【解决方案3】:

这取决于操作系统的实现。在某些配置中,例如,ExecShield 将保护大部分超出程序范围的内存,并且通常要保护数据段的前几个字节(使用 NULL 指针表示访问),但也有可能是指针实际上指向程序中一个有效的、任意的内存地址。

【讨论】:

    【解决方案4】:

    c/c++ 不提供内存保护。您可能会发现指针恰好包含指向有效内存的指针,例如前一个函数在堆栈上有一个 ptr 变量,而稍后调用的另一个函数恰好使用相同的堆栈空间作为指针。

    如果使用 gcc 编译和运行以下代码,将打印“Hello”: #包括

    char buffer[10];
    
    void function1(void) {
        char * ptr = buffer;
        sprintf(buffer, "Hello");
        return;
    }
    
    void function2(void) {
        char * ptr2;
        printf("%s\n", ptr2);
    }
    
    int main(int argc, char * argv[]) {
        function1();
        function2();
    }
    

    对于调试版本,一些编译器(我知道 Visual Studio 曾经这样做过)会秘密地将所有变量(如 ptr2)初始化为错误值以检测此类错误。

    使用 C 通常你会发现内存已被操作系统滥用而杀死你的程序。

    【讨论】:

      【解决方案5】:

      简单地说,我假设答案是“不,地址中未使用的内存不受空间保护。” C 语言不够复杂,无法处理此类情况。

      【讨论】:

      • 这与C无关,但与进程内存映射保护有关,这取决于操作系统。
      • 但是这种保护可以内置到语言中,我假设当他提到“空间保护”时,他是在强调特定语言的属性(无论它是否内置):例外。跨度>
      猜你喜欢
      • 2013-12-14
      • 1970-01-01
      • 1970-01-01
      • 2015-09-05
      • 2011-06-30
      • 2014-05-23
      • 2012-04-22
      • 2019-11-06
      • 2014-01-16
      相关资源
      最近更新 更多