【问题标题】:What is the difference between memory leak, accessing freed memory and double free?内存泄漏,访问释放的内存和双重释放有什么区别?
【发布时间】:2017-11-28 14:04:28
【问题描述】:

我试图弄清楚与内存模型相关的这三种问题之间的区别。

如果我想模拟memory leak的场景,我可以创建一个指针而不调用对应的delete方法。

int main() {
    // OK
    int * p = new int;
    delete p; 

    // Memory leak
    int * q = new int;
    // no delete
}

如果我想模拟double free的场景,我可以释放一个指针两次,这部分内存稍后会分配两次。

a = malloc(10);     // 0xa04010
b = malloc(10);     // 0xa04030
c = malloc(10);     // 0xa04050

free(a);
free(b);  // To bypass "double free or corruption (fasttop)" check
free(a);  // Double Free !!

d = malloc(10);     // 0xa04010
e = malloc(10);     // 0xa04030
f = malloc(10);     // 0xa04010   - Same as 'd' !

但是,我不知道accessing freed memory 是什么。谁能给我一个accessing freed memory的例子吗?

【问题讨论】:

    标签: c++ memory memory-management memory-leaks


    【解决方案1】:
    1. 内存泄漏很糟糕。
    2. 双重免费更糟糕。
    3. 访问释放的内存更糟糕

    内存泄漏

    这不是一个错误本身。泄漏的程序仍然有效。这可能不是问题。但这仍然很糟糕;随着时间的推移,您的程序将从主机保留内存并且永远不会释放它。如果在程序完成之前主机的内存已满,你就会遇到麻烦。

    双重释放

    根据标准,这是未定义的行为。实际上,这几乎总是由 C++ 运行时调用 std::abort()

    访问释放的内存

    还有未定义的行为。但在某些情况下,不会发生任何不好的事情。您将测试您的程序,并将其投入生产。总有一天,它会在没有明显原因的情况下破裂。它会很难打破:随机。修改简历的最佳时机。

    这里是如何访问释放的内存:

    // dont do this at home
    int* n = new int{};
    delete n;
    std::cout << *n << "\n"; // UNDEFINED BEHAVIOUR. DONT.
    

    【讨论】:

    • "break" 表示崩溃 - 我觉得应该指出“中断”可能只是读取了错误的数据。在您授予某人仅删除一个登录会话的文件的权限并且您的所有日志都说他们不能等等之前,这听起来还不错(因此有新的简历要求)
    • @UKMonkey 你听说过鼻恶魔吗?
    • @cmaster 想象以下场景:a=new int; delete a; b=new int /*suppose reuse =&gt; a==b*/; delete a; /* ... */ delete b /* =&gt; abort()*/;:您获得了释放的内存访问权限,但也获得了仁慈的中止。
    • 恕我直言,double-free 的唯一特别之处在于,它是一种 可检测 形式的 use-after-free。不过,它的危险性并没有降低。
    • @cmaster 你会说两者都应该是 worser :D?无论如何我同意,两者都非常糟糕。我只是想命令他们找点乐子。 (编辑:在阅读您的最后一条评论之前写下;我们同意;)
    【解决方案2】:

    您的内存泄漏示例(分配内存但释放它)和双重释放(将分配内存的指针传递给free / delete 不止一次)是正确的。

    然而,执行双重释放并不意味着malloc 将多次返回一段内存,如您的示例所示。它所做的是调用undefined behavior,这意味着您的程序的行为无法预测。

    访问已释放的内存意味着释放一个指针,然后尝试使用它:

    int *a = malloc(10 * sizeof(int));     // allocate memory
    free(a);                               // free memory
    print("a[0]=%d\n", a[0]);              // illegal: use after free
    

    【讨论】:

    • 这可以作为一个例子吗?新建一个长度为 3 的数组,删除它并尝试访问它的组件。 c = new int[3]; delete c; res = c[2];
    • @Coding_Rabbit 是的,是一样的。
    • @Coding_Rabbit 是的,你的例子很有效。
    【解决方案3】:

    您对内存泄漏和双重释放是正确的。当您取消引用已释放的指针时,会访问已释放的内存:

    int *ptr = malloc(sizeof(int));
    *ptr = 123;
    free(ptr);
    int invalid = *ptr; // Accessing freed memory
    

    众所周知,此类问题很难检测到,因为该程序会继续按预期工作一段时间。如果您希望稍后重用指针变量,最好在调用free 后立即为其分配NULL。这样,后续的取消引用将很快失败。

    【讨论】:

      【解决方案4】:

      我试图弄清楚与内存模型相关的这三种问题之间的区别。

      • 内存泄漏 - 您动态分配内存并且永远不会释放它。

      • 双重释放——动态分配内存并多次释放

      • 释放后访问 - 您动态分配内存,然后释放并在释放后访问该内存。

      【讨论】:

      • 我认为内存泄漏是指内存未释放和未引用(因此不可释放)时更正确。
      • @EmeraldWeapon 不,它不会。例如智能指针中的交叉引用。内存被引用了,但还是内存泄漏。
      猜你喜欢
      • 2012-06-08
      • 1970-01-01
      • 2011-01-27
      • 1970-01-01
      • 2014-08-26
      • 1970-01-01
      • 2018-06-04
      • 2014-09-19
      • 1970-01-01
      相关资源
      最近更新 更多