Clang Static Analyzer - Warning Suppress
Clang Static Analyzer默認抑制三種類型的warning,當然對於warning的抑制會產生一定的漏報,但是在一定程度上會避免大量的誤報。
- 發生在在sink結點或者no-return函數之前leak類型的warning,例如發生在空指針解引用之前的memory leak,CSA面對這些warning保持靜默。
- Callee返回null,導致在Caller中產生的warning,一般是空指針解引用,CSA面對這些bug保持靜默。
- 庫文件源碼中產生的warning,CSA面對這些warning保持靜默。
1.對在sink結點和no-return函數前warning的抑制
該類型的warning大部分都是leak類型的bug,例如內存泄漏,文件未關閉等bug,如下代碼所示:
// suppress-on-sink.cppn#include <stdlib.h>nvoid get_mem()n{n int* ptr = (int*)malloc(sizeof(int));n *ptr = 0;n} // <--- memory leaknnint main()n{n get_mem();n int operand = 0;n int num = 10 / operand; // fatal error, sinkn return num;n}n
clang中給出的原因是leak類型的warning相比於sink結點,例如空指針解引用,除0等致命錯誤來說,優先順序比較低,另外進程佔用的資源,在程序崩潰以後也基本會被操作系統回收。只要warning被sink結點後支配(post-dominated),該warning都會被sink結點suppress。在實現checker的時候可以使用setSuppressOnSink()來設定bug是否可以被sink結點suppress。
no-return函數也被視作sink結點,比如說exit(1)函數就是non-return函數,這樣做也是為了能夠實現suppress-on-sink的效果。throw當前也有可能作為sink結點,這是由於當前csa對於exception沒有合適的建模,見D35674。
註:rL150312 [analyzer] Malloc checker: Leak bugs should be suppressed by sinks.
註:D28023 [analyzer] Fix leak false positives before no-return functions caused by incomplete analyses.
註:rL290341 [analyzer] Improve suppress-on-sink behavior in incomplete analyses.
註:rL162156 [analyzer] Treat @throw as a sink (stop processing).
註:D35674 [analyzer] Treat C++ throw as sink during CFG-based suppress-on-sink.
另外MallocChecker除了被sink結點suppress,還會被pointer-escape suppress,如果指向new或者free出來的內存塊的指針逃逸了是不會觸發memory leak報錯的。如果對於memory leak在sink結點上的suppress不滿意,可以修改MallocChecker.cpp。如果想讓自己的某些checker中bug type在特定場景下進行suppress,也可以使用setSuppressOnSink()將自己的BugType進行suppress-on-sink。
2.對函數返回null導致的warning的抑制
對於null-return-path上warning的抑制基本上是出於降低誤報率的考慮,但是這種做法不可避免的會帶有部分漏報。如下代碼示例所示,代碼中有一個Null Pointer Deference,但是這個warning被抑制了。
// suppress-non-return.cppn#include <stdlib.h>nint *getMem(int size) n{n if (size < 0)n return 0;n elsen return (int*)malloc(size * sizeof(int));n}nnint main()n{n // Definitely return 0.n int *ptr = getMem(-1); // suppress-non-returnn *ptr = 0;n return 0;n}n
rL164449 [analyzer] Suppress bugs whose paths go through the return of a null pointer 中詳細的介紹了這類suppress的原因以及相應的優缺點。這樣做的原因主要是為了降低誤報率,有很多代碼採用了防禦式編程(defensive programming),例如在callee中,開頭有類似於"if(parm == null) return null;"的檢查語句,這些語句有時候是無用的,但是引擎由於機制原因符號執行了這條語句,所以callee會返回null,從而產生空指針解引用的誤報。開發者可以使用"-analyzer-config suppress-non-return-paths=false"來對著這種suppress進行禁止。
其實anna在[Static Analyzer Query] Why is suppress-null-return-paths enabled by default?中也提到了這是一個rude false positive suppression heuristic,但是Clang Static Analyzer的首要目標始終是降低誤報率,所以只能採用了這條策略。
註:This is a heuristic intended to greatly reduce the number of false positives resulting from inlining, particularly inlining of generic, defensive C++ methods that live in header files. The suppression i triggered in the cases where we ask to track where a null pointer came from, and it turns out that the source of the null pointer was an inlined function call.
This change brings the number of bug reports in LLVM from ~1500 down to around ~300, a much more manageable number. Yes, some true positives may be hidden as well, but from what I looked at the vast majority of silenced reports are false positives, and many of the true issues found by the analyzer are still reported.
http://clang-developers.42468.n3.nabble.com/Static-Analyzer-Query-Why-is-suppress-null-return-paths-enabled-by-default-tp4034027p4034036.html
3.庫文件中warning的抑制
對於庫文件的suppress是csa從一開始的做法,csa不會對於庫文件中的方法進行分析,具體是在AnalysisConsumer::HandleCode()(HandleCode()是AM_Syntax和AM_Path分析的同一入口)方法中判斷函數的Analysis Mode,默認System Header的分析模式是AM_None。除非system header被inline產生warning,否則是不會產生warning的,這樣做一是避免了誤報,二是節省了大量的分析時間。但如果指定了-analyze-all的話,還是會對system header進行分析的。但是有一種情況是會將warning報出來的,也就是在inline system headers時生成warning。
這裡我貼一些關於庫文件warning抑制的討論,有可能有些廢話 :)。
(1)Clang: Suppress static analysis of system header files
By default, the analyzer does not analyze code in ANY headers, system or otherwise, with the idea that you dont want to see warnings in every file that includes the header. The exception to this rule is inlined functions that come from headers, and in that case you definitely do not want to ignore the header!
Yes, this could lead to the analyzer reporting issues that really are the headers fault, but as Anna said its just as likely that its the callers fault. Consider this hypothetical addition to string.h:
int isempty(const char *str) { return str[0] == 0; }
If you call this function with a null pointer, the analyzer will warn about it as a null dereference, but the problem is really the caller. And if you compiled and ran the program, it would indeed crash! So there is definitely value from warning here.
If you have an existing, concrete example (as in, you can attach the HTML file or an Xcode screenshot), then please file a bug at http://llvm.org/bugs/, but otherwise it seems difficult to have a meaningful discussion here.
Best,
Jordan
(2)Add correct "-isystem"/"-isysroot" warning handling to static analysis BugReporter.這裡有一段描述,我感覺更為準確一點。
The analyzer has two different kinds of diagnostics: AST-based and path-sensitive. AST-based diagnostics are similar to the diagnostics that clang performs in Sema in that they can usually be localized to a single program point. For AST-based checks, we follow clangs policy and never report a diagnostic in system headers.
In contrast, path-sensitive diagnostics describe errors that occur along a particular path through the program and so they involve multiple program points. Even when these paths *end* in system headers, they may start in application code and ultimately be the application codes fault. For example, if the application passes NULL to an inline function in a C++ header that then dereferences that pointer, we do want to emit a diagnostic even though the location of the diagnostic is in a system header. In this case the application programmer can do something about it: they should not pass NULL to the function.
By design the analyzer doesnt ever *start* a path-sensitive check in a header (either user or system) -- but if a path starts in an application source file and eventually calls into a header and does something bad, we do report it under the assumption that the application code is violating a header precondition.
This can lead to false positives when the analyzer doesnt understand the system headers properly. In those cases we have custom heuristics to suppress to known patterns the analyzer doesnt handle.
What specific diagnostics in headers are you seeing? Are these in libcxx? We know we have at least one issue in <regex> that isnt being properly suppressed. But if there are others wed love to hear about them.
Devin Coughlin給出的描述,我感覺應該更貼近clang static analyzer的設計。
感覺warning suppress沒有一定的標準,開發者可以按照自己的需求去抑制某些warning,例例如va_arg某些實現模型,就會觸發clang static analyzer的數組越界誤報,這個就需要用戶去做判斷然後進行抑制。
推薦閱讀:
※LLVM每日談之十一 編譯器相關學習資料推薦
※Clang Static Analyzer - BodyFarm
※誰說不能與龍一起跳舞:Clang / LLVM (2)
TAG:Clang |