`Syscall param read(buf) points to unaddressable bytes` and `address is 0 bytes after a block of n bytes alloc'd`

`Syscall param read(buf) points to unaddressable bytes` and `address is 0 bytes after a block of n bytes alloc'd`

Valgrind 给我奇怪的输出,如果分配了更多内存,该输出就会消失。在我的程序中,我必须添加才能使其消失的数字是 2064。这个数字在我的程序中没有出现,我已经在这件事上上下下了几个小时,试图找到我可能出错的地方。没有运气。一切似乎都密不透风,我看不出为什么需要额外的 2064 字节内存。

谢天谢地,我设法在一个最小的程序中重现了这个错误。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv){
    /* 8500 is a number I chose that is just above the generated executable size (8472)
     * I double-checked after compiling and the executable is still under 8472 bytes
     * You may have to choose a different size.
     */
    unsigned char *p = malloc(8500);
    unsigned char *pp;
    FILE *fp = fopen(argv[0], "rb");
    ssize_t nread;

    if(p == NULL)
        return 1;

    pp = p;

    while((nread = fread(pp, 1, 4096, fp)) > 0)
        pp += nread;

    free(p);
    fclose(fp);
    return 0;
}

valgrind(和 wc)的输出:

$ gcc wtf.c -Wall
$ wc -c a.out
8472
$ valgrind ./a.out
==31142== Memcheck, a memory error detector
==31142== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==31142== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31142== Command: ./a.out
==31142==
==31142== Syscall param read(buf) points to unaddressable byte(s)
==31142==    at 0x4F4E151: read (read.c:27)
==31142==    by 0x4EC9741: _IO_file_xsgetn (fileops.c:1364)
==31142==    by 0x4EBD4A0: fread (iofread.c:38)
==31142==    by 0x1087CF: main (in /home/braden/code/git/bfvm2/src/a.out)
==31142==  Address 0x5231174 is 0 bytes after a block of size 8,500 alloc'd
==31142==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==31142==    by 0x108782: main (in /home/braden/code/git/bfvm2/src/a.out)
==31142==
==31142==
==31142== HEAP SUMMARY:
==31142==     in use at exit: 0 bytes in 0 blocks
==31142==   total heap usage: 3 allocs, 3 frees, 13,148 bytes allocated
==31142==
==31142== All heap blocks were freed -- no leaks are possible
==31142==
==31142== For counts of detected and suppressed errors, rerun with: -v
==31142== ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)

有什么想法吗?我不相信这里有任何未定义的行为。这并没有阻止我的程序 运行,但它让我发疯,我不希望它在未来变成严重的事情。

valgrind 消息表明试图写入已分配 space 末尾。

这可能是由于调用 fread 请求 4096 字节,而缓冲区中没有那么多 space 剩余——尽管可能只有少数输入流中剩余的字符。

C 标准在这个问题上并不完全清楚,但无论如何它似乎是:

  • 您的编译器擅自写入 4096 请求中超出分配的 space 末尾的其他位置,或
  • Valgrind 正在报告将不完整的缓冲区传递给系统调用 read 的错误,即使 read 调用不会写到最后。

为避免这种情况,请确保传递给 fread 的最大范围不超过剩余的缓冲区大小,例如fread(pp, 1, MIN(4096, (p + 8500 - pp)), fp) 其中 MIN 是通常的最小宏。