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

C语言fgets读取文件触发段错误:Ubuntu正常CentOS运行失败

故障原因
  • 核心触发原因:main函数中定义的字符指针char *c未分配内存空间、未初始化,属于野指针。调用fgets(c,20,fptr)时,fgets会尝试向c指向的非法内存地址写入读取到的文件内容,触发内存访问异常。
    不同操作系统的栈内存初始值策略不同:Ubuntu环境下c的随机值可能恰好指向可写的内存区域,所以不会直接崩溃;CentOS环境下c的初始值为不可访问的地址,直接触发段错误。
  • 其他潜在风险:
    1. 未检查fopen返回值:如果/usr/bin/.mypass不存在、无读权限,fopen会返回NULL,后续传入fgets也会触发空指针解引用。
    2. 未检查fgets返回值:如果文件为空、读取失败,fgets返回NULL,传入trimew函数会触发空指针解引用。
    3. trimew函数使用静态固定长度缓冲区,无边界检查:如果传入的字符串长度超过98,会发生缓冲区溢出;且静态缓冲区不具备线程安全性。
    4. 长度校验逻辑错误:拼接引号、文件内容时没有更新len变量,后续参数长度校验的基准值错误,存在command缓冲区溢出风险。
    5. trimew仅处理了前导空格,没有处理fgets读取内容末尾自带的换行符,会导致拼接的指令格式异常。
修复方案

对应问题逐一修复,修复后代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXC 1024
#define READ_BUF_SIZE 20

// 修改trim函数,支持去除前后空白字符,传入外部缓冲区避免静态变量问题
char* trim(char* str, char* out_buf, size_t out_buf_size) {
    if (!str || !out_buf || out_buf_size == 0) return NULL;
    // 跳过前导空白
    char* start = str;
    while (isspace((unsigned char)*start)) start++;
    if (*start == '\0') {
        out_buf[0] = '\0';
        return out_buf;
    }
    // 跳过尾部空白
    char* end = start + strlen(start) - 1;
    while (end > start && isspace((unsigned char)*end)) end--;
    size_t res_len = end - start + 1;
    // 边界检查,避免溢出
    if (res_len >= out_buf_size) res_len = out_buf_size - 1;
    strncpy(out_buf, start, res_len);
    out_buf[res_len] = '\0';
    return out_buf;
}

int main (int argc, char **argv) {
    FILE *fptr = fopen("/usr/bin/.mypass", "r");
    // 新增文件打开错误检查
    if (!fptr) {
        perror("打开/usr/bin/.mypass失败");
        return 1;
    }
   
    char command[MAXC] = "/usr/bin/foo LOAD=\"";
    size_t len = strlen(command);
    // 定义栈数组作为读取缓冲区,避免野指针
    char read_buf[READ_BUF_SIZE] = {0};
    char trimed_buf[READ_BUF_SIZE] = {0};
    // 检查fgets返回值
    if (!fgets(read_buf, READ_BUF_SIZE, fptr)) {
        perror("读取/usr/bin/.mypass失败");
        fclose(fptr);
        return 1;
    }
    fclose(fptr);
    // 修剪读取到的内容
    trim(read_buf, trimed_buf, sizeof(trimed_buf));
    size_t trim_len = strlen(trimed_buf);
    // 检查拼接后是否超过缓冲区大小
    if (len + trim_len + 1 >= MAXC) {
        fputs("error: 文件内容过长,超出命令存储上限\n", stderr);
        return 1;
    }
    strcat(command, trimed_buf);
    strcat(command, "\"");
    len = strlen(command);

    int i;
    for (i = 1; i < argc; i++) {
        size_t arglen = strlen(argv[i]);
        if (len + arglen + 2 > MAXC) {
            fputs ("error: 参数过长,超出命令存储上限\n", stderr);
            return 1;
        }
        strcat(command, " ");
        strcat(command, argv[i]);
        len += arglen + 1;
    }
    
    printf("system (%s)\n", command);
    // system(command);
    return 0;
}

主要修复点说明:

  • 将未初始化的字符指针c替换为栈上分配的固定大小数组read_buf,解决野指针问题。
  • 新增fopen、fgets返回值检查,避免空指针解引用。
  • 重构trim函数,支持去除前后空白(包括fgets读取的换行符),使用外部传入的缓冲区,避免静态缓冲区的溢出和线程安全问题,增加边界检查。
  • 修正长度校验逻辑,每次拼接后确保不会超出command缓冲区大小,避免溢出。

内容的提问来源于stack exchange,提问作者MonsterPiyush

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 02:15:01