等等,回来,我保证这不是关于未初始化的指针!
问题
我用Criterion编写了一些单元测试.测试中的代码并不重要;问题突然出现在in the test itself处.以下是这项测试的简化版本:
#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)编译时,运行此测试会产生:
[====] 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
它没有在输出中显示,但这是一个SIGSEGV.如果我使用gdb附加到测试并打印值*param
,我会看到error: Cannot access memory at address ...
:
Thread 1 "test_example" hit Breakpoint 1, Example_test_example_impl (param=0x7ffff7fa1330)
at test_example.c:21
21 printf("input value is: %s\n", param->input);
(gdb) p *param
$1 = {input = 0x55abdb8e81ec <error: Cannot access memory at address 0x55abdb8e81ec>}
但!
谜团变得更浓了
如果我在Fedora 34下构建代码(我之所以 Select Fedora 34,是因为它包括GCC 11.3.1,它与11.4.0非常匹配),代码可以很好地工作:
[====] Running 1 test from Example:
[RUN ] Example::test_example
input value is: this is a test
[PASS] Example::test_example: (0.00s)
[====] Synthesis: Tested: 1 | Passing: 1 | Failing: 0 | Crashing: 0
该代码不仅在构建它的Fedora环境中运行良好--它also在Ubuntu环境中运行时没有错误!
在这两种环境中,gdb都能够看到字符串值:
(gdb) p *param
$1 = {input = 0x41aac9 "this is a test"}
问题是
build环境的哪个方面导致了段错误?这只是同一文件中的代码正在访问的静态字符串;没有指针分配会出错或诸如此类的事情.
在Ubuntu端,我和GCC一起构建了这个-{9,10,11},所有情况下的行为都是相同的.
使用消毒剂的建筑
构建包含-fsanitize=undefined,address
个代码的代码会产生以下结果:
==16==ERROR: AddressSanitizer: SEGV on unknown address 0x5594e31d7400 (pc 0x7f1491d4e086 bp 0x7ffdfe1765b0 sp 0x7ffdfe175cf8 T0)
==16==The signal is caused by a READ memory access.
#0 0x7f1491d4e086 in __sanitizer::internal_strlen(char const*) ../../../../src/libsanitizer/sanitizer_common/sanitizer_libc.cpp:167
#1 0x7f1491cdf2ed in printf_common ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc:551
#2 0x7f1491cdf6cc in __interceptor_vprintf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1660
#3 0x7f1491cdf7c6 in __interceptor_printf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1718
#4 0x555fd432c365 in Example_test_example_impl /src/test_example.c:21
#5 0x7f1491c18298 in criterion_internal_test_main ../src/core/test.c:97
#6 0x555fd432c2e7 in Example_test_example_jmp /src/test_example.c:20
#7 0x7f1491c16849 in run_test_child ../src/core/runner_coroutine.c:230
#8 0x7f1491c28a92 in bxfi_main ../subprojects/boxfort/src/sandbox.c:57
#9 0x7f14913ced8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#10 0x7f14913cee3f in __libc_start_main_impl ../csu/libc-start.c:392
#11 0x555fd432c1a4 in _start (/src/test_example+0x21a4)
AddressSanitizer can not provide additional info.
...但这几乎就是gdb
早些时候告诉我们的.只有在Ubuntu上构建时才会出现这个错误;我在Fedora构建上没有看到类似的错误.
一个完整的复制者
如果任何人有兴趣仔细看看,我已经整理了一个完整的复制器here,其中包括测试代码、Makefile、Dockerfile和自述文件.