1 //== GenericTaintChecker.cpp ----------------------------------- -*- C++ -*--=// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This checker defines the attack surface for generic taint propagation. 10 // 11 // The taint information produced by it might be useful to other checkers. For 12 // example, checkers should report errors which involve tainted data more 13 // aggressively, even if the involved symbols are under constrained. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "Yaml.h" 18 #include "clang/AST/Attr.h" 19 #include "clang/Basic/Builtins.h" 20 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 21 #include "clang/StaticAnalyzer/Checkers/Taint.h" 22 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 23 #include "clang/StaticAnalyzer/Core/Checker.h" 24 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 25 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 26 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 28 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 29 #include "llvm/Support/YAMLTraits.h" 30 31 #include <limits> 32 #include <memory> 33 #include <optional> 34 #include <utility> 35 36 #define DEBUG_TYPE "taint-checker" 37 38 using namespace clang; 39 using namespace ento; 40 using namespace taint; 41 42 using llvm::ImmutableSet; 43 44 namespace { 45 46 class GenericTaintChecker; 47 48 /// Check for CWE-134: Uncontrolled Format String. 49 constexpr llvm::StringLiteral MsgUncontrolledFormatString = 50 "Untrusted data is used as a format string " 51 "(CWE-134: Uncontrolled Format String)"; 52 53 /// Check for: 54 /// CERT/STR02-C. "Sanitize data passed to complex subsystems" 55 /// CWE-78, "Failure to Sanitize Data into an OS Command" 56 constexpr llvm::StringLiteral MsgSanitizeSystemArgs = 57 "Untrusted data is passed to a system call " 58 "(CERT/STR02-C. Sanitize data passed to complex subsystems)"; 59 60 /// Check if tainted data is used as a buffer size in strn.. functions, 61 /// and allocators. 62 constexpr llvm::StringLiteral MsgTaintedBufferSize = 63 "Untrusted data is used to specify the buffer size " 64 "(CERT/STR31-C. Guarantee that storage for strings has sufficient space " 65 "for character data and the null terminator)"; 66 67 /// Check if tainted data is used as a custom sink's parameter. 68 constexpr llvm::StringLiteral MsgCustomSink = 69 "Untrusted data is passed to a user-defined sink"; 70 71 using ArgIdxTy = int; 72 using ArgVecTy = llvm::SmallVector<ArgIdxTy, 2>; 73 74 /// Denotes the return value. 75 constexpr ArgIdxTy ReturnValueIndex{-1}; 76 77 static ArgIdxTy fromArgumentCount(unsigned Count) { 78 assert(Count <= 79 static_cast<std::size_t>(std::numeric_limits<ArgIdxTy>::max()) && 80 "ArgIdxTy is not large enough to represent the number of arguments."); 81 return Count; 82 } 83 84 /// Check if the region the expression evaluates to is the standard input, 85 /// and thus, is tainted. 86 /// FIXME: Move this to Taint.cpp. 87 bool isStdin(SVal Val, const ASTContext &ACtx) { 88 // FIXME: What if Val is NonParamVarRegion? 89 90 // The region should be symbolic, we do not know it's value. 91 const auto *SymReg = dyn_cast_or_null<SymbolicRegion>(Val.getAsRegion()); 92 if (!SymReg) 93 return false; 94 95 // Get it's symbol and find the declaration region it's pointing to. 96 const auto *DeclReg = 97 dyn_cast_or_null<DeclRegion>(SymReg->getSymbol()->getOriginRegion()); 98 if (!DeclReg) 99 return false; 100 101 // This region corresponds to a declaration, find out if it's a global/extern 102 // variable named stdin with the proper type. 103 if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) { 104 D = D->getCanonicalDecl(); 105 // FIXME: This should look for an exact match. 106 if (D->getName().contains("stdin") && D->isExternC()) { 107 const QualType FILETy = ACtx.getFILEType().getCanonicalType(); 108 const QualType Ty = D->getType().getCanonicalType(); 109 110 if (Ty->isPointerType()) 111 return Ty->getPointeeType() == FILETy; 112 } 113 } 114 return false; 115 } 116 117 SVal getPointeeOf(const CheckerContext &C, Loc LValue) { 118 const QualType ArgTy = LValue.getType(C.getASTContext()); 119 if (!ArgTy->isPointerType() || !ArgTy->getPointeeType()->isVoidType()) 120 return C.getState()->getSVal(LValue); 121 122 // Do not dereference void pointers. Treat them as byte pointers instead. 123 // FIXME: we might want to consider more than just the first byte. 124 return C.getState()->getSVal(LValue, C.getASTContext().CharTy); 125 } 126 127 /// Given a pointer/reference argument, return the value it refers to. 128 std::optional<SVal> getPointeeOf(const CheckerContext &C, SVal Arg) { 129 if (auto LValue = Arg.getAs<Loc>()) 130 return getPointeeOf(C, *LValue); 131 return std::nullopt; 132 } 133 134 /// Given a pointer, return the SVal of its pointee or if it is tainted, 135 /// otherwise return the pointer's SVal if tainted. 136 /// Also considers stdin as a taint source. 137 std::optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, 138 SVal Arg) { 139 const ProgramStateRef State = C.getState(); 140 141 if (auto Pointee = getPointeeOf(C, Arg)) 142 if (isTainted(State, *Pointee)) // FIXME: isTainted(...) ? Pointee : None; 143 return Pointee; 144 145 if (isTainted(State, Arg)) 146 return Arg; 147 148 // FIXME: This should be done by the isTainted() API. 149 if (isStdin(Arg, C.getASTContext())) 150 return Arg; 151 152 return std::nullopt; 153 } 154 155 bool isTaintedOrPointsToTainted(const Expr *E, const ProgramStateRef &State, 156 CheckerContext &C) { 157 return getTaintedPointeeOrPointer(C, C.getSVal(E)).has_value(); 158 } 159 160 /// ArgSet is used to describe arguments relevant for taint detection or 161 /// taint application. A discrete set of argument indexes and a variadic 162 /// argument list signified by a starting index are supported. 163 class ArgSet { 164 public: 165 ArgSet() = default; 166 ArgSet(ArgVecTy &&DiscreteArgs, 167 std::optional<ArgIdxTy> VariadicIndex = std::nullopt) 168 : DiscreteArgs(std::move(DiscreteArgs)), 169 VariadicIndex(std::move(VariadicIndex)) {} 170 171 bool contains(ArgIdxTy ArgIdx) const { 172 if (llvm::is_contained(DiscreteArgs, ArgIdx)) 173 return true; 174 175 return VariadicIndex && ArgIdx >= *VariadicIndex; 176 } 177 178 bool isEmpty() const { return DiscreteArgs.empty() && !VariadicIndex; } 179 180 private: 181 ArgVecTy DiscreteArgs; 182 std::optional<ArgIdxTy> VariadicIndex; 183 }; 184 185 /// A struct used to specify taint propagation rules for a function. 186 /// 187 /// If any of the possible taint source arguments is tainted, all of the 188 /// destination arguments should also be tainted. If ReturnValueIndex is added 189 /// to the dst list, the return value will be tainted. 190 class GenericTaintRule { 191 /// Arguments which are taints sinks and should be checked, and a report 192 /// should be emitted if taint reaches these. 193 ArgSet SinkArgs; 194 /// Arguments which should be sanitized on function return. 195 ArgSet FilterArgs; 196 /// Arguments which can participate in taint propagationa. If any of the 197 /// arguments in PropSrcArgs is tainted, all arguments in PropDstArgs should 198 /// be tainted. 199 ArgSet PropSrcArgs; 200 ArgSet PropDstArgs; 201 202 /// A message that explains why the call is sensitive to taint. 203 std::optional<StringRef> SinkMsg; 204 205 GenericTaintRule() = default; 206 207 GenericTaintRule(ArgSet &&Sink, ArgSet &&Filter, ArgSet &&Src, ArgSet &&Dst, 208 std::optional<StringRef> SinkMsg = std::nullopt) 209 : SinkArgs(std::move(Sink)), FilterArgs(std::move(Filter)), 210 PropSrcArgs(std::move(Src)), PropDstArgs(std::move(Dst)), 211 SinkMsg(SinkMsg) {} 212 213 public: 214 /// Make a rule that reports a warning if taint reaches any of \p FilterArgs 215 /// arguments. 216 static GenericTaintRule Sink(ArgSet &&SinkArgs, 217 std::optional<StringRef> Msg = std::nullopt) { 218 return {std::move(SinkArgs), {}, {}, {}, Msg}; 219 } 220 221 /// Make a rule that sanitizes all FilterArgs arguments. 222 static GenericTaintRule Filter(ArgSet &&FilterArgs) { 223 return {{}, std::move(FilterArgs), {}, {}}; 224 } 225 226 /// Make a rule that unconditionally taints all Args. 227 /// If Func is provided, it must also return true for taint to propagate. 228 static GenericTaintRule Source(ArgSet &&SourceArgs) { 229 return {{}, {}, {}, std::move(SourceArgs)}; 230 } 231 232 /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted. 233 static GenericTaintRule Prop(ArgSet &&SrcArgs, ArgSet &&DstArgs) { 234 return {{}, {}, std::move(SrcArgs), std::move(DstArgs)}; 235 } 236 237 /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted. 238 static GenericTaintRule 239 SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs, ArgSet &&DstArgs, 240 std::optional<StringRef> Msg = std::nullopt) { 241 return { 242 std::move(SinkArgs), {}, std::move(SrcArgs), std::move(DstArgs), Msg}; 243 } 244 245 /// Process a function which could either be a taint source, a taint sink, a 246 /// taint filter or a taint propagator. 247 void process(const GenericTaintChecker &Checker, const CallEvent &Call, 248 CheckerContext &C) const; 249 250 /// Handles the resolution of indexes of type ArgIdxTy to Expr*-s. 251 static const Expr *GetArgExpr(ArgIdxTy ArgIdx, const CallEvent &Call) { 252 return ArgIdx == ReturnValueIndex ? Call.getOriginExpr() 253 : Call.getArgExpr(ArgIdx); 254 }; 255 256 /// Functions for custom taintedness propagation. 257 static bool UntrustedEnv(CheckerContext &C); 258 }; 259 260 using RuleLookupTy = CallDescriptionMap<GenericTaintRule>; 261 262 /// Used to parse the configuration file. 263 struct TaintConfiguration { 264 using NameScopeArgs = std::tuple<std::string, std::string, ArgVecTy>; 265 enum class VariadicType { None, Src, Dst }; 266 267 struct Common { 268 std::string Name; 269 std::string Scope; 270 }; 271 272 struct Sink : Common { 273 ArgVecTy SinkArgs; 274 }; 275 276 struct Filter : Common { 277 ArgVecTy FilterArgs; 278 }; 279 280 struct Propagation : Common { 281 ArgVecTy SrcArgs; 282 ArgVecTy DstArgs; 283 VariadicType VarType; 284 ArgIdxTy VarIndex; 285 }; 286 287 std::vector<Propagation> Propagations; 288 std::vector<Filter> Filters; 289 std::vector<Sink> Sinks; 290 291 TaintConfiguration() = default; 292 TaintConfiguration(const TaintConfiguration &) = default; 293 TaintConfiguration(TaintConfiguration &&) = default; 294 TaintConfiguration &operator=(const TaintConfiguration &) = default; 295 TaintConfiguration &operator=(TaintConfiguration &&) = default; 296 }; 297 298 struct GenericTaintRuleParser { 299 GenericTaintRuleParser(CheckerManager &Mgr) : Mgr(Mgr) {} 300 /// Container type used to gather call identification objects grouped into 301 /// pairs with their corresponding taint rules. It is temporary as it is used 302 /// to finally initialize RuleLookupTy, which is considered to be immutable. 303 using RulesContTy = std::vector<std::pair<CallDescription, GenericTaintRule>>; 304 RulesContTy parseConfiguration(const std::string &Option, 305 TaintConfiguration &&Config) const; 306 307 private: 308 using NamePartsTy = llvm::SmallVector<StringRef, 2>; 309 310 /// Validate part of the configuration, which contains a list of argument 311 /// indexes. 312 void validateArgVector(const std::string &Option, const ArgVecTy &Args) const; 313 314 template <typename Config> static NamePartsTy parseNameParts(const Config &C); 315 316 // Takes the config and creates a CallDescription for it and associates a Rule 317 // with that. 318 template <typename Config> 319 static void consumeRulesFromConfig(const Config &C, GenericTaintRule &&Rule, 320 RulesContTy &Rules); 321 322 void parseConfig(const std::string &Option, TaintConfiguration::Sink &&P, 323 RulesContTy &Rules) const; 324 void parseConfig(const std::string &Option, TaintConfiguration::Filter &&P, 325 RulesContTy &Rules) const; 326 void parseConfig(const std::string &Option, 327 TaintConfiguration::Propagation &&P, 328 RulesContTy &Rules) const; 329 330 CheckerManager &Mgr; 331 }; 332 333 class GenericTaintChecker : public Checker<check::PreCall, check::PostCall> { 334 public: 335 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 336 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 337 338 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 339 const char *Sep) const override; 340 341 /// Generate a report if the expression is tainted or points to tainted data. 342 bool generateReportIfTainted(const Expr *E, StringRef Msg, 343 CheckerContext &C) const; 344 345 private: 346 const BugType BT{this, "Use of Untrusted Data", "Untrusted Data"}; 347 348 bool checkUncontrolledFormatString(const CallEvent &Call, 349 CheckerContext &C) const; 350 351 void taintUnsafeSocketProtocol(const CallEvent &Call, 352 CheckerContext &C) const; 353 354 /// Default taint rules are initilized with the help of a CheckerContext to 355 /// access the names of built-in functions like memcpy. 356 void initTaintRules(CheckerContext &C) const; 357 358 /// CallDescription currently cannot restrict matches to the global namespace 359 /// only, which is why multiple CallDescriptionMaps are used, as we want to 360 /// disambiguate global C functions from functions inside user-defined 361 /// namespaces. 362 // TODO: Remove separation to simplify matching logic once CallDescriptions 363 // are more expressive. 364 365 mutable std::optional<RuleLookupTy> StaticTaintRules; 366 mutable std::optional<RuleLookupTy> DynamicTaintRules; 367 }; 368 } // end of anonymous namespace 369 370 /// YAML serialization mapping. 371 LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Sink) 372 LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Filter) 373 LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Propagation) 374 375 namespace llvm { 376 namespace yaml { 377 template <> struct MappingTraits<TaintConfiguration> { 378 static void mapping(IO &IO, TaintConfiguration &Config) { 379 IO.mapOptional("Propagations", Config.Propagations); 380 IO.mapOptional("Filters", Config.Filters); 381 IO.mapOptional("Sinks", Config.Sinks); 382 } 383 }; 384 385 template <> struct MappingTraits<TaintConfiguration::Sink> { 386 static void mapping(IO &IO, TaintConfiguration::Sink &Sink) { 387 IO.mapRequired("Name", Sink.Name); 388 IO.mapOptional("Scope", Sink.Scope); 389 IO.mapRequired("Args", Sink.SinkArgs); 390 } 391 }; 392 393 template <> struct MappingTraits<TaintConfiguration::Filter> { 394 static void mapping(IO &IO, TaintConfiguration::Filter &Filter) { 395 IO.mapRequired("Name", Filter.Name); 396 IO.mapOptional("Scope", Filter.Scope); 397 IO.mapRequired("Args", Filter.FilterArgs); 398 } 399 }; 400 401 template <> struct MappingTraits<TaintConfiguration::Propagation> { 402 static void mapping(IO &IO, TaintConfiguration::Propagation &Propagation) { 403 IO.mapRequired("Name", Propagation.Name); 404 IO.mapOptional("Scope", Propagation.Scope); 405 IO.mapOptional("SrcArgs", Propagation.SrcArgs); 406 IO.mapOptional("DstArgs", Propagation.DstArgs); 407 IO.mapOptional("VariadicType", Propagation.VarType); 408 IO.mapOptional("VariadicIndex", Propagation.VarIndex); 409 } 410 }; 411 412 template <> struct ScalarEnumerationTraits<TaintConfiguration::VariadicType> { 413 static void enumeration(IO &IO, TaintConfiguration::VariadicType &Value) { 414 IO.enumCase(Value, "None", TaintConfiguration::VariadicType::None); 415 IO.enumCase(Value, "Src", TaintConfiguration::VariadicType::Src); 416 IO.enumCase(Value, "Dst", TaintConfiguration::VariadicType::Dst); 417 } 418 }; 419 } // namespace yaml 420 } // namespace llvm 421 422 /// A set which is used to pass information from call pre-visit instruction 423 /// to the call post-visit. The values are signed integers, which are either 424 /// ReturnValueIndex, or indexes of the pointer/reference argument, which 425 /// points to data, which should be tainted on return. 426 REGISTER_MAP_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, const LocationContext *, 427 ImmutableSet<ArgIdxTy>) 428 REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ArgIdxFactory, ArgIdxTy) 429 430 void GenericTaintRuleParser::validateArgVector(const std::string &Option, 431 const ArgVecTy &Args) const { 432 for (ArgIdxTy Arg : Args) { 433 if (Arg < ReturnValueIndex) { 434 Mgr.reportInvalidCheckerOptionValue( 435 Mgr.getChecker<GenericTaintChecker>(), Option, 436 "an argument number for propagation rules greater or equal to -1"); 437 } 438 } 439 } 440 441 template <typename Config> 442 GenericTaintRuleParser::NamePartsTy 443 GenericTaintRuleParser::parseNameParts(const Config &C) { 444 NamePartsTy NameParts; 445 if (!C.Scope.empty()) { 446 // If the Scope argument contains multiple "::" parts, those are considered 447 // namespace identifiers. 448 StringRef{C.Scope}.split(NameParts, "::", /*MaxSplit*/ -1, 449 /*KeepEmpty*/ false); 450 } 451 NameParts.emplace_back(C.Name); 452 return NameParts; 453 } 454 455 template <typename Config> 456 void GenericTaintRuleParser::consumeRulesFromConfig(const Config &C, 457 GenericTaintRule &&Rule, 458 RulesContTy &Rules) { 459 NamePartsTy NameParts = parseNameParts(C); 460 Rules.emplace_back(CallDescription(NameParts), std::move(Rule)); 461 } 462 463 void GenericTaintRuleParser::parseConfig(const std::string &Option, 464 TaintConfiguration::Sink &&S, 465 RulesContTy &Rules) const { 466 validateArgVector(Option, S.SinkArgs); 467 consumeRulesFromConfig(S, GenericTaintRule::Sink(std::move(S.SinkArgs)), 468 Rules); 469 } 470 471 void GenericTaintRuleParser::parseConfig(const std::string &Option, 472 TaintConfiguration::Filter &&S, 473 RulesContTy &Rules) const { 474 validateArgVector(Option, S.FilterArgs); 475 consumeRulesFromConfig(S, GenericTaintRule::Filter(std::move(S.FilterArgs)), 476 Rules); 477 } 478 479 void GenericTaintRuleParser::parseConfig(const std::string &Option, 480 TaintConfiguration::Propagation &&P, 481 RulesContTy &Rules) const { 482 validateArgVector(Option, P.SrcArgs); 483 validateArgVector(Option, P.DstArgs); 484 bool IsSrcVariadic = P.VarType == TaintConfiguration::VariadicType::Src; 485 bool IsDstVariadic = P.VarType == TaintConfiguration::VariadicType::Dst; 486 std::optional<ArgIdxTy> JustVarIndex = P.VarIndex; 487 488 ArgSet SrcDesc(std::move(P.SrcArgs), 489 IsSrcVariadic ? JustVarIndex : std::nullopt); 490 ArgSet DstDesc(std::move(P.DstArgs), 491 IsDstVariadic ? JustVarIndex : std::nullopt); 492 493 consumeRulesFromConfig( 494 P, GenericTaintRule::Prop(std::move(SrcDesc), std::move(DstDesc)), Rules); 495 } 496 497 GenericTaintRuleParser::RulesContTy 498 GenericTaintRuleParser::parseConfiguration(const std::string &Option, 499 TaintConfiguration &&Config) const { 500 501 RulesContTy Rules; 502 503 for (auto &F : Config.Filters) 504 parseConfig(Option, std::move(F), Rules); 505 506 for (auto &S : Config.Sinks) 507 parseConfig(Option, std::move(S), Rules); 508 509 for (auto &P : Config.Propagations) 510 parseConfig(Option, std::move(P), Rules); 511 512 return Rules; 513 } 514 515 void GenericTaintChecker::initTaintRules(CheckerContext &C) const { 516 // Check for exact name match for functions without builtin substitutes. 517 // Use qualified name, because these are C functions without namespace. 518 519 if (StaticTaintRules || DynamicTaintRules) 520 return; 521 522 using RulesConstructionTy = 523 std::vector<std::pair<CallDescription, GenericTaintRule>>; 524 using TR = GenericTaintRule; 525 526 const Builtin::Context &BI = C.getASTContext().BuiltinInfo; 527 528 RulesConstructionTy GlobalCRules{ 529 // Sources 530 {{{"fdopen"}}, TR::Source({{ReturnValueIndex}})}, 531 {{{"fopen"}}, TR::Source({{ReturnValueIndex}})}, 532 {{{"freopen"}}, TR::Source({{ReturnValueIndex}})}, 533 {{{"getch"}}, TR::Source({{ReturnValueIndex}})}, 534 {{{"getchar"}}, TR::Source({{ReturnValueIndex}})}, 535 {{{"getchar_unlocked"}}, TR::Source({{ReturnValueIndex}})}, 536 {{{"gets"}}, TR::Source({{0}, ReturnValueIndex})}, 537 {{{"gets_s"}}, TR::Source({{0}, ReturnValueIndex})}, 538 {{{"scanf"}}, TR::Source({{}, 1})}, 539 {{{"scanf_s"}}, TR::Source({{}, {1}})}, 540 {{{"wgetch"}}, TR::Source({{}, ReturnValueIndex})}, 541 // Sometimes the line between taint sources and propagators is blurry. 542 // _IO_getc is choosen to be a source, but could also be a propagator. 543 // This way it is simpler, as modeling it as a propagator would require 544 // to model the possible sources of _IO_FILE * values, which the _IO_getc 545 // function takes as parameters. 546 {{{"_IO_getc"}}, TR::Source({{ReturnValueIndex}})}, 547 {{{"getcwd"}}, TR::Source({{0, ReturnValueIndex}})}, 548 {{{"getwd"}}, TR::Source({{0, ReturnValueIndex}})}, 549 {{{"readlink"}}, TR::Source({{1, ReturnValueIndex}})}, 550 {{{"readlinkat"}}, TR::Source({{2, ReturnValueIndex}})}, 551 {{{"get_current_dir_name"}}, TR::Source({{ReturnValueIndex}})}, 552 {{{"gethostname"}}, TR::Source({{0}})}, 553 {{{"getnameinfo"}}, TR::Source({{2, 4}})}, 554 {{{"getseuserbyname"}}, TR::Source({{1, 2}})}, 555 {{{"getgroups"}}, TR::Source({{1, ReturnValueIndex}})}, 556 {{{"getlogin"}}, TR::Source({{ReturnValueIndex}})}, 557 {{{"getlogin_r"}}, TR::Source({{0}})}, 558 559 // Props 560 {{{"atoi"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 561 {{{"atol"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 562 {{{"atoll"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 563 {{{"fgetc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 564 {{{"fgetln"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 565 {{{"fgets"}}, TR::Prop({{2}}, {{0, ReturnValueIndex}})}, 566 {{{"fscanf"}}, TR::Prop({{0}}, {{}, 2})}, 567 {{{"fscanf_s"}}, TR::Prop({{0}}, {{}, {2}})}, 568 {{{"sscanf"}}, TR::Prop({{0}}, {{}, 2})}, 569 570 {{{"getc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 571 {{{"getc_unlocked"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 572 {{{"getdelim"}}, TR::Prop({{3}}, {{0}})}, 573 {{{"getline"}}, TR::Prop({{2}}, {{0}})}, 574 {{{"getw"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 575 {{{"pread"}}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})}, 576 {{{"read"}}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})}, 577 {{{"strchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 578 {{{"strrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 579 {{{"tolower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 580 {{{"toupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 581 {{{"fread"}}, TR::Prop({{3}}, {{0, ReturnValueIndex}})}, 582 {{{"recv"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, 583 {{{"recvfrom"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, 584 585 {{{"ttyname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 586 {{{"ttyname_r"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, 587 588 {{{"basename"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 589 {{{"dirname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 590 {{{"fnmatch"}}, TR::Prop({{1}}, {{ReturnValueIndex}})}, 591 {{{"memchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 592 {{{"memrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 593 {{{"rawmemchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 594 595 {{{"mbtowc"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, 596 {{{"wctomb"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, 597 {{{"wcwidth"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 598 599 {{{"memcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, 600 {{{"memcpy"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, 601 {{{"memmove"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, 602 // If memmem was called with a tainted needle and the search was 603 // successful, that would mean that the value pointed by the return value 604 // has the same content as the needle. If we choose to go by the policy of 605 // content equivalence implies taintedness equivalence, that would mean 606 // haystack should be considered a propagation source argument. 607 {{{"memmem"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 608 609 // The comment for memmem above also applies to strstr. 610 {{{"strstr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 611 {{{"strcasestr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 612 613 {{{"strchrnul"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 614 615 {{{"index"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 616 {{{"rindex"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 617 618 // FIXME: In case of arrays, only the first element of the array gets 619 // tainted. 620 {{{"qsort"}}, TR::Prop({{0}}, {{0}})}, 621 {{{"qsort_r"}}, TR::Prop({{0}}, {{0}})}, 622 623 {{{"strcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, 624 {{{"strcasecmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, 625 {{{"strncmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, 626 {{{"strncasecmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, 627 {{{"strspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, 628 {{{"strcspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, 629 {{{"strpbrk"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 630 {{{"strndup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 631 {{{"strndupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 632 {{{"strlen"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 633 {{{"strnlen"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 634 {{{"strtol"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, 635 {{{"strtoll"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, 636 {{{"strtoul"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, 637 {{{"strtoull"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, 638 639 {{{"isalnum"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 640 {{{"isalpha"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 641 {{{"isascii"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 642 {{{"isblank"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 643 {{{"iscntrl"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 644 {{{"isdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 645 {{{"isgraph"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 646 {{{"islower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 647 {{{"isprint"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 648 {{{"ispunct"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 649 {{{"isspace"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 650 {{{"isupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 651 {{{"isxdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 652 653 {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncat)}}, 654 TR::Prop({{1, 2}}, {{0, ReturnValueIndex}})}, 655 {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcpy)}}, 656 TR::Prop({{1, 2}}, {{0}})}, 657 {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcat)}}, 658 TR::Prop({{1, 2}}, {{0}})}, 659 {{CDF_MaybeBuiltin, {{"snprintf"}}}, 660 TR::Prop({{1}, 3}, {{0, ReturnValueIndex}})}, 661 {{CDF_MaybeBuiltin, {{"sprintf"}}}, 662 TR::Prop({{1}, 2}, {{0, ReturnValueIndex}})}, 663 {{CDF_MaybeBuiltin, {{"strcpy"}}}, 664 TR::Prop({{1}}, {{0, ReturnValueIndex}})}, 665 {{CDF_MaybeBuiltin, {{"stpcpy"}}}, 666 TR::Prop({{1}}, {{0, ReturnValueIndex}})}, 667 {{CDF_MaybeBuiltin, {{"strcat"}}}, 668 TR::Prop({{1}}, {{0, ReturnValueIndex}})}, 669 {{CDF_MaybeBuiltin, {{"strdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 670 {{CDF_MaybeBuiltin, {{"strdupa"}}}, 671 TR::Prop({{0}}, {{ReturnValueIndex}})}, 672 {{CDF_MaybeBuiltin, {{"wcsdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, 673 674 // Sinks 675 {{{"system"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, 676 {{{"popen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, 677 {{{"execl"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, 678 {{{"execle"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, 679 {{{"execlp"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, 680 {{{"execvp"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, 681 {{{"execvP"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, 682 {{{"execve"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, 683 {{{"dlopen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, 684 {{CDF_MaybeBuiltin, {{"malloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, 685 {{CDF_MaybeBuiltin, {{"calloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, 686 {{CDF_MaybeBuiltin, {{"alloca"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, 687 {{CDF_MaybeBuiltin, {{"memccpy"}}}, 688 TR::Sink({{3}}, MsgTaintedBufferSize)}, 689 {{CDF_MaybeBuiltin, {{"realloc"}}}, 690 TR::Sink({{1}}, MsgTaintedBufferSize)}, 691 {{{{"setproctitle"}}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, 692 {{{{"setproctitle_fast"}}}, 693 TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, 694 695 // SinkProps 696 {{CDF_MaybeBuiltin, BI.getName(Builtin::BImemcpy)}, 697 TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}}, 698 MsgTaintedBufferSize)}, 699 {{CDF_MaybeBuiltin, {BI.getName(Builtin::BImemmove)}}, 700 TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}}, 701 MsgTaintedBufferSize)}, 702 {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncpy)}}, 703 TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}}, 704 MsgTaintedBufferSize)}, 705 {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrndup)}}, 706 TR::SinkProp({{1}}, {{0, 1}}, {{ReturnValueIndex}}, 707 MsgTaintedBufferSize)}, 708 {{CDF_MaybeBuiltin, {{"bcopy"}}}, 709 TR::SinkProp({{2}}, {{0, 2}}, {{1}}, MsgTaintedBufferSize)}}; 710 711 // `getenv` returns taint only in untrusted environments. 712 if (TR::UntrustedEnv(C)) { 713 // void setproctitle_init(int argc, char *argv[], char *envp[]) 714 GlobalCRules.push_back( 715 {{{"setproctitle_init"}}, TR::Sink({{1, 2}}, MsgCustomSink)}); 716 GlobalCRules.push_back({{{"getenv"}}, TR::Source({{ReturnValueIndex}})}); 717 } 718 719 StaticTaintRules.emplace(std::make_move_iterator(GlobalCRules.begin()), 720 std::make_move_iterator(GlobalCRules.end())); 721 722 // User-provided taint configuration. 723 CheckerManager *Mgr = C.getAnalysisManager().getCheckerManager(); 724 assert(Mgr); 725 GenericTaintRuleParser ConfigParser{*Mgr}; 726 std::string Option{"Config"}; 727 StringRef ConfigFile = 728 Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option); 729 std::optional<TaintConfiguration> Config = 730 getConfiguration<TaintConfiguration>(*Mgr, this, Option, ConfigFile); 731 if (!Config) { 732 // We don't have external taint config, no parsing required. 733 DynamicTaintRules = RuleLookupTy{}; 734 return; 735 } 736 737 GenericTaintRuleParser::RulesContTy Rules{ 738 ConfigParser.parseConfiguration(Option, std::move(*Config))}; 739 740 DynamicTaintRules.emplace(std::make_move_iterator(Rules.begin()), 741 std::make_move_iterator(Rules.end())); 742 } 743 744 void GenericTaintChecker::checkPreCall(const CallEvent &Call, 745 CheckerContext &C) const { 746 initTaintRules(C); 747 748 // FIXME: this should be much simpler. 749 if (const auto *Rule = 750 Call.isGlobalCFunction() ? StaticTaintRules->lookup(Call) : nullptr) 751 Rule->process(*this, Call, C); 752 else if (const auto *Rule = DynamicTaintRules->lookup(Call)) 753 Rule->process(*this, Call, C); 754 755 // FIXME: These edge cases are to be eliminated from here eventually. 756 // 757 // Additional check that is not supported by CallDescription. 758 // TODO: Make CallDescription be able to match attributes such as printf-like 759 // arguments. 760 checkUncontrolledFormatString(Call, C); 761 762 // TODO: Modeling sockets should be done in a specific checker. 763 // Socket is a source, which taints the return value. 764 taintUnsafeSocketProtocol(Call, C); 765 } 766 767 void GenericTaintChecker::checkPostCall(const CallEvent &Call, 768 CheckerContext &C) const { 769 // Set the marked values as tainted. The return value only accessible from 770 // checkPostStmt. 771 ProgramStateRef State = C.getState(); 772 const StackFrameContext *CurrentFrame = C.getStackFrame(); 773 774 // Depending on what was tainted at pre-visit, we determined a set of 775 // arguments which should be tainted after the function returns. These are 776 // stored in the state as TaintArgsOnPostVisit set. 777 TaintArgsOnPostVisitTy TaintArgsMap = State->get<TaintArgsOnPostVisit>(); 778 779 const ImmutableSet<ArgIdxTy> *TaintArgs = TaintArgsMap.lookup(CurrentFrame); 780 if (!TaintArgs) 781 return; 782 assert(!TaintArgs->isEmpty()); 783 784 LLVM_DEBUG(for (ArgIdxTy I 785 : *TaintArgs) { 786 llvm::dbgs() << "PostCall<"; 787 Call.dump(llvm::dbgs()); 788 llvm::dbgs() << "> actually wants to taint arg index: " << I << '\n'; 789 }); 790 791 for (ArgIdxTy ArgNum : *TaintArgs) { 792 // Special handling for the tainted return value. 793 if (ArgNum == ReturnValueIndex) { 794 State = addTaint(State, Call.getReturnValue()); 795 continue; 796 } 797 798 // The arguments are pointer arguments. The data they are pointing at is 799 // tainted after the call. 800 if (auto V = getPointeeOf(C, Call.getArgSVal(ArgNum))) 801 State = addTaint(State, *V); 802 } 803 804 // Clear up the taint info from the state. 805 State = State->remove<TaintArgsOnPostVisit>(CurrentFrame); 806 C.addTransition(State); 807 } 808 809 void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State, 810 const char *NL, const char *Sep) const { 811 printTaint(State, Out, NL, Sep); 812 } 813 814 void GenericTaintRule::process(const GenericTaintChecker &Checker, 815 const CallEvent &Call, CheckerContext &C) const { 816 ProgramStateRef State = C.getState(); 817 const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs()); 818 819 /// Iterate every call argument, and get their corresponding Expr and SVal. 820 const auto ForEachCallArg = [&C, &Call, CallNumArgs](auto &&Fun) { 821 for (ArgIdxTy I = ReturnValueIndex; I < CallNumArgs; ++I) { 822 const Expr *E = GetArgExpr(I, Call); 823 Fun(I, E, C.getSVal(E)); 824 } 825 }; 826 827 /// Check for taint sinks. 828 ForEachCallArg([this, &Checker, &C, &State](ArgIdxTy I, const Expr *E, SVal) { 829 if (SinkArgs.contains(I) && isTaintedOrPointsToTainted(E, State, C)) 830 Checker.generateReportIfTainted(E, SinkMsg.value_or(MsgCustomSink), C); 831 }); 832 833 /// Check for taint filters. 834 ForEachCallArg([this, &C, &State](ArgIdxTy I, const Expr *E, SVal S) { 835 if (FilterArgs.contains(I)) { 836 State = removeTaint(State, S); 837 if (auto P = getPointeeOf(C, S)) 838 State = removeTaint(State, *P); 839 } 840 }); 841 842 /// Check for taint propagation sources. 843 /// A rule is relevant if PropSrcArgs is empty, or if any of its signified 844 /// args are tainted in context of the current CallEvent. 845 bool IsMatching = PropSrcArgs.isEmpty(); 846 ForEachCallArg( 847 [this, &C, &IsMatching, &State](ArgIdxTy I, const Expr *E, SVal) { 848 IsMatching = IsMatching || (PropSrcArgs.contains(I) && 849 isTaintedOrPointsToTainted(E, State, C)); 850 }); 851 852 if (!IsMatching) 853 return; 854 855 const auto WouldEscape = [](SVal V, QualType Ty) -> bool { 856 if (!isa<Loc>(V)) 857 return false; 858 859 const bool IsNonConstRef = Ty->isReferenceType() && !Ty.isConstQualified(); 860 const bool IsNonConstPtr = 861 Ty->isPointerType() && !Ty->getPointeeType().isConstQualified(); 862 863 return IsNonConstRef || IsNonConstPtr; 864 }; 865 866 /// Propagate taint where it is necessary. 867 auto &F = State->getStateManager().get_context<ArgIdxFactory>(); 868 ImmutableSet<ArgIdxTy> Result = F.getEmptySet(); 869 ForEachCallArg( 870 [&](ArgIdxTy I, const Expr *E, SVal V) { 871 if (PropDstArgs.contains(I)) { 872 LLVM_DEBUG(llvm::dbgs() << "PreCall<"; Call.dump(llvm::dbgs()); 873 llvm::dbgs() 874 << "> prepares tainting arg index: " << I << '\n';); 875 Result = F.add(Result, I); 876 } 877 878 // TODO: We should traverse all reachable memory regions via the 879 // escaping parameter. Instead of doing that we simply mark only the 880 // referred memory region as tainted. 881 if (WouldEscape(V, E->getType())) { 882 LLVM_DEBUG(if (!Result.contains(I)) { 883 llvm::dbgs() << "PreCall<"; 884 Call.dump(llvm::dbgs()); 885 llvm::dbgs() << "> prepares tainting arg index: " << I << '\n'; 886 }); 887 Result = F.add(Result, I); 888 } 889 }); 890 891 if (!Result.isEmpty()) 892 State = State->set<TaintArgsOnPostVisit>(C.getStackFrame(), Result); 893 C.addTransition(State); 894 } 895 896 bool GenericTaintRule::UntrustedEnv(CheckerContext &C) { 897 return !C.getAnalysisManager() 898 .getAnalyzerOptions() 899 .ShouldAssumeControlledEnvironment; 900 } 901 902 bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg, 903 CheckerContext &C) const { 904 assert(E); 905 std::optional<SVal> TaintedSVal{getTaintedPointeeOrPointer(C, C.getSVal(E))}; 906 907 if (!TaintedSVal) 908 return false; 909 910 // Generate diagnostic. 911 if (ExplodedNode *N = C.generateNonFatalErrorNode()) { 912 auto report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); 913 report->addRange(E->getSourceRange()); 914 report->addVisitor(std::make_unique<TaintBugVisitor>(*TaintedSVal)); 915 C.emitReport(std::move(report)); 916 return true; 917 } 918 return false; 919 } 920 921 /// TODO: remove checking for printf format attributes and socket whitelisting 922 /// from GenericTaintChecker, and that means the following functions: 923 /// getPrintfFormatArgumentNum, 924 /// GenericTaintChecker::checkUncontrolledFormatString, 925 /// GenericTaintChecker::taintUnsafeSocketProtocol 926 927 static bool getPrintfFormatArgumentNum(const CallEvent &Call, 928 const CheckerContext &C, 929 ArgIdxTy &ArgNum) { 930 // Find if the function contains a format string argument. 931 // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf, 932 // vsnprintf, syslog, custom annotated functions. 933 const Decl *CallDecl = Call.getDecl(); 934 if (!CallDecl) 935 return false; 936 const FunctionDecl *FDecl = CallDecl->getAsFunction(); 937 if (!FDecl) 938 return false; 939 940 const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs()); 941 942 for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) { 943 ArgNum = Format->getFormatIdx() - 1; 944 if ((Format->getType()->getName() == "printf") && CallNumArgs > ArgNum) 945 return true; 946 } 947 948 return false; 949 } 950 951 bool GenericTaintChecker::checkUncontrolledFormatString( 952 const CallEvent &Call, CheckerContext &C) const { 953 // Check if the function contains a format string argument. 954 ArgIdxTy ArgNum = 0; 955 if (!getPrintfFormatArgumentNum(Call, C, ArgNum)) 956 return false; 957 958 // If either the format string content or the pointer itself are tainted, 959 // warn. 960 return generateReportIfTainted(Call.getArgExpr(ArgNum), 961 MsgUncontrolledFormatString, C); 962 } 963 964 void GenericTaintChecker::taintUnsafeSocketProtocol(const CallEvent &Call, 965 CheckerContext &C) const { 966 if (Call.getNumArgs() < 1) 967 return; 968 const IdentifierInfo *ID = Call.getCalleeIdentifier(); 969 if (!ID) 970 return; 971 if (!ID->getName().equals("socket")) 972 return; 973 974 SourceLocation DomLoc = Call.getArgExpr(0)->getExprLoc(); 975 StringRef DomName = C.getMacroNameOrSpelling(DomLoc); 976 // Allow internal communication protocols. 977 bool SafeProtocol = DomName.equals("AF_SYSTEM") || 978 DomName.equals("AF_LOCAL") || DomName.equals("AF_UNIX") || 979 DomName.equals("AF_RESERVED_36"); 980 if (SafeProtocol) 981 return; 982 983 ProgramStateRef State = C.getState(); 984 auto &F = State->getStateManager().get_context<ArgIdxFactory>(); 985 ImmutableSet<ArgIdxTy> Result = F.add(F.getEmptySet(), ReturnValueIndex); 986 State = State->set<TaintArgsOnPostVisit>(C.getStackFrame(), Result); 987 C.addTransition(State); 988 } 989 990 /// Checker registration 991 void ento::registerGenericTaintChecker(CheckerManager &Mgr) { 992 Mgr.registerChecker<GenericTaintChecker>(); 993 } 994 995 bool ento::shouldRegisterGenericTaintChecker(const CheckerManager &mgr) { 996 return true; 997 } 998