C Shell程序重复执行命令时execve报Bad address错误求助
C Shell二次执行命令报错"Bad address"的问题排查与修复
问题描述
开发的C Shell程序首次执行命令(如ls)正常,但同一会话中第二次执行命令时,execve报错Error in execution: Bad address。
核心错误原因
1. strtok直接破坏PATH环境变量
getcommand函数中,strtok(path, ":")直接操作getenv("PATH")返回的指针——getenv返回的是环境变量的原始存储地址,strtok会在分割位置插入\0,第一次调用后PATH的结构被永久破坏,后续调用getenv拿到的是被截断的无效路径,导致无法找到命令,甚至产生无效内存地址。
2. argv内存分配越界
统计命令参数时,初始执行argv = malloc(sizeof(char *) * (count + 1));时count为0,仅分配1个指针空间,但后续循环中count不断增加,写入argv[count]会越界访问内存,造成堆内存损坏,第二次执行时触发地址错误。
3. command内存重复分配
getline函数会自动为command分配或扩容内存,但每次循环先执行command = malloc(sizeof(char) * n);,初始n=0时malloc(0)属于未定义行为,且getline会重新分配内存,导致原malloc的内存泄漏,还可能干扰getline的正常工作。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <sys/stat.h> extern char **environ; char *getcommand(char *command) { // 复制PATH到本地缓冲区,避免破坏原环境变量 char *path = getenv("PATH"); if (!path) return NULL; char *path_copy = strdup(path); if (!path_copy) { perror("strdup"); return NULL; } char *token; char *execution; struct stat st; token = strtok(path_copy, ":"); while (token) { execution = malloc(strlen(token) + strlen(command) + 2); if (!execution) { perror("malloc"); free(path_copy); return NULL; } strcpy(execution, token); strcat(execution, "/"); strcat(execution, command); if (stat(execution, &st) == 0) { free(path_copy); return execution; } free(execution); token = strtok(NULL, ":"); } free(path_copy); return NULL; } int main() { char *prompt = "$ "; ssize_t toread; char *command = NULL; // 初始化为NULL,交给getline分配 size_t n = 0; const char *delim = " "; char *token; int count = 0; char **argv; pid_t pid; int status; char *execution; int i = 0; while (1) { write(1, prompt, strlen(prompt)); // 移除手动malloc,直接用getline分配command toread = getline(&command, &n, stdin); if (toread == -1) { printf("\n"); free(command); exit(1); } if (toread > 0 && command[toread - 1] == '\n') { command[toread - 1] = '\0'; toread--; } token = strtok(command, delim); count = 0; // 先统计token数量,再分配足够的内存 char *temp_token = token; while (temp_token != NULL) { count++; temp_token = strtok(NULL, delim); } argv = malloc(sizeof(char *) * (count + 1)); if (!argv) { perror("malloc argv"); free(command); exit(1); } // 重新分割token填充argv token = strtok(command, delim); count = 0; while (token != NULL) { argv[count] = strdup(token); token = strtok(NULL, delim); count++; } argv[count] = NULL; execution = getcommand(argv[0]); if (!execution) { fprintf(stderr, "Command not found: %s\n", argv[0]); // 释放资源后继续循环 for (i = 0; i < count; i++) free(argv[i]); free(argv); free(command); command = NULL; n = 0; continue; } pid = fork(); if (pid == -1) { perror("Fork Failed"); free(execution); for (i = 0; i < count; i++) free(argv[i]); free(argv); free(command); exit(1); } if (pid == 0) { if (execve(execution, argv, environ) == -1) { perror("Error in execution"); free(execution); for (i = 0; i < count; i++) free(argv[i]); free(argv); free(command); exit(1); } } else { wait(&status); } // 清理资源 for (i = 0; i < count; i++) { free(argv[i]); } free(execution); free(command); free(argv); // 重置command和n,为下一次getline做准备 command = NULL; n = 0; count = 0; } return 0; }
关键修复点说明
- 保护PATH环境变量:用
strdup复制PATH到本地缓冲区,再用strtok分割,避免修改原始环境变量。 - 正确分配argv内存:先遍历统计token数量,再分配对应大小的内存,避免越界写入。
- 移除command重复分配:将
command初始化为NULL,完全由getline负责内存管理,避免内存泄漏和未定义行为。 - 完善错误处理:在命令未找到、内存分配失败等场景下,及时释放已分配的资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Imane
相关产品推荐
相关产品推荐

