访问静态字符串偶发段错误:与构建环境相关的问题排查
Criterion参数化测试Ubuntu编译后段错误问题
别急着走,我保证这不是未初始化指针的问题!
问题背景
我用Criterion编写了单元测试,问题出在测试代码本身,而非被测代码。简化后的测试代码如下:
#include <stdio.h> #include <criterion/criterion.h> #include <criterion/parameterized.h> typedef struct { char *input; } paramspec; TestSuite(Example); ParameterizedTestParameters(Example, test_example) { static paramspec params[] = { {"this is a test"}, }; size_t nb_params = sizeof (params) / sizeof (paramspec); return cr_make_param_array(paramspec, params, nb_params); } ParameterizedTest(paramspec *param, Example, test_example) { printf("input value is: %s\n", param->input); }
在Ubuntu 22.04容器中使用gcc-11(11.4.0)或gcc-10(10.5.0)编译后,运行测试触发SIGSEGV:
[====] Running 1 test from Example: [RUN ] Example::test_example [----] test_example.c:20: Unexpected signal caught below this line! [FAIL] Example::test_example: CRASH! [====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 1
通过gdb调试发现无法访问*param指向的内存。
关键差异
在Fedora 34(gcc 11.3.1)环境编译后,测试运行正常,且该编译产物在Ubuntu环境中也能正常运行,gdb可正常查看字符串值。
疑问
仅在Ubuntu环境编译时出现段错误,这只是访问同一文件中的静态字符串,无指针分配问题,请问构建环境的哪些因素导致了这个问题?Ubuntu下使用gcc-9/10/11编译均出现相同问题。
Sanitizer编译结果
使用-fsanitize=undefined,address编译后,报错信息指向内存读取错误,但仅在Ubuntu编译时出现该错误。
问题分析与解决建议
可能的构建环境差异因素
- Criterion库版本差异:Ubuntu 22.04仓库中的Criterion版本(如2.3.3-4)可能存在参数化测试的bug,而Fedora 34使用的是更新版本,修复了该问题。静态结构体中的字符串指针在旧版本的参数传递逻辑中可能未被正确处理,导致访问非法内存。
- 默认编译选项差异:Ubuntu的gcc默认开启PIE(位置无关执行),而Fedora的默认编译设置可能不同。PIE会改变程序内存布局,若Criterion的参数化测试逻辑未适配PIE环境,可能导致参数指针地址计算错误。
- 链接器选项差异:Ubuntu链接器默认启用的RELRO(重定位只读)、BIND_NOW等选项,可能影响静态数据区域的访问权限,导致参数结构体中的指针无法正常访问字符串常量。
解决办法
- 关闭PIE编译选项:编译时添加
-no-pie参数,禁用位置无关执行,测试是否能正常运行:gcc -o test test_example.c -lcriterion -no-pie - 升级Criterion库:从源码编译最新版本的Criterion(而非使用Ubuntu仓库版本),替换系统中的旧版本库,重新编译测试代码。
- 检查头文件与库一致性:确保编译时使用的Criterion头文件和链接的库文件版本一致,避免混用不同版本导致的兼容性问题。
内容的提问来源于stack exchange,提问作者larsks
相关产品推荐
相关产品推荐

