【问题标题】:Valgrind complains with "Invalid write of size 8"Valgrind 抱怨“大小为 8 的无效写入”
【发布时间】:2011-10-05 22:29:05
【问题描述】:

我正在从事一个小型爱好项目 (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。
  • 你的意思是 6,如 MAX_SOURCE_STRINGS+1?

标签: c debugging memory-management valgrind


【解决方案1】:

这看起来不对:

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 个字符数组组成的数组。然后代码在第 335 行执行另一个 malloc,它在位置 i (return_data[i]) 处分配子数组。因此,我们可以通过 return_data[i][0] 等来访问该数组。
  • 但就像你说的,将 MALLOC_SOURCE_STRINGS 更改为 8 确实可以消除投诉。我必须重新考虑这一点,直到我明白我做错了什么,如果它与你的信息相匹配 =)
  • 啊,我想我现在明白了。关于指针,您是对的,我认为我们需要 6 个指针,并且每个指针占用 1 个字节,当然不是这样。
  • 是的,你必须记住malloc 对类型一无所知,它只知道字节。因此,您必须自己计算字节数(正如您所发现的,这与项目数不同)。
  • 使用 calloc(number, size) 而不是 malloc(total_size) 使得考虑大小的需要更加明显...... - 除了对新请求的内存进行初始化的积极影响之外。
猜你喜欢
  • 2021-12-15
  • 2016-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 2017-06-29
相关资源
最近更新 更多