fclose()在Docker容器中触发Segmentation Fault求助排查
问题:fclose()在Docker容器中触发段错误,本地运行正常
问题背景
这段C程序用于创建指定长度的空文件,在本地Ubuntu 20.04笔记本上运行正常,但在DigitalOcean的Ubuntu 22.04实例的Docker容器中执行时,fclose(outfile);行触发Segmentation Fault。文件已成功创建并最终关闭——关闭前大小显示为0,关闭后呈现指定的4GB长度,且已排除文件指针为空的常规触发情况。
GDB段错误详情
Program received signal SIGSEGV, Segmentation fault. 0x00007fe121321c70 in __GI__IO_setb (f=f@entry=0x145b210, b=b@entry=0x0, eb=eb@entry=0x0, a=a@entry=0) at ./libio/genops.c:338 338 ./libio/genops.c: No such file or directory.
相关代码
#include <stdio.h> #include <string.h> #include <errno.h> size_t create_empty_file (char * out_fname, size_t file_size) { FILE *outfile; char buf[1024]; outfile = fopen(out_fname, "wb+"); if (outfile == NULL) { strcpy(buf, strerror(errno)); printf("%d --> %s\n", errno, buf); } fseek(outfile, file_size, SEEK_SET); if (errno != 0) { strcpy(buf, strerror(errno)); printf("%d --> %s\n", errno, buf); } fputc('\0', outfile); if (errno != 0) { strcpy(buf, strerror(errno)); printf("%d --> %s\n", errno, buf); } fclose( outfile ); return 0; }
排查解决思路
- 修复错误检查逻辑:当前代码存在严重未定义行为隐患——如果
fopen失败,outfile是空指针,但后续仍会执行fseek、fputc、fclose,直接操作空指针必然触发异常。另外,依赖errno判断fseek、fputc的错误不可靠:errno不会自动清零,之前的操作可能残留错误值,且这些函数本身有明确返回值(fseek返回非0表示失败,fputc返回EOF表示失败),应优先用返回值判断。修改后的错误检查逻辑:outfile = fopen(out_fname, "wb+"); if (outfile == NULL) { snprintf(buf, sizeof(buf), "%d --> %s\n", errno, strerror(errno)); fputs(buf, stderr); return -1; // 失败直接返回,避免后续操作空指针 } if (fseek(outfile, file_size, SEEK_SET) != 0) { snprintf(buf, sizeof(buf), "%d --> %s\n", errno, strerror(errno)); fputs(buf, stderr); fclose(outfile); return -1; } if (fputc('\0', outfile) == EOF) { snprintf(buf, sizeof(buf), "%d --> %s\n", errno, strerror(errno)); fputs(buf, stderr); fclose(outfile); return -1; } - 替换
strcpy为snprintf:strcpy没有长度限制,若strerror返回的错误信息超过buf的1024字节长度,会触发缓冲区溢出,破坏栈上的FILE*指针或其他数据,最终在fclose时引发段错误。用snprintf可以严格控制写入长度,避免溢出。 - 检查Docker容器的文件系统限制:创建4GB文件时,需确认容器挂载目录的磁盘空间是否充足、是否有配额限制,同时检查文件系统类型是否支持大文件(比如部分老旧的overlay版本可能存在兼容性问题)。可在容器内执行
df -h查看磁盘使用情况,stat -f -c %T /path/to/target查看文件系统类型。 - 验证libc版本兼容性:Ubuntu 20.04(glibc 2.31)和22.04(glibc 2.35)的libc版本差异较大,Docker容器内的libc可能与宿主机存在兼容性问题。可尝试用静态编译方式构建程序(
gcc -static your_program.c -o your_program),避免依赖宿主机的动态libc库。 - 确认文件指针未被篡改:在
fclose前添加printf("outfile address: %p\n", outfile);,打印文件指针地址,确认它没有被之前的缓冲区溢出等操作篡改。
内容的提问来源于stack exchange,提问作者RTC222
相关产品推荐
相关产品推荐

