Skip to content

Commit 3236f7d

Browse files
committed
Taint source is correctly shown
The alpha.security.taint.TaintPropagation checker indicated incorrectly the origin of the source if the taintedness propagated through multiple variables. This is fixed now.
1 parent 15b9080 commit 3236f7d

18 files changed

+493
-253
lines changed

clang/include/clang/StaticAnalyzer/Checkers/Taint.h

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H
1414
#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H
1515

16+
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
1617
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
18+
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
1719
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
1820

1921
namespace clang {
@@ -23,27 +25,48 @@ namespace taint {
2325
/// The type of taint, which helps to differentiate between different types of
2426
/// taint.
2527
using TaintTagType = unsigned;
28+
using TaintFlowType = int;
2629

2730
static constexpr TaintTagType TaintTagGeneric = 0;
31+
static constexpr TaintFlowType UninitializedFlow = -1;
32+
33+
/// Used to store the taint flow related information
34+
/// in the program state.
35+
struct TaintData {
36+
TaintTagType Type; // Type of the taintedness (e.g. sql escape checked)
37+
TaintFlowType Flow; // Unique identifier of the taint flow
38+
TaintData() : Type(TaintTagGeneric), Flow(UninitializedFlow){};
39+
TaintData(TaintFlowType f) : Type(TaintTagGeneric), Flow(f){};
40+
TaintData(TaintTagType t, TaintFlowType f) : Type(t), Flow(f){};
41+
bool operator==(const TaintData &X) const { return Flow == X.Flow; }
42+
int operator<(const TaintData &X) const { return Flow < X.Flow; }
43+
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Flow); }
44+
};
45+
46+
static inline raw_ostream &operator<<(llvm::raw_ostream &Out,
47+
const clang::ento::taint::TaintData t) {
48+
Out << "Type:" << t.Type << " Flow:" << t.Flow << "\n";
49+
return Out;
50+
}
2851

2952
/// Create a new state in which the value of the statement is marked as tainted.
3053
[[nodiscard]] ProgramStateRef addTaint(ProgramStateRef State, const Stmt *S,
3154
const LocationContext *LCtx,
32-
TaintTagType Kind = TaintTagGeneric);
55+
TaintData Data = TaintData());
3356

3457
/// Create a new state in which the value is marked as tainted.
3558
[[nodiscard]] ProgramStateRef addTaint(ProgramStateRef State, SVal V,
36-
TaintTagType Kind = TaintTagGeneric);
59+
TaintData Data = TaintData());
3760

3861
/// Create a new state in which the symbol is marked as tainted.
3962
[[nodiscard]] ProgramStateRef addTaint(ProgramStateRef State, SymbolRef Sym,
40-
TaintTagType Kind = TaintTagGeneric);
63+
TaintData Data = TaintData());
4164

4265
/// Create a new state in which the pointer represented by the region
4366
/// is marked as tainted.
4467
[[nodiscard]] ProgramStateRef addTaint(ProgramStateRef State,
4568
const MemRegion *R,
46-
TaintTagType Kind = TaintTagGeneric);
69+
TaintData Data = TaintData());
4770

4871
[[nodiscard]] ProgramStateRef removeTaint(ProgramStateRef State, SVal V);
4972

@@ -56,10 +79,38 @@ static constexpr TaintTagType TaintTagGeneric = 0;
5679
/// This might be necessary when referring to regions that can not have an
5780
/// individual symbol, e.g. if they are represented by the default binding of
5881
/// a LazyCompoundVal.
59-
[[nodiscard]] ProgramStateRef
60-
addPartialTaint(ProgramStateRef State, SymbolRef ParentSym,
61-
const SubRegion *SubRegion,
62-
TaintTagType Kind = TaintTagGeneric);
82+
[[nodiscard]] ProgramStateRef addPartialTaint(ProgramStateRef State,
83+
SymbolRef ParentSym,
84+
const SubRegion *SubRegion,
85+
TaintData = TaintData());
86+
87+
std::optional<TaintData> getTaintData(ProgramStateRef State, const Stmt *S,
88+
const LocationContext *LCtx);
89+
90+
/// Get the taint data for a value if tainted in the given state.
91+
std::optional<TaintData> getTaintData(ProgramStateRef State, SVal V);
92+
93+
/// Get the taint data for symbol if tainted in the given state.
94+
std::optional<TaintData> getTaintData(ProgramStateRef State, SymbolRef Sym);
95+
96+
/// Get the Taint data for a pointer represented by the region
97+
// if tainted in the given state.
98+
std::optional<TaintData> getTaintData(ProgramStateRef State, const MemRegion *Reg);
99+
100+
/// Check if the region the expression evaluates to is the standard input,
101+
/// and thus, is tainted.
102+
bool isStdin(SVal Val, const ASTContext &ACtx);
103+
104+
/// Returns the TaintData for a pointed value (if tainted), otherwise
105+
/// the TaintData of the pointer itself.
106+
std::optional<TaintData> getTaintDataPointeeOrPointer(const CheckerContext &C,
107+
SVal Arg);
108+
std::optional<TaintData> getTaintDataPointeeOrPointer(const Expr *E,
109+
const ProgramStateRef &State,
110+
CheckerContext &C);
111+
112+
SVal getPointeeOf(const CheckerContext &C, Loc LValue);
113+
std::optional<SVal> getPointeeOf(const CheckerContext &C, SVal Arg);
63114

64115
/// Check if the statement has a tainted value in the given state.
65116
bool isTainted(ProgramStateRef State, const Stmt *S,
@@ -84,19 +135,19 @@ void printTaint(ProgramStateRef State, raw_ostream &Out, const char *nl = "\n",
84135

85136
LLVM_DUMP_METHOD void dumpTaint(ProgramStateRef State);
86137

87-
/// The bug visitor prints a diagnostic message at the location where a given
88-
/// variable was tainted.
89-
class TaintBugVisitor final : public BugReporterVisitor {
90-
private:
91-
const SVal V;
92-
138+
/// Always use TaintBugReport to give taint related warnings in any checker.
139+
/// The TaintData member carries the flow associated data of the tainted SVal
140+
class TaintBugReport : public clang::ento::PathSensitiveBugReport {
93141
public:
94-
TaintBugVisitor(const SVal V) : V(V) {}
95-
void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); }
96-
97-
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
98-
BugReporterContext &BRC,
99-
PathSensitiveBugReport &BR) override;
142+
TaintBugReport(const BugType &bt, StringRef desc,
143+
const ExplodedNode *errorNode, TaintData T)
144+
: clang::ento::PathSensitiveBugReport(bt, desc, errorNode,
145+
Kind::TaintPathSensitive),
146+
taint_data(T){};
147+
static bool classof(const BugReport *R) {
148+
return R->getKind() == Kind::TaintPathSensitive;
149+
}
150+
TaintData taint_data;
100151
};
101152

102153
} // namespace taint

clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class StackHintGeneratorForSymbol : public StackHintGenerator {
118118
/// individual bug reports.
119119
class BugReport {
120120
public:
121-
enum class Kind { Basic, PathSensitive };
121+
enum class Kind { Basic, PathSensitive, TaintPathSensitive };
122122

123123
protected:
124124
friend class BugReportEquivClass;
@@ -367,14 +367,16 @@ class PathSensitiveBugReport : public BugReport {
367367

368368
public:
369369
PathSensitiveBugReport(const BugType &bt, StringRef desc,
370-
const ExplodedNode *errorNode)
371-
: PathSensitiveBugReport(bt, desc, desc, errorNode) {}
370+
const ExplodedNode *errorNode,
371+
BugReport::Kind kind = Kind::PathSensitive)
372+
: PathSensitiveBugReport(bt, desc, desc, errorNode, kind) {}
372373

373374
PathSensitiveBugReport(const BugType &bt, StringRef shortDesc, StringRef desc,
374-
const ExplodedNode *errorNode)
375+
const ExplodedNode *errorNode,
376+
BugReport::Kind kind = Kind::PathSensitive)
375377
: PathSensitiveBugReport(bt, shortDesc, desc, errorNode,
376378
/*LocationToUnique*/ {},
377-
/*DeclToUnique*/ nullptr) {}
379+
/*DeclToUnique*/ nullptr, kind) {}
378380

379381
/// Create a PathSensitiveBugReport with a custom uniqueing location.
380382
///
@@ -386,17 +388,20 @@ class PathSensitiveBugReport : public BugReport {
386388
PathSensitiveBugReport(const BugType &bt, StringRef desc,
387389
const ExplodedNode *errorNode,
388390
PathDiagnosticLocation LocationToUnique,
389-
const Decl *DeclToUnique)
391+
const Decl *DeclToUnique,
392+
BugReport::Kind kind = Kind::PathSensitive)
390393
: PathSensitiveBugReport(bt, desc, desc, errorNode, LocationToUnique,
391-
DeclToUnique) {}
394+
DeclToUnique, kind) {}
392395

393396
PathSensitiveBugReport(const BugType &bt, StringRef shortDesc, StringRef desc,
394397
const ExplodedNode *errorNode,
395398
PathDiagnosticLocation LocationToUnique,
396-
const Decl *DeclToUnique);
399+
const Decl *DeclToUnique,
400+
BugReport::Kind kind = Kind::PathSensitive);
397401

398402
static bool classof(const BugReport *R) {
399-
return R->getKind() == Kind::PathSensitive;
403+
return R->getKind() >= Kind::PathSensitive &&
404+
R->getKind() <= Kind::TaintPathSensitive;
400405
}
401406

402407
const ExplodedNode *getErrorNode() const { return ErrorNode; }

clang/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ extern const char *const CXXObjectLifecycle;
2222
extern const char *const CXXMoveSemantics;
2323
extern const char *const SecurityError;
2424
extern const char *const UnusedCode;
25+
extern const char *const TaintedData;
2526
} // namespace categories
2627
} // namespace ento
2728
} // namespace clang

clang/include/clang/StaticAnalyzer/Core/BugReporter/TaintBugType.h

Whitespace-only changes.

clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ using namespace taint;
3232
namespace {
3333
class ArrayBoundCheckerV2 :
3434
public Checker<check::Location> {
35-
mutable std::unique_ptr<BuiltinBug> BT;
35+
mutable std::unique_ptr<BugType> BT;
3636

3737
enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
3838

3939
void reportOOB(CheckerContext &C, ProgramStateRef errorState, OOB_Kind kind,
40-
std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
40+
SVal &Offset) const;
4141

4242
public:
4343
void checkLocation(SVal l, bool isLoad, const Stmt*S,
@@ -166,7 +166,8 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
166166

167167
// Are we constrained enough to definitely precede the lower bound?
168168
if (state_precedesLowerBound && !state_withinLowerBound) {
169-
reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
169+
reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes,
170+
rawOffsetVal);
170171
return;
171172
}
172173

@@ -208,14 +209,15 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
208209
SVal ByteOffset = rawOffset.getByteOffset();
209210
if (isTainted(state, ByteOffset)) {
210211
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted,
211-
std::make_unique<TaintBugVisitor>(ByteOffset));
212+
rawOffsetVal);
212213
return;
213214
}
214215
} else if (state_exceedsUpperBound) {
215216
// If we are constrained enough to definitely exceed the upper bound,
216217
// report.
217218
assert(!state_withinUpperBound);
218-
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
219+
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes,
220+
rawOffsetVal);
219221
return;
220222
}
221223

@@ -227,16 +229,22 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
227229
checkerContext.addTransition(state);
228230
}
229231

230-
void ArrayBoundCheckerV2::reportOOB(
231-
CheckerContext &checkerContext, ProgramStateRef errorState, OOB_Kind kind,
232-
std::unique_ptr<BugReporterVisitor> Visitor) const {
232+
void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
233+
ProgramStateRef errorState, OOB_Kind kind,
234+
SVal &Offset) const {
233235

234236
ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState);
235237
if (!errorNode)
236238
return;
237239

238-
if (!BT)
239-
BT.reset(new BuiltinBug(this, "Out-of-bound access"));
240+
if (!BT) {
241+
if (kind != OOB_Tainted)
242+
BT.reset(
243+
new BugType(this, "Out-of-bound access", categories::LogicError));
244+
else
245+
BT.reset(
246+
new BugType(this, "Out-of-bound access", categories::TaintedData));
247+
}
240248

241249
// FIXME: This diagnostics are preliminary. We should get far better
242250
// diagnostics for explaining buffer overruns.
@@ -255,9 +263,13 @@ void ArrayBoundCheckerV2::reportOOB(
255263
os << "(index is tainted)";
256264
break;
257265
}
266+
std::optional<TaintData> TD = std::nullopt;
267+
if (kind == OOB_Tainted)
268+
TD = getTaintDataPointeeOrPointer(checkerContext, Offset);
258269

259-
auto BR = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), errorNode);
260-
BR->addVisitor(std::move(Visitor));
270+
auto BR =
271+
TD ? std::make_unique<TaintBugReport>(*BT, os.str(), errorNode, *TD)
272+
: std::make_unique<PathSensitiveBugReport>(*BT, os.str(), errorNode);
261273
checkerContext.emitReport(std::move(BR));
262274
}
263275

clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ using namespace taint;
2525

2626
namespace {
2727
class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > {
28-
mutable std::unique_ptr<BuiltinBug> BT;
29-
void reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
28+
mutable std::unique_ptr<BugType> BT;
29+
void reportBug(const char *Msg, std::string Category,
30+
ProgramStateRef StateZero, CheckerContext &C,
3031
std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
3132

3233
public:
@@ -42,13 +43,19 @@ static const Expr *getDenomExpr(const ExplodedNode *N) {
4243
}
4344

4445
void DivZeroChecker::reportBug(
45-
const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
46-
std::unique_ptr<BugReporterVisitor> Visitor) const {
46+
const char *Msg, std::string Category, ProgramStateRef StateZero,
47+
CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const {
4748
if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
4849
if (!BT)
49-
BT.reset(new BuiltinBug(this, "Division by zero"));
50+
BT.reset(new BugType(this, "Division by zero", Category));
5051

51-
auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
52+
std::optional<TaintData> TD = std::nullopt;
53+
if (Category == categories::TaintedData)
54+
TD = getTaintDataPointeeOrPointer(C, C.getSVal(getDenomExpr(N)));
55+
56+
auto R = TD ? std::make_unique<TaintBugReport>(*BT, Msg, N, *TD)
57+
: std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
58+
std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
5259
R->addVisitor(std::move(Visitor));
5360
bugreporter::trackExpressionValue(N, getDenomExpr(N), *R);
5461
C.emitReport(std::move(R));
@@ -82,14 +89,14 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
8289

8390
if (!stateNotZero) {
8491
assert(stateZero);
85-
reportBug("Division by zero", stateZero, C);
92+
reportBug("Division by zero", categories::LogicError, stateZero, C);
8693
return;
8794
}
8895

8996
bool TaintedD = isTainted(C.getState(), *DV);
9097
if ((stateNotZero && stateZero && TaintedD)) {
91-
reportBug("Division by a tainted value, possibly zero", stateZero, C,
92-
std::make_unique<taint::TaintBugVisitor>(*DV));
98+
reportBug("Division by a tainted value, possibly zero",
99+
categories::TaintedData, stateZero, C);
93100
return;
94101
}
95102

0 commit comments

Comments
 (0)