Skip to content

Commit 273e674

Browse files
committed
[analyzer] Add support for namespaces to GenericTaintChecker
This patch introduces the namespaces for the configured functions and also enables the use of the member functions. I added an optional Scope field for every configured function. Functions without Scope match for every function regardless of the namespace. Functions with Scope will match if the full name of the function starts with the Scope. Multiple functions can exist with the same name. Differential Revision: https://reviews.llvm.org/D70878
1 parent 0133dc3 commit 273e674

File tree

3 files changed

+290
-58
lines changed

3 files changed

+290
-58
lines changed

clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp

+123-58
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
2525
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2626
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
27-
#include "llvm/ADT/StringMap.h"
2827
#include "llvm/Support/YAMLTraits.h"
28+
#include <algorithm>
2929
#include <limits>
30+
#include <unordered_map>
3031
#include <utility>
3132

3233
using namespace clang;
@@ -56,19 +57,20 @@ class GenericTaintChecker
5657

5758
/// Used to parse the configuration file.
5859
struct TaintConfiguration {
59-
using NameArgsPair = std::pair<std::string, ArgVector>;
60+
using NameScopeArgs = std::tuple<std::string, std::string, ArgVector>;
6061

6162
struct Propagation {
6263
std::string Name;
64+
std::string Scope;
6365
ArgVector SrcArgs;
6466
SignedArgVector DstArgs;
6567
VariadicType VarType;
6668
unsigned VarIndex;
6769
};
6870

6971
std::vector<Propagation> Propagations;
70-
std::vector<NameArgsPair> Filters;
71-
std::vector<NameArgsPair> Sinks;
72+
std::vector<NameScopeArgs> Filters;
73+
std::vector<NameScopeArgs> Sinks;
7274

7375
TaintConfiguration() = default;
7476
TaintConfiguration(const TaintConfiguration &) = default;
@@ -97,18 +99,49 @@ class GenericTaintChecker
9799
BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data"));
98100
}
99101

102+
struct FunctionData {
103+
FunctionData() = delete;
104+
FunctionData(const FunctionData &) = default;
105+
FunctionData(FunctionData &&) = default;
106+
FunctionData &operator=(const FunctionData &) = delete;
107+
FunctionData &operator=(FunctionData &&) = delete;
108+
109+
static Optional<FunctionData> create(const CallExpr *CE,
110+
const CheckerContext &C) {
111+
const FunctionDecl *FDecl = C.getCalleeDecl(CE);
112+
if (!FDecl || (FDecl->getKind() != Decl::Function &&
113+
FDecl->getKind() != Decl::CXXMethod))
114+
return None;
115+
116+
StringRef Name = C.getCalleeName(FDecl);
117+
std::string FullName = FDecl->getQualifiedNameAsString();
118+
if (Name.empty() || FullName.empty())
119+
return None;
120+
121+
return FunctionData{FDecl, Name, FullName};
122+
}
123+
124+
bool isInScope(StringRef Scope) const {
125+
return StringRef(FullName).startswith(Scope);
126+
}
127+
128+
const FunctionDecl *const FDecl;
129+
const StringRef Name;
130+
const std::string FullName;
131+
};
132+
100133
/// Catch taint related bugs. Check if tainted data is passed to a
101134
/// system call etc. Returns true on matching.
102-
bool checkPre(const CallExpr *CE, const FunctionDecl *FDecl, StringRef Name,
135+
bool checkPre(const CallExpr *CE, const FunctionData &FData,
103136
CheckerContext &C) const;
104137

105138
/// Add taint sources on a pre-visit. Returns true on matching.
106-
bool addSourcesPre(const CallExpr *CE, const FunctionDecl *FDecl,
107-
StringRef Name, CheckerContext &C) const;
139+
bool addSourcesPre(const CallExpr *CE, const FunctionData &FData,
140+
CheckerContext &C) const;
108141

109142
/// Mark filter's arguments not tainted on a pre-visit. Returns true on
110143
/// matching.
111-
bool addFiltersPre(const CallExpr *CE, StringRef Name,
144+
bool addFiltersPre(const CallExpr *CE, const FunctionData &FData,
112145
CheckerContext &C) const;
113146

114147
/// Propagate taint generated at pre-visit. Returns true on matching.
@@ -149,16 +182,25 @@ class GenericTaintChecker
149182
/// Check if tainted data is used as a custom sink's parameter.
150183
static constexpr llvm::StringLiteral MsgCustomSink =
151184
"Untrusted data is passed to a user-defined sink";
152-
bool checkCustomSinks(const CallExpr *CE, StringRef Name,
185+
bool checkCustomSinks(const CallExpr *CE, const FunctionData &FData,
153186
CheckerContext &C) const;
154187

155188
/// Generate a report if the expression is tainted or points to tainted data.
156189
bool generateReportIfTainted(const Expr *E, StringRef Msg,
157190
CheckerContext &C) const;
158191

159192
struct TaintPropagationRule;
160-
using NameRuleMap = llvm::StringMap<TaintPropagationRule>;
161-
using NameArgMap = llvm::StringMap<ArgVector>;
193+
template <typename T>
194+
using ConfigDataMap =
195+
std::unordered_multimap<std::string, std::pair<std::string, T>>;
196+
using NameRuleMap = ConfigDataMap<TaintPropagationRule>;
197+
using NameArgMap = ConfigDataMap<ArgVector>;
198+
199+
/// Find a function with the given name and scope. Returns the first match
200+
/// or the end of the map.
201+
template <typename T>
202+
static auto findFunctionInConfig(const ConfigDataMap<T> &Map,
203+
const FunctionData &FData);
162204

163205
/// A struct used to specify taint propagation rules for a function.
164206
///
@@ -200,8 +242,7 @@ class GenericTaintChecker
200242
/// Get the propagation rule for a given function.
201243
static TaintPropagationRule
202244
getTaintPropagationRule(const NameRuleMap &CustomPropagations,
203-
const FunctionDecl *FDecl, StringRef Name,
204-
CheckerContext &C);
245+
const FunctionData &FData, CheckerContext &C);
205246

206247
void addSrcArg(unsigned A) { SrcArgs.push_back(A); }
207248
void addDstArg(unsigned A) { DstArgs.push_back(A); }
@@ -236,14 +277,15 @@ class GenericTaintChecker
236277
CheckerContext &C);
237278
};
238279

239-
/// Defines a map between the propagation function's name and
240-
/// TaintPropagationRule.
280+
/// Defines a map between the propagation function's name, scope
281+
/// and TaintPropagationRule.
241282
NameRuleMap CustomPropagations;
242283

243-
/// Defines a map between the filter function's name and filtering args.
284+
/// Defines a map between the filter function's name, scope and filtering
285+
/// args.
244286
NameArgMap CustomFilters;
245287

246-
/// Defines a map between the sink function's name and sinking args.
288+
/// Defines a map between the sink function's name, scope and sinking args.
247289
NameArgMap CustomSinks;
248290
};
249291

@@ -260,7 +302,7 @@ constexpr llvm::StringLiteral GenericTaintChecker::MsgCustomSink;
260302
using TaintConfig = GenericTaintChecker::TaintConfiguration;
261303

262304
LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::Propagation)
263-
LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::NameArgsPair)
305+
LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::NameScopeArgs)
264306

265307
namespace llvm {
266308
namespace yaml {
@@ -275,6 +317,7 @@ template <> struct MappingTraits<TaintConfig> {
275317
template <> struct MappingTraits<TaintConfig::Propagation> {
276318
static void mapping(IO &IO, TaintConfig::Propagation &Propagation) {
277319
IO.mapRequired("Name", Propagation.Name);
320+
IO.mapOptional("Scope", Propagation.Scope);
278321
IO.mapOptional("SrcArgs", Propagation.SrcArgs);
279322
IO.mapOptional("DstArgs", Propagation.DstArgs);
280323
IO.mapOptional("VariadicType", Propagation.VarType,
@@ -292,10 +335,11 @@ template <> struct ScalarEnumerationTraits<GenericTaintChecker::VariadicType> {
292335
}
293336
};
294337

295-
template <> struct MappingTraits<TaintConfig::NameArgsPair> {
296-
static void mapping(IO &IO, TaintConfig::NameArgsPair &NameArg) {
297-
IO.mapRequired("Name", NameArg.first);
298-
IO.mapRequired("Args", NameArg.second);
338+
template <> struct MappingTraits<TaintConfig::NameScopeArgs> {
339+
static void mapping(IO &IO, TaintConfig::NameScopeArgs &NSA) {
340+
IO.mapRequired("Name", std::get<0>(NSA));
341+
IO.mapOptional("Scope", std::get<1>(NSA));
342+
IO.mapRequired("Args", std::get<2>(NSA));
299343
}
300344
};
301345
} // namespace yaml
@@ -328,31 +372,51 @@ void GenericTaintChecker::parseConfiguration(CheckerManager &Mgr,
328372
const std::string &Option,
329373
TaintConfiguration &&Config) {
330374
for (auto &P : Config.Propagations) {
331-
GenericTaintChecker::CustomPropagations.try_emplace(
332-
P.Name, std::move(P.SrcArgs),
333-
convertToArgVector(Mgr, Option, P.DstArgs), P.VarType, P.VarIndex);
375+
GenericTaintChecker::CustomPropagations.emplace(
376+
P.Name,
377+
std::make_pair(P.Scope, TaintPropagationRule{
378+
std::move(P.SrcArgs),
379+
convertToArgVector(Mgr, Option, P.DstArgs),
380+
P.VarType, P.VarIndex}));
334381
}
335382

336383
for (auto &F : Config.Filters) {
337-
GenericTaintChecker::CustomFilters.try_emplace(F.first,
338-
std::move(F.second));
384+
GenericTaintChecker::CustomFilters.emplace(
385+
std::get<0>(F),
386+
std::make_pair(std::move(std::get<1>(F)), std::move(std::get<2>(F))));
339387
}
340388

341389
for (auto &S : Config.Sinks) {
342-
GenericTaintChecker::CustomSinks.try_emplace(S.first, std::move(S.second));
390+
GenericTaintChecker::CustomSinks.emplace(
391+
std::get<0>(S),
392+
std::make_pair(std::move(std::get<1>(S)), std::move(std::get<2>(S))));
343393
}
344394
}
345395

396+
template <typename T>
397+
auto GenericTaintChecker::findFunctionInConfig(const ConfigDataMap<T> &Map,
398+
const FunctionData &FData) {
399+
auto Range = Map.equal_range(FData.Name);
400+
auto It =
401+
std::find_if(Range.first, Range.second, [&FData](const auto &Entry) {
402+
const auto &Value = Entry.second;
403+
StringRef Scope = Value.first;
404+
return Scope.empty() || FData.isInScope(Scope);
405+
});
406+
return It != Range.second ? It : Map.end();
407+
}
408+
346409
GenericTaintChecker::TaintPropagationRule
347410
GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
348-
const NameRuleMap &CustomPropagations, const FunctionDecl *FDecl,
349-
StringRef Name, CheckerContext &C) {
411+
const NameRuleMap &CustomPropagations, const FunctionData &FData,
412+
CheckerContext &C) {
350413
// TODO: Currently, we might lose precision here: we always mark a return
351414
// value as tainted even if it's just a pointer, pointing to tainted data.
352415

353416
// Check for exact name match for functions without builtin substitutes.
417+
// Use qualified name, because these are C functions without namespace.
354418
TaintPropagationRule Rule =
355-
llvm::StringSwitch<TaintPropagationRule>(Name)
419+
llvm::StringSwitch<TaintPropagationRule>(FData.FullName)
356420
// Source functions
357421
// TODO: Add support for vfscanf & family.
358422
.Case("fdopen", TaintPropagationRule({}, {ReturnValueIndex}))
@@ -397,6 +461,7 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
397461

398462
// Check if it's one of the memory setting/copying functions.
399463
// This check is specialized but faster then calling isCLibraryFunction.
464+
const FunctionDecl *FDecl = FData.FDecl;
400465
unsigned BId = 0;
401466
if ((BId = FDecl->getMemoryFunctionKind()))
402467
switch (BId) {
@@ -440,35 +505,32 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
440505
// or smart memory copy:
441506
// - memccpy - copying until hitting a special character.
442507

443-
auto It = CustomPropagations.find(Name);
444-
if (It != CustomPropagations.end())
445-
return It->getValue();
508+
auto It = findFunctionInConfig(CustomPropagations, FData);
509+
if (It != CustomPropagations.end()) {
510+
const auto &Value = It->second;
511+
return Value.second;
512+
}
446513

447514
return TaintPropagationRule();
448515
}
449516

450517
void GenericTaintChecker::checkPreStmt(const CallExpr *CE,
451518
CheckerContext &C) const {
452-
const FunctionDecl *FDecl = C.getCalleeDecl(CE);
453-
// Check for non-global functions.
454-
if (!FDecl || FDecl->getKind() != Decl::Function)
455-
return;
456-
457-
StringRef Name = C.getCalleeName(FDecl);
458-
if (Name.empty())
519+
Optional<FunctionData> FData = FunctionData::create(CE, C);
520+
if (!FData)
459521
return;
460522

461523
// Check for taintedness related errors first: system call, uncontrolled
462524
// format string, tainted buffer size.
463-
if (checkPre(CE, FDecl, Name, C))
525+
if (checkPre(CE, *FData, C))
464526
return;
465527

466528
// Marks the function's arguments and/or return value tainted if it present in
467529
// the list.
468-
if (addSourcesPre(CE, FDecl, Name, C))
530+
if (addSourcesPre(CE, *FData, C))
469531
return;
470532

471-
addFiltersPre(CE, Name, C);
533+
addFiltersPre(CE, *FData, C);
472534
}
473535

474536
void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
@@ -484,12 +546,11 @@ void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State,
484546
}
485547

486548
bool GenericTaintChecker::addSourcesPre(const CallExpr *CE,
487-
const FunctionDecl *FDecl,
488-
StringRef Name,
549+
const FunctionData &FData,
489550
CheckerContext &C) const {
490551
// First, try generating a propagation rule for this function.
491552
TaintPropagationRule Rule = TaintPropagationRule::getTaintPropagationRule(
492-
this->CustomPropagations, FDecl, Name, C);
553+
this->CustomPropagations, FData, C);
493554
if (!Rule.isNull()) {
494555
ProgramStateRef State = Rule.process(CE, C);
495556
if (State) {
@@ -500,14 +561,16 @@ bool GenericTaintChecker::addSourcesPre(const CallExpr *CE,
500561
return false;
501562
}
502563

503-
bool GenericTaintChecker::addFiltersPre(const CallExpr *CE, StringRef Name,
564+
bool GenericTaintChecker::addFiltersPre(const CallExpr *CE,
565+
const FunctionData &FData,
504566
CheckerContext &C) const {
505-
auto It = CustomFilters.find(Name);
567+
auto It = findFunctionInConfig(CustomFilters, FData);
506568
if (It == CustomFilters.end())
507569
return false;
508570

509571
ProgramStateRef State = C.getState();
510-
const ArgVector &Args = It->getValue();
572+
const auto &Value = It->second;
573+
const ArgVector &Args = Value.second;
511574
for (unsigned ArgNum : Args) {
512575
if (ArgNum >= CE->getNumArgs())
513576
continue;
@@ -564,19 +627,19 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
564627
}
565628

566629
bool GenericTaintChecker::checkPre(const CallExpr *CE,
567-
const FunctionDecl *FDecl, StringRef Name,
630+
const FunctionData &FData,
568631
CheckerContext &C) const {
569632

570633
if (checkUncontrolledFormatString(CE, C))
571634
return true;
572635

573-
if (checkSystemCall(CE, Name, C))
636+
if (checkSystemCall(CE, FData.Name, C))
574637
return true;
575638

576-
if (checkTaintedBufferSize(CE, FDecl, C))
639+
if (checkTaintedBufferSize(CE, FData.FDecl, C))
577640
return true;
578641

579-
if (checkCustomSinks(CE, Name, C))
642+
if (checkCustomSinks(CE, FData, C))
580643
return true;
581644

582645
return false;
@@ -595,7 +658,7 @@ Optional<SVal> GenericTaintChecker::getPointedToSVal(CheckerContext &C,
595658

596659
QualType ArgTy = Arg->getType().getCanonicalType();
597660
if (!ArgTy->isPointerType())
598-
return None;
661+
return State->getSVal(*AddrLoc);
599662

600663
QualType ValTy = ArgTy->getPointeeType();
601664

@@ -848,13 +911,15 @@ bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE,
848911
generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C);
849912
}
850913

851-
bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE, StringRef Name,
914+
bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE,
915+
const FunctionData &FData,
852916
CheckerContext &C) const {
853-
auto It = CustomSinks.find(Name);
917+
auto It = findFunctionInConfig(CustomSinks, FData);
854918
if (It == CustomSinks.end())
855919
return false;
856920

857-
const GenericTaintChecker::ArgVector &Args = It->getValue();
921+
const auto &Value = It->second;
922+
const GenericTaintChecker::ArgVector &Args = Value.second;
858923
for (unsigned ArgNum : Args) {
859924
if (ArgNum >= CE->getNumArgs())
860925
continue;

0 commit comments

Comments
 (0)