抓到了Splint的一只奇怪的bug
2014-05-08
先看一段还算正常的代码:
1 int (*f) ();
2 void g() {
3 int n = f();
4 }
5 int main() {return 0;}
这段代码虽然没干什么事情,但也没什么严重的问题,用gcc和clang都能无警告编译通过。
它定义了一个函数指针(*f) ()
。然后我们在函数g()
中调用了f()
,把返回值放进了int n
。
用Splint过一遍,一个警告,因为n
没有被使用。
然后我们要开始做一些邪恶的奇怪的事情了:
1 int (*(f)) ();
2 void g() {
3 int n = (f());;
4 }
5 int main() {return 0;}
加了两对括号和一个额外的分号(空语句)。我们再编译一下,编译器仍然一声不吭。
比较它们的编译结果,发现完全相同,截取g()
函数的编译结果:
1 pushq %rbp
2 movq %rsp, %rbp
3 subq $16, %rsp
4 movq f(%rip), %rdx
5 movl $0, %eax
6 call *%rdx
7 movl %eax, -4(%rbp)
8 leave
9 ret
但是Splint不干了:
1 Splint 3.1.2 --- 03 May 2009
2
3 constraintResolve.c:1517: at source point
4 test.c:4:2:
5 *** Internal Bug at constraintResolve.c:1517: llassert failed:
6 constraint_isDefined(c) [errno: 25]
7 *** Please report bug to submit@bugs.debian.org (via reportbug) ***
8 (attempting to continue, results may be incorrect)
9 constraint.c:1195: at source point
10 test.c:4:2:
11 *** Internal Bug at constraint.c:1195: llassert failed:
12 constraint_isDefined (c) [errno: 25]
13 *** Please report bug to submit@bugs.debian.org (via reportbug) ***
14 (attempting to continue, results may be incorrect)
15 *** Segmentation Violation
16 *** Location (not trusted): test.c:4:2
17 *** Last code point: transferChecks.c:4415
18 *** Previous code point: transferChecks.c:4002
19 *** Please report bug to submit@bugs.debian.org (via reportbug)
20 *** A useful bug report should include everything we need to reproduce the bug.
对代码再作一些调整,发现两对括号、一个分号必须都加上才会触发这个内部错误。
再尝试不同的修改,观察到Splint似乎把f
看作了一个二阶的指针,于是解一层引用,内部错误消失了:
1 int (*(f)) ();
2 void g() {
3 int n = ((*f)());;
4 }
5 int main() {return 0;}
是这样吗?
1 int (*(f)) ();
2 void g() {
3 int n = (*f());;
4 }
5 int main() {return 0;}
错误还在。
一时半会推敲不出,作罢。
考虑到C的语法、语义中有许多复杂的细节,各个编译器(以及linter)会采用不同的实现方式(甚至会出错,就像上面的Splint那样),以后得更加小心。