Tom*_*ter 6 clang-static-analyzer
我想在所有方面匹配函数的特定参数可以为null。现在我正在使用
hasArgument(
3,
anyOf(
cxxNullPtrLiteralExpr()
,integerLiteral() // Technically this would alert on a constant pointer; but that's madness
)
)
Run Code Online (Sandbox Code Playgroud)
但是,这与以下代码不匹配:
void* nullObj = nullptr;
function(nullptr, false, false, nullObj);
Run Code Online (Sandbox Code Playgroud)
是否可以/容易地跟踪并匹配它?现在,我有一个非常简单的匹配器,但我想这种分析需要更多的逻辑吗?
您不能只“匹配”值为 NULL 的表达式。AST 匹配只能检查参数的语法,因此如果参数不是文字,您不知道它是否可能为 NULL。
相反,您需要使用流敏感检查器来查询 Clang SA 约束引擎。约束引擎跟踪流经程序的值。
这种检查器的核心如下所示:
bool NullArgChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
ProgramStateRef state = C.getState();
auto SVal = C.getSVal(CE->getArg(0)).getAs<DefinedOrUnknownSVal>();
if (SVal) {
ConditionTruthVal Nullness = state->isNull(*SVal);
if (Nullness.isConstrainedTrue()) {
Run Code Online (Sandbox Code Playgroud)
给定一个调用表达式CE,我们获取它的第一个参数,然后查询与第一个参数关联的CheckerContext符号值。SVal然后我们询问该值是否已知为 NULL。
下面是一个完整的示例检查器,每次看到已知为 NULL 的值作为任何函数的第一个参数传递时都会报告警告。
NullArgChecker.cpp:
// NullArgChecker.cpp
// /sf/ask/4036576841/
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class NullArgChecker : public Checker< eval::Call > {
mutable std::unique_ptr<BuiltinBug> BT_nullarg;
public:
NullArgChecker() {}
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
};
} // end anonymous namespace
bool NullArgChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
ProgramStateRef state = C.getState();
auto SVal = C.getSVal(CE->getArg(0)).getAs<DefinedOrUnknownSVal>();
if (SVal) {
// This is the core of this example checker: we query the constraint
// engine to see if the symbolic value associated with the first
// argument is known to be NULL along the current path.
ConditionTruthVal Nullness = state->isNull(*SVal);
if (Nullness.isConstrainedTrue()) {
// Create a warning for this condition.
ExplodedNode *N = C.generateErrorNode();
if (N) {
if (!BT_nullarg) {
BT_nullarg.reset(new BuiltinBug(
this, "Null Argument", "The first argument is NULL."));
}
C.emitReport(llvm::make_unique<BugReport>(
*BT_nullarg, BT_nullarg->getDescription(), N));
}
}
}
return false;
}
void ento::registerNullArgChecker(CheckerManager &mgr) {
mgr.registerChecker<NullArgChecker>();
}
bool ento::shouldRegisterNullArgChecker(const LangOptions &LO) {
return true;
}
Run Code Online (Sandbox Code Playgroud)
更改其他文件以将其挂钩:
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -148,6 +148,10 @@ def NonnullGlobalConstantsChecker: Checker<"NonnilStringCon
stants">,
let ParentPackage = CoreAlpha in {
+def NullArgChecker : Checker<"NullArg">,
+ HelpText<"Check for passing a NULL argument">,
+ Documentation<NotDocumented>;
+
def BoolAssignmentChecker : Checker<"BoolAssignment">,
HelpText<"Warn about assigning non-{0,1} values to Boolean variables">,
Documentation<HasAlphaDocumentation>;
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -62,6 +62,7 @@ add_clang_library(clangStaticAnalyzerCheckers
NonNullParamChecker.cpp
NonnullGlobalConstantsChecker.cpp
NullabilityChecker.cpp
+ NullArgChecker.cpp
NumberObjectConversionChecker.cpp
ObjCAtSyncChecker.cpp
ObjCAutoreleaseWriteChecker.cpp
Run Code Online (Sandbox Code Playgroud)
用于测试的示例输入:
// nullargpp.cpp
// Testing NullArg checker with C++.
#include <stddef.h> // NULL
void somefunc(int*);
void nullarg1()
{
somefunc(NULL); // reported
somefunc(0); // reported
somefunc(nullptr); // reported
}
void nullarg2()
{
int *p = 0;
somefunc(p); // reported
}
void nullarg3(int *p)
{
if (p) {
somefunc(p); // not reported
}
else {
somefunc(p); // reported
}
}
void not_nullarg(int *p)
{
somefunc(p); // not reported
}
Run Code Online (Sandbox Code Playgroud)
运行示例:
$ g++ -std=c++11 -E -o nullargpp.ii nullargpp.cpp
$ ~/bld/llvm-project/build/bin/clang -cc1 -analyze -analyzer-checker=alpha.core.NullArg nullargpp.ii
nullargpp.cpp:10:3: warning: The first argument is NULL
somefunc(
^~~~~~~~~
nullargpp.cpp:11:3: warning: The first argument is NULL
somefunc(0);
^~~~~~~~~~~
nullargpp.cpp:12:3: warning: The first argument is NULL
somefunc(nullptr);
^~~~~~~~~~~~~~~~~
nullargpp.cpp:18:3: warning: The first argument is NULL
somefunc(p);
^~~~~~~~~~~
nullargpp.cpp:27:5: warning: The first argument is NULL
somefunc(p);
^~~~~~~~~~~
5 warnings generated.
Run Code Online (Sandbox Code Playgroud)
为了获得最大的特异性,上述更改是对 llvm-project 提交 05efe0fdc4(2019 年 3 月)进行的,在 Linux 上运行,但应该适用于任何 Clang v9。