类Shell程序中&&||组合命令场景的内存泄漏修复求助
Shell程序
invalid_command && command1 || command2场景下的内存泄漏修复 问题场景
仅在invalid_command && command1 || command2场景下出现内存泄漏:当invalid_command执行失败时,会跳过command1执行command2,此时存在未释放的内存块,Valgrind检测到的泄漏字节数与invalid_command的长度相关(比如输入a && b || echo 3泄漏8字节,aa && b || echo 3泄漏9字节)。
相关函数代码
void execute_user_command(char *command, runData *data, int saved_stderr, const char *error_file) { char *copy = strdup(command); char *args[MAX_ARGS + 1]; // Array to store command arguments int arg_count; int background = 0; char *sep; int status = 0; char *check_quotes = NULL; // Check for || operator sep = strstr(copy, "||"); char *or_command = NULL; if (sep) { *sep = '\0'; sep += 2; check_quotes = strdup(copy); status = 2; while (*sep == ' ') sep++; // Skip spaces or_command = sep; } // Check for && operator sep = strstr(copy, "&&"); if (sep) { *sep = '\0'; sep += 2; check_quotes = strdup(copy); while (*sep == ' ') sep++; // Skip spaces split_command(copy, args, &arg_count); // If the first command is a special command, handle it if (handle_special_commands(copy, data) != -1) { // If the second command is also a special command, handle it if (handle_special_commands(sep, data) == -1) { // If the second command is not a special command, execute it execute_user_command(sep, data, saved_stderr, error_file); } } else { // If the first command is not a special command, execute it if (execute_command(args, copy, background, error_file, data)) { check_matching_quotes(check_quotes, data); // If the second command is a special command, handle it if (handle_special_commands(sep, data) == -1) { // If the second command is not a special command, execute it execute_user_command(sep, data, saved_stderr, error_file); } // If the first command wasn't special and resulted in an error, don't execute the second command and skip to the third } else if (or_command) { if (handle_special_commands(or_command, data) == -1) { execute_user_command(or_command, data, saved_stderr, error_file); } } } free(check_quotes); free(copy); return; } // Split the command into arguments split_command(copy, args, &arg_count); if (arg_count == 0) { free(copy); return; // Ignore empty commands } if (arg_count > MAX_ARGS) { // Command plus 4 arguments is allowed fprintf(stderr,"ERR: Too many arguments\n"); free(copy); return; // Skip commands with more than 4 arguments } if (arg_count > 0) { if (status == 1 || status == 0) { execute_command(args, command, background, error_file, data); } else { if (!execute_command(args, copy, background, error_file, data) && handle_special_commands(or_command, data) == -1) { execute_user_command(or_command, data, saved_stderr, error_file); } else { check_matching_quotes(check_quotes, data); } } } free(check_quotes); free(copy); }
Valgrind检测结果
测试输入与对应泄漏情况:
- 输入:
a && b || echo 3definitely lost: 8 bytes in 1 blocks
- 输入:
aa && b || echo 3definitely lost: 9 bytes in 1 blocks
- 输入:
aa && bb || echo 3definitely lost: 10 bytes in 1 blocks
- 输入:
aa && bb || echo 33definitely lost: 10 bytes in 1 blocks
Valgrind调用栈:
==8988== (A Number Depends on input as shown above) bytes in 1 blocks are definitely lost in loss record 1 of 1 ==8988== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==8988== by 0x491558E: strdup (strdup.c:42) ==8988== by 0x10AEE9: execute_user_command (in /home/student/CLionProjects/ShellLike) ==8988== by 0x10A0A7: process_input (in /home/student/CLionProjects/ShellLike/main) ==8988== by 0x10B512: main (in /home/student/CLionProjects/ShellLike1/main)
问题排查
泄漏的内存来自strdup调用,核心原因:
- 函数开头检查
||运算符时,会通过strdup(copy)给check_quotes分配内存; - 接着检查
&&运算符时,直接执行check_quotes = strdup(copy)覆盖了之前的指针,导致第一次strdup的内存块丢失,没有被释放; - 当
invalid_command执行失败进入else if (or_command)分支时,后续只释放了第二次strdup的check_quotes,第一次分配的内存彻底无法追踪,造成泄漏。
修复方案
在&&检测分支中,给check_quotes重新分配内存前,先检查它是否已经被||检测分配过,如果是则先释放旧内存:
修改&&检测部分的代码:
// Check for && operator sep = strstr(copy, "&&"); if (sep) { *sep = '\0'; sep += 2; // 修复:先释放之前||检测分配的check_quotes内存 if (check_quotes != NULL) { free(check_quotes); } check_quotes = strdup(copy); while (*sep == ' ') sep++; // Skip spaces // 后续代码保持不变... }
该修复直接解决了指针覆盖导致的内存泄漏问题,无需调整其他逻辑。
内容的提问来源于stack exchange,提问作者Johnnie
相关产品推荐
相关产品推荐

