Skip to content

Commit 859bcf4

Browse files
committed
[analyzer][taint] Add isTainted debug expression inspection check
Summary: This patch introduces the `clang_analyzer_isTainted` expression inspection check for checking taint. Using this we could query the analyzer whether the expression used as the argument is tainted or not. This would be useful in tests, where we don't want to issue warning for all tainted expressions in a given file (like the `debug.TaintTest` would do) but only for certain expressions. Example usage: ```lang=c++ int read_integer() { int n; clang_analyzer_isTainted(n); // expected-warning{{NO}} scanf("%d", &n); clang_analyzer_isTainted(n); // expected-warning{{YES}} clang_analyzer_isTainted(n + 2); // expected-warning{{YES}} clang_analyzer_isTainted(n > 0); // expected-warning{{YES}} int next_tainted_value = n; // no-warning return n; } ``` Reviewers: NoQ, Szelethus, baloghadamsoftware, xazax.hun, boga95 Reviewed By: Szelethus Subscribers: martong, rnkovacs, whisperity, xazax.hun, baloghadamsoftware, szepet, a.sidorin, mikhail.ramalho, donat.nagy, Charusso, cfe-commits, boga95, dkrupp, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D74131
1 parent cdcce3c commit 859bcf4

File tree

3 files changed

+90
-20
lines changed

3 files changed

+90
-20
lines changed

clang/docs/analyzer/developer-docs/DebugChecks.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,28 @@ ExprInspection checks
275275

276276
See clang_analyzer_denote().
277277

278+
- ``void clang_analyzer_isTainted(a single argument of any type);``
279+
280+
Queries the analyzer whether the expression used as argument is tainted or not.
281+
This is useful in tests, where we don't want to issue warning for all tainted
282+
expressions but only check for certain expressions.
283+
This would help to reduce the *noise* that the `TaintTest` debug checker would
284+
introduce and let you focus on the `expected-warning`s that you really care
285+
about.
286+
287+
Example usage::
288+
289+
int read_integer() {
290+
int n;
291+
clang_analyzer_isTainted(n); // expected-warning{{NO}}
292+
scanf("%d", &n);
293+
clang_analyzer_isTainted(n); // expected-warning{{YES}}
294+
clang_analyzer_isTainted(n + 2); // expected-warning{{YES}}
295+
clang_analyzer_isTainted(n > 0); // expected-warning{{YES}}
296+
int next_tainted_value = n; // no-warning
297+
return n;
298+
}
299+
278300
Statistics
279301
==========
280302

clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "Taint.h"
910
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1011
#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
1112
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -46,6 +47,7 @@ class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols,
4647
void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const;
4748
void analyzerDenote(const CallExpr *CE, CheckerContext &C) const;
4849
void analyzerExpress(const CallExpr *CE, CheckerContext &C) const;
50+
void analyzerIsTainted(const CallExpr *CE, CheckerContext &C) const;
4951

5052
typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
5153
CheckerContext &C) const;
@@ -73,26 +75,34 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call,
7375

7476
// These checks should have no effect on the surrounding environment
7577
// (globals should not be invalidated, etc), hence the use of evalCall.
76-
FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
77-
.Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
78-
.Case("clang_analyzer_checkInlined",
79-
&ExprInspectionChecker::analyzerCheckInlined)
80-
.Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
81-
.Case("clang_analyzer_warnIfReached",
82-
&ExprInspectionChecker::analyzerWarnIfReached)
83-
.Case("clang_analyzer_warnOnDeadSymbol",
84-
&ExprInspectionChecker::analyzerWarnOnDeadSymbol)
85-
.StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
86-
.StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump)
87-
.Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
88-
.Case("clang_analyzer_printState",
89-
&ExprInspectionChecker::analyzerPrintState)
90-
.Case("clang_analyzer_numTimesReached",
91-
&ExprInspectionChecker::analyzerNumTimesReached)
92-
.Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump)
93-
.Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
94-
.Case("clang_analyzer_express", &ExprInspectionChecker::analyzerExpress)
95-
.Default(nullptr);
78+
FnCheck Handler =
79+
llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
80+
.Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
81+
.Case("clang_analyzer_checkInlined",
82+
&ExprInspectionChecker::analyzerCheckInlined)
83+
.Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
84+
.Case("clang_analyzer_warnIfReached",
85+
&ExprInspectionChecker::analyzerWarnIfReached)
86+
.Case("clang_analyzer_warnOnDeadSymbol",
87+
&ExprInspectionChecker::analyzerWarnOnDeadSymbol)
88+
.StartsWith("clang_analyzer_explain",
89+
&ExprInspectionChecker::analyzerExplain)
90+
.StartsWith("clang_analyzer_dump",
91+
&ExprInspectionChecker::analyzerDump)
92+
.Case("clang_analyzer_getExtent",
93+
&ExprInspectionChecker::analyzerGetExtent)
94+
.Case("clang_analyzer_printState",
95+
&ExprInspectionChecker::analyzerPrintState)
96+
.Case("clang_analyzer_numTimesReached",
97+
&ExprInspectionChecker::analyzerNumTimesReached)
98+
.Case("clang_analyzer_hashDump",
99+
&ExprInspectionChecker::analyzerHashDump)
100+
.Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
101+
.Case("clang_analyzer_express",
102+
&ExprInspectionChecker::analyzerExpress)
103+
.StartsWith("clang_analyzer_isTainted",
104+
&ExprInspectionChecker::analyzerIsTainted)
105+
.Default(nullptr);
96106

97107
if (!Handler)
98108
return false;
@@ -412,6 +422,17 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
412422
reportBug(*Str, C);
413423
}
414424

425+
void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE,
426+
CheckerContext &C) const {
427+
if (CE->getNumArgs() != 1) {
428+
reportBug("clang_analyzer_isTainted() requires exactly one argument", C);
429+
return;
430+
}
431+
const bool IsTainted =
432+
taint::isTainted(C.getState(), CE->getArg(0), C.getLocationContext());
433+
reportBug(IsTainted ? "YES" : "NO", C);
434+
}
435+
415436
void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
416437
Mgr.registerChecker<ExprInspectionChecker>();
417438
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang_analyze_cc1 -verify %s \
2+
// RUN: -analyzer-checker=core \
3+
// RUN: -analyzer-checker=debug.ExprInspection \
4+
// RUN: -analyzer-checker=alpha.security.taint
5+
6+
int scanf(const char *restrict format, ...);
7+
void clang_analyzer_isTainted(char);
8+
void clang_analyzer_isTainted_any_suffix(char);
9+
void clang_analyzer_isTainted_many_arguments(char, int, int);
10+
11+
void foo() {
12+
char buf[32] = "";
13+
clang_analyzer_isTainted(buf[0]); // expected-warning {{NO}}
14+
clang_analyzer_isTainted_any_suffix(buf[0]); // expected-warning {{NO}}
15+
scanf("%s", buf);
16+
clang_analyzer_isTainted(buf[0]); // expected-warning {{YES}}
17+
clang_analyzer_isTainted_any_suffix(buf[0]); // expected-warning {{YES}}
18+
19+
int tainted_value = buf[0]; // no-warning
20+
}
21+
22+
void exactly_one_argument_required() {
23+
char buf[32] = "";
24+
scanf("%s", buf);
25+
clang_analyzer_isTainted_many_arguments(buf[0], 42, 42);
26+
// expected-warning@-1 {{clang_analyzer_isTainted() requires exactly one argument}}
27+
}

0 commit comments

Comments
 (0)