C语言fgets读取文件触发段错误:Ubuntu正常CentOS运行失败
故障原因
- 核心触发原因:
main函数中定义的字符指针char *c未分配内存空间、未初始化,属于野指针。调用fgets(c,20,fptr)时,fgets会尝试向c指向的非法内存地址写入读取到的文件内容,触发内存访问异常。
不同操作系统的栈内存初始值策略不同:Ubuntu环境下c的随机值可能恰好指向可写的内存区域,所以不会直接崩溃;CentOS环境下c的初始值为不可访问的地址,直接触发段错误。 - 其他潜在风险:
- 未检查
fopen返回值:如果/usr/bin/.mypass不存在、无读权限,fopen会返回NULL,后续传入fgets也会触发空指针解引用。 - 未检查
fgets返回值:如果文件为空、读取失败,fgets返回NULL,传入trimew函数会触发空指针解引用。 trimew函数使用静态固定长度缓冲区,无边界检查:如果传入的字符串长度超过98,会发生缓冲区溢出;且静态缓冲区不具备线程安全性。- 长度校验逻辑错误:拼接引号、文件内容时没有更新
len变量,后续参数长度校验的基准值错误,存在command缓冲区溢出风险。 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
相关产品推荐
相关产品推荐

