C读取重新分配缓冲区

时间:2013-05-23 21:35:40

标签: c io

我在linux上阅读标准输入。我提供读取缓冲区长度不足(只有两个字符),缓冲区应该溢出和应该发生分段错误。但是程序运行正常。为什么呢?

编译:

gcc file.c -ansi

运行于:

echo abcd | ./a.out

程序:

#include<stdio.h>
#define STDIN 0

int main() {

    /* This buffer is intentionally too small for input */
    char * smallBuffer = (char *) malloc( sizeof(char) * 2 );

    int readedBytes;
    readedBytes = read(STDIN, smallBuffer, sizeof(char) * 4);

    printf("Readed: %i, String:'%s'\n", readedBytes, smallBuffer);

    return 0;
}

输出:

Readed: 4, String:'abcd'

6 个答案:

答案 0 :(得分:2)

在这种情况下,期望出现分段错误通常是错误的。你看,缓冲区溢出导致undefined behavior。这意味着这种代码的行为是不可预测的。它可能会也可能不会导致分段错误。

从技术上讲,例如,当您分配两个字节的缓冲区时,有两种可能的情况。

首先是在堆栈上分配缓冲区。堆栈本身大于2个字节,如果溢出该缓冲区,内存保护单元仍然允许您在缓冲区“外部”的内存中写入。在这种情况下,您不会得到分段,但可能会破坏堆栈中“附近”存储的其他变量,这种情况通常被称为“堆栈粉碎”。

第二种可能的情况是动态分配内存(即使用malloc())。在这种情况下,很可能实际分配的缓冲区较大,或者与之前分配/保留的内存位于同一页面上。在这种情况下,程序将写入超过两个字节的缓冲区。它可能会也可能不会收到分段违规信号,但行为未定义。

有时,如果没有特别小心,很难找到这种情况。有一些工具可以帮助追踪相似的问题。例如,Valgrind就是其中之一。

另外,如果您确定所使用的虚拟地址无效或受memory protection unit(可能是{3}}的读取,写入或执行保护,您可能只会发生分段错误。在您运行应用程序的硬件上根本不存在。)

希望它有所帮助。祝你好运!

答案 1 :(得分:1)

malloc保证为您提供至少您请求的内存量。要查看错误,您可以使用valgrind等程序,您将看到以下内容:

 ==22265== Syscall param read(buf) points to unaddressable byte(s)
 ==22265==    at 0x4F188B0: __read_nocancel (syscall-template.S:82)
 ==22265==    by 0x4005B4: main (in /home/def/p/cm/Git/git/a.out)
 ==22265==  Address 0x51f1042 is 0 bytes after a block of size 2 alloc'd
 ==22265==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
 ==22265==    by 0x400595: main (in /home/def/p/cm/Git/git/a.out)

答案 2 :(得分:1)

在这种情况下,程序会覆盖自己的一些内存。操作系统没有注意到这一点。

当进程尝试访问不属于它的内存时,会发生分段错误。但是,操作系统不是基于每个字节分配存储块,而是使用较大的块 - 页面(例如,经常使用4KB的大小)。因此,当您分配两个字节时,这两个字节由堆管理器放置在某个内存页面上(先前已分配或新分配),并且整个内存页面被标记为属于您的进程。很可能这两个字节不会在内存页面的末尾结束,也就是说你的程序在写完这两个字节之后就能写出来而没有任何操作系统异常(但很可能它会触发你更高版本)。

答案 3 :(得分:0)

缓冲区太小并不能保证程序崩溃。它取决于缓冲区后面的字节中存在哪些数据,编译器如何排列可执行文件,以及操作系统如何组织内存。 有可能缓冲区后面的字节已经“属于”你的程序,并且正在填充或以其他方式存储任何导入内容。

答案 4 :(得分:0)

第3个参数不是缓冲区的大小,而是要读取的字节数。因此,您调用该函数并说“这是一个流,从它读取4个字节并放入此缓冲区”。但它不知道缓冲区大小(它只知道文件大小)。所以它发生了什么,它尽可能多地读取并将其放入缓冲区(假设你提供了足够大的缓冲区)。那么你得到的是内存损坏。在这个简单的情况下,你的程序可能正常工作,但通常在其他地方意外失败。

答案 5 :(得分:0)

我认为你应该特别注意malloc()真正做的事情,在linux下进行malloc()调用它不仅不会失败,而且即使它返回它也不会给你真正的空间预留积极的回应。

这种行为被简单地命名为“乐观内存分配策略”或“过度使用”,它与Linux中的内核和编程严格相关并不容易,在我看来你应该切换到C ++,你会发现一个熟悉的语法开始,使用C ++提高生产力比使用C语言更有意义,使用简单的RAII方法C ++比C语言更安全。