You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 15:37:05