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

开发Shell时execvp权限拒绝与字符串拼接异常问题

问题根源及修复方案

核心问题1:野指针与栈内存失效

你的代码存在两个直接导致args[1]值突变的内存非法访问问题:

  • 空格分支提前释放内存:处理空格分割参数时,你为temp_arg分配堆内存并赋值给args[j],但紧接着就调用free(temp_arg)。这会让args[j]变成野指针,后续访问该内存时,内容已被系统回收或覆盖。
    char *temp_arg = malloc(k+1);
    // ...赋值操作
    args[j] = temp_arg;
    free(temp_arg); // 直接释放了args[j]指向的内存,后续访问非法
    
  • 换行分支栈局部变量失效:处理换行时,你用栈上的局部数组temp_arg存储参数,再将args[j]指向该数组。但栈局部变量在离开当前if块后就会被销毁,后续执行malloc等操作时,栈内存会被新数据覆盖,导致args[1]指向的内容变成随机值。
    char temp_arg[k+1]; // 栈上局部数组,出if块后失效
    args[j] = temp_arg; // 赋值后后续访问指向已失效的栈内存
    

核心问题2:路径拼接逻辑错误

你手动循环拼接路径的方式存在隐患:

  • 最后一行path[strlen(path)] = '\0';多余且危险,因为此时path未完全初始化,strlen(path)可能读取到非法内存。正确做法是直接用path[len] = '\0';,因为len已经是/bin/与命令的长度之和。

修复后的代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

#define MAX_CMD_BUFFER 256

void extern_com(char* buffer){
    int i = 0, j = 1, k = 0;
    pid_t pid;
    char *args[5];
    char temp[MAX_CMD_BUFFER];
    args[0] = "/bin/";
    args[4] = NULL;

    while(buffer[i] != '\0'){
        temp[k] = buffer[i];
        if(buffer[i] == '\n'){
            temp[k] = '\0';
            // 改用堆内存存储参数,避免栈内存失效
            char *temp_arg = malloc(strlen(temp) + 1);
            strcpy(temp_arg, temp);
            args[j] = temp_arg;
            break;
        }
        if(buffer[i] == ' '){
            temp[k] = '\0';
            char *temp_arg = malloc(strlen(temp) + 1);
            strcpy(temp_arg, temp);
            args[j] = temp_arg;
            j += 1;
            k = 0;
            i += 1;
            // 不提前释放内存,父进程在wait后统一释放
            continue;
        }
        k++;
        i++;
    }

    // 初始化剩余参数位为NULL
    for(; j < 4; j++){
        args[j] = NULL;
    }

    // 安全拼接路径:用strcpy+strcat简化逻辑
    char path[MAX_CMD_BUFFER];
    strcpy(path, args[0]);
    strcat(path, args[1]);

    printf("path: %s\n", path);
    pid = fork();
    if(pid == 0) {
        execvp(path, args);
        perror("exec");
        exit(EXIT_FAILURE); // exec失败后确保子进程退出
    } else {
        wait(NULL);
        // 父进程在子进程结束后释放堆内存
        for(int m = 1; m < 4; m++){
            if(args[m] != NULL){
                free(args[m]);
            }
        }
    }
}

额外优化建议

  • 用strtok函数分割命令行参数,比手动循环处理空格和换行更简洁可靠。
  • 路径拼接改用snprintf,避免栈数组溢出风险:snprintf(path, sizeof(path), "%s%s", args[0], args[1]);
  • 子进程在execvp失败后必须调用exit,防止子进程继续执行父进程代码。

内容的提问来源于stack exchange,提问作者shinny.dogma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 12:47:03