Valgrind报告“大小为8的无效写入”错误。

17

我正在做一个小型的兴趣项目(www.github.com/AzP/GLSL-Validate),其中我使用了一些旧代码(对于我自己的品味来说太多的c和太少的c++,但嘿,你能做什么呢?),并试图在Linux和Windows上运行它。我遇到了一些崩溃(希望现在已经修复),但自从我开始使用Valgrind查找问题后,我就想解决我得到的投诉。

除了这段代码中存在许多难以阅读的"魔法数字"之外,我确实看不出与Valgrind投诉相关的问题所在。

我使用以下命令运行Valgrind:valgrind --track-origins=yes ./Program

291 //
292 //   Malloc a string of sufficient size and read a string into it.
293 //
294 # define MAX_SOURCE_STRINGS 5
295 char** ReadFileData(char *fileName)
296 {
297     FILE *in = fopen(fileName, "r");
298     char *fdata;
299     int count = 0;
300     char**return_data=(char**)malloc(MAX_SOURCE_STRINGS+1);
301 
302     //return_data[MAX_SOURCE_STRINGS]=NULL;
303     if (!in) {
304         printf("Error: unable to open input file: %s\n", fileName);
305         return 0;
306     }
307 
308     // Count size of file by looping through it
309     while (fgetc(in) != EOF)
310         count++;
311 
312     fseek(in, 0, SEEK_SET);
313 
314 
315     if (!(fdata = (char *)malloc(count+2))) {
316             printf("Error allocating memory\n");
317             return 0;
318     }
319     if (fread(fdata, sizeof(char), count, in) != count) {
320             printf("Error reading input file: %s\n", fileName);
321             return 0;
322     }
323     fdata[count] = '\0';
324     fclose(in);
325     if(count==0){
326         return_data[0]=(char*)malloc(count+2);
327         return_data[0][0]='\0';
328         OutputMultipleStrings=0;
329         return return_data;
330     }
331 
332     int len = (int)(ceil)((float)count/(float)OutputMultipleStrings);
333     int ptr_len=0,i=0;
334     while(count>0){
335         return_data[i]=(char*)malloc(len+2);
336         memcpy(return_data[i],fdata+ptr_len,len);
337         return_data[i][len]='\0';
338         count-=(len);
339         ptr_len+=(len);
340         if(count<len){
341             if(count==0){
342                OutputMultipleStrings=(i+1);
343                break;
344             }
345            len = count;
346         }
347         ++i;
348     }
349     return return_data;
350 }

这里是Valgrind输出。 is 0 bytes inside a block of size 6 alloc'd 的意思是我可以忽略它吗?我的意思是“0字节”听起来不危险?但既然我在这里发布了问题,我想你肯定能看出我认为我应该重视它。

==10570== Invalid write of size 8
==10570==    at 0x401602: ReadFileData(char*) (StandAlone.cpp:335)
==10570==    by 0x4013D8: CompileFile(char*, void*, int, TBuiltInResource const*) (StandAlone.cpp:255)
==10570==    by 0x401016: main (StandAlone.cpp:152)
==10570==  Address 0x5f627a0 is 0 bytes inside a block of size 6 alloc'd
==10570==    at 0x4C2880D: malloc (vg_replace_malloc.c:236)
==10570==    by 0x401475: ReadFileData(char*) (StandAlone.cpp:300)
==10570==    by 0x4013D8: CompileFile(char*, void*, int, TBuiltInResource const*) (StandAlone.cpp:255)
==10570==    by 0x401016: main (StandAlone.cpp:152)
==10570== 
==10570== Invalid read of size 8
==10570==    at 0x401624: ReadFileData(char*) (StandAlone.cpp:336)
==10570==    by 0x4013D8: CompileFile(char*, void*, int, TBuiltInResource const*) (StandAlone.cpp:255)
==10570==    by 0x401016: main (StandAlone.cpp:152)
==10570==  Address 0x5f627a0 is 0 bytes inside a block of size 6 alloc'd
==10570==    at 0x4C2880D: malloc (vg_replace_malloc.c:236)
==10570==    by 0x401475: ReadFileData(char*) (StandAlone.cpp:300)
==10570==    by 0x4013D8: CompileFile(char*, void*, int, TBuiltInResource const*) (StandAlone.cpp:255)
==10570==    by 0x401016: main (StandAlone.cpp:152)
==10570== 
==10570== Invalid read of size 8
==10570==    at 0x40163F: ReadFileData(char*) (StandAlone.cpp:337)
==10570==    by 0x4013D8: CompileFile(char*, void*, int, TBuiltInResource const*) (StandAlone.cpp:255)
==10570==    by 0x401016: main (StandAlone.cpp:152)
==10570==  Address 0x5f627a0 is 0 bytes inside a block of size 6 alloc'd
==10570==    at 0x4C2880D: malloc (vg_replace_malloc.c:236)
==10570==    by 0x401475: ReadFileData(char*) (StandAlone.cpp:300)
==10570==    by 0x4013D8: CompileFile(char*, void*, int, TBuiltInResource const*) (StandAlone.cpp:255)
==10570==    by 0x401016: main (StandAlone.cpp:152)

编辑:我需要的代码能在C++编译器中编译,这就是为什么我必须保留malloc所有的强制转换的原因。


也许可以跟踪变量 i 的值,看它们是否超过了5。 - Kerrek SB
您的意思是6,即MAX_SOURCE_STRINGS+1吗? - AzP
1个回答

21
这看起来不对:
char**return_data=(char**)malloc(MAX_SOURCE_STRINGS+1);

建议改为:

char **return_data = malloc ( (MAX_SOURCE_STRINGS+1) * sizeof *return_data );

编辑:进一步解释:

当你写return_data[i]=...时,你正在尝试将某些内容写入return_data[i]。现在,return_datachar**类型的,因此return_data[i]char*类型的。因此,你正在向内存中的某个位置写入一个指针。

看起来你的指针长为8个字节(这没问题),但你只分配了6个字节:MAX_SOURCE_STRING + 1。所以有问题了。

你尝试将其写入偏移量0并不重要——你仍然在尝试写入比缓冲区能够容纳更多的数据,这就是valgrind在抱怨什么的原因。

为了解决问题,你应该分配足够的空间来容纳指针数组。每个指针都需要sizeof(char*)大小的空间,也可以写成sizeof(*return_data)sizeof *return_data。因此,总共你应该分配n * sizeof *return_data字节,其中n是(在你的情况下)魔数6。


谢谢你的回答!我理解return_data是一个char*数组,也可以看作是char数组的数组。我认为malloc会分配6个char数组的数组。然后代码在335行处执行另一个malloc,在位置i(return_data[i])分配子数组。因此,我们可以通过return_data[i][0]等方式访问该数组。 - AzP
但就像你所说的那样,将MALLOC_SOURCE_STRINGS更改为8确实消除了投诉。我必须重新思考这个问题,直到我理解自己做错了什么,并且是否与你提供的信息相匹配 =) - AzP
啊,我现在明白了。你关于指针的说法是正确的,我原本认为我们需要6个指针,并且它们每个占用1个字节,但这显然不是事实。 - AzP
1
是的,你必须记住 malloc 不知道任何类型,它只知道字节数。因此,你必须自己计算字节数(正如你发现的那样,这与项目数不同)。 - Omri Barel
使用calloc(number, size)而不是malloc(total_size),可以更明显地考虑到内存大小的需求,此外还有对新请求的内存进行初始化的积极影响。 - alk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接