malloc后出现Valgrind「Invalid write of size 8」错误原因排查
Shell命令解析函数的Valgrind内存错误分析
我需要解析类Shell命令并将其存储到char**中,用于传递给execv系统调用。以下函数功能符合预期,但用Valgrind运行时出现了内存错误,请问错误原因是什么?
问题代码
char ** input_delimit(char *command){ // 19 char **retval; // 20 const char space[2]=" "; // 21 int i=0; // 22 retval=(char**)malloc(sizeof(char*)); // 23 retval[i]=(char*)malloc(sizeof(char)); // 24 retval[i]=strtok(command,space); // 25 while(retval[i]!=NULL){ // 26 i++; // 27 retval[i]=(char*)malloc(sizeof(char)); // 28 retval[i]=strtok(NULL,space); // 29 } // 30 return retval; // 31 } // 32
Valgrind错误信息
==824== Invalid write of size 8 ==824== at 0x109410: input_delimit (shell.c:28) ==824== by 0x109493: execute_command (shell.c:35) ==824== by 0x109600: main (shell.c:64) ==824== Address 0x4a489d8 is 0 bytes after a block of size 8 alloc'd ==824== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==824== by 0x1093A7: input_delimit (shell.c:23) ==824== by 0x109493: execute_command (shell.c:35) ==824== by 0x109600: main (shell.c:64) ==824== ==824== Invalid write of size 8 ==824== at 0x109439: input_delimit (shell.c:29) ==824== by 0x109493: execute_command (shell.c:35) ==824== by 0x109600: main (shell.c:64) ==824== Address 0x4a489d8 is 0 bytes after a block of size 8 alloc'd ==824== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==824== by 0x1093A7: input_delimit (shell.c:23) ==824== by 0x109493: execute_command (shell.c:35) ==824== by 0x109600: main (shell.c:64) ==824== ==824== Invalid read of size 8 ==824== at 0x109450: input_delimit (shell.c:26) ==824== by 0x109493: execute_command (shell.c:35) ==824== by 0x109600: main (shell.c:64) ==824== Address 0x4a489d8 is 0 bytes after a block of size 8 alloc'd ==824== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==824== by 0x1093A7: input_delimit (shell.c:23) ==824== by 0x109493: execute_command (shell.c:35) ==824== by 0x109600: main (shell.c:64)
错误原因分析
- 数组内存越界:第23行仅为
retval分配了1个char*的空间,但循环中每次i++后直接写入retval[i],超出了已分配的内存范围,导致Valgrind检测到无效写/读操作。 - 冗余内存分配导致泄漏:第24、28行的
malloc(sizeof(char))完全无用,因为strtok返回的是原字符串内的指针,后续赋值直接覆盖了malloc返回的地址,造成内存泄漏。 - 参数数组终止逻辑不严谨:
execv要求参数数组必须以NULL结尾,当前代码虽然最后会将retval[i]设为NULL,但这是通过越界写入实现的,属于未定义行为,只是侥幸能运行。
修复后的代码
char **input_delimit(char *command) { char **retval = NULL; const char *space = " "; int count = 0; char *token = strtok(command, space); // 先统计token数量 while (token != NULL) { count++; token = strtok(NULL, space); } // 分配足够空间(包含末尾的NULL) retval = malloc(sizeof(char*) * (count + 1)); if (retval == NULL) { return NULL; } // 重新分割字符串填充数组 token = strtok(command, space); for (int i = 0; i < count; i++) { retval[i] = token; token = strtok(NULL, space); } retval[count] = NULL; // 严格添加终止符 return retval; }
内容的提问来源于stack exchange,提问作者Giomell
相关产品推荐
相关产品推荐

