【问题标题】:Writing more characters than malloced. Why does it not fail?写比 malloced 更多的字符。为什么它不会失败?
【发布时间】:2011-10-06 19:12:15
【问题描述】:

为什么以下工作不会引发某种分段错误?

char *path = "/usr/bin/";
char *random = "012";

// path + random + \0
// so its malloc(13), but I get 16 bytes due to memory alignment (im on 32bit)
newPath = (char *) malloc(strlen(path) + strlen(random) + 1);

strcat(newPath, path);
strcat(newPath, "random");
// newPath is now: "/usr/bin/012\0" which makes 13 characters.

但是,如果我添加

strcat(newPath, "RANDOMBUNNIES");

这个调用不应该失败,因为 strcat 使用的内存比分配的多吗?因此,不应该

free(newPath)

也失败了,因为它试图释放 16 个字节,但我使用了 26 个字节(“/usr/bin/012RANDOMBUNNIES\0”)?

非常感谢您!

【问题讨论】:

    标签: c memory-management segmentation-fault memory-alignment strcat


    【解决方案1】:

    大多数情况下,这种溢出问题不会使您的程序在烟雾和烧焦的硫磺味中爆炸。更微妙的是:在溢出变量之后分配的变量将被更改,从而导致程序稍后出现无法解释且看似随机的行为。

    【讨论】:

    • 如果内存溢出确实导致程序爆炸,那就太棒了——想想找到它们是多么容易!当您可以分配 n 个字节并写入 n +1 个字节并且在 100 次中 99 次都没有看到不良影响时,很难找到问题。
    • @graeme 同意 100%。这就是为什么特别在 C 程序员中应该小心、非常小心和非常有条理
    • @graeme:如果您喜欢这种行为,您应该查看Electric Fence。来自 README:Electric Fence 是一种不同的 malloc() 调试器。它使用系统的虚拟内存硬件来检测软件何时超出 malloc() 缓冲区的边界。它还将检测任何已由 free() 释放的内存访问。因为它使用 VM 硬件进行检测,所以 Electric Fence 会在导致边界违规的第一条指令处停止您的程序。
    【解决方案2】:

    它会失败,也不会随机失败,这取决于 malloc 内存之后的内存可用性。

    此外,当您想连接随机时,您不应该加上引号。那应该是

    strcat(newPath, random);
    

    【讨论】:

      【解决方案3】:

      许多 C 库函数不检查它们是否溢出。由程序员来管理分配的内存。您可能只是在内存中写入另一个变量,对程序的操作产生不可预测的影响。 C 是为提高效率而设计的,而不是为了指出编程中的错误。

      【讨论】:

        【解决方案4】:

        这个电话你很幸运。您不会收到段错误,因为您的调用可能停留在地址空间的分配部分中。这是未定义的行为。不保证不会覆盖已写入内容的最后一个字符。此调用也可能失败。

        【讨论】:

        • 那不走运。相反,这意味着您破坏了其他有价值的数据(可能是内部的malloc 簿记),这将导致稍后在程序执行中出现不正确的行为(最坏的情况:安全漏洞或大量数据丢失)。幸运的是,如果程序崩溃了,那么您会立即知道存在错误,而没有其他任何东西被破坏......
        • 一些调试器会在那里崩溃。当然,幸运意味着电话没有出错,因为他们不能,而是因为例如。该页面被分配到存储额外字节的位置。
        • 我知道为什么。我只是认为“不崩溃”是不幸(危险)的可能性,而“崩溃”是幸运的可能性。
        【解决方案5】:

        写入比 malloced 更多的字符是一种未定义行为
        未定义的行为意味着任何事情都可能发生并且行为无法解释。

        【讨论】:

          【解决方案6】:

          不保证缓冲区溢出会导致段错误。行为只是未定义的。您可能会一次成功地写入不属于您的内存,另一次导致崩溃,第三次默默地覆盖完全不相关的内容。其中哪一种发生取决于操作系统(和操作系统版本)、硬件、编译器(和编译器标志)以及系统上运行的几乎所有其他内容。

          这就是缓冲区溢出如此严重的错误来源的原因:通常,明显的症状会在生产中显示出来,但在通过调试器运行时不会;并且症状通常不会显示在它们起源的程序部分中。当然,它们是一个受欢迎的漏洞,可以注入您自己的代码。

          【讨论】:

            【解决方案7】:

            操作系统在我的系统上以一定的粒度分配 page-size 4kb(这在 32 位机器上是典型的),malloc() 是否总是从操作系统获取新页面取决于您的 C 运行时库

            【讨论】:

              【解决方案8】:

              整个程序sn-p是错误的。您假设malloc() 返回的内容至少第一个字节设置为0。通常情况并非如此,因此即使您的“安全”strcat() 也是错误的。

              但是,正如其他人所说,未定义的行为并不意味着您的程序会崩溃。这只意味着它可以做任何事情(包括崩溃,但也不会崩溃,如果你不走运的话)。

              (另外,你不应该转换malloc()的返回值。)

              【讨论】:

              • 为什么不应该转换malloc的返回值?
              • 好的,谢谢你的链接。我知道在 C 中它不是必要的,但我从没想过它实际上是一个坏主意。
              • 为什么我假设第一个字节设置为 0?不明白。
              • @pewpew, strcat(newPath, path); 假定 newPath 是一个以空字符结尾的字符串。由于newPathmalloc() 的返回值,因此不能保证其中会有一个0 字节(malloc() 只是分配内存,它不会将其设置为任何内容。请参阅memset()calloc()如果你想要,甚至在第一个 strcat() 之前的 newPath[0] = 0; 都可以。)
              【解决方案9】:

              分段错误一般是因为访问了无效的内存段。在这里它不会给出错误(Segmentation fault),因为您仍然可以访问内存。但是,您正在覆盖其他未定义行为的内存位置,您的代码运行良好。

              【讨论】:

                猜你喜欢
                • 2021-02-22
                • 1970-01-01
                • 1970-01-01
                • 2010-10-22
                • 1970-01-01
                • 1970-01-01
                • 2011-04-04
                • 2012-06-28
                • 1970-01-01
                相关资源
                最近更新 更多