1 //===--- ProperlySeededRandomGeneratorCheck.cpp - clang-tidy---------------===// 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 #include "ProperlySeededRandomGeneratorCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "llvm/ADT/STLExtras.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace cert { 19 20 ProperlySeededRandomGeneratorCheck::ProperlySeededRandomGeneratorCheck( 21 StringRef Name, ClangTidyContext *Context) 22 : ClangTidyCheck(Name, Context), 23 RawDisallowedSeedTypes( 24 Options.get("DisallowedSeedTypes", "time_t,std::time_t")) { 25 StringRef(RawDisallowedSeedTypes).split(DisallowedSeedTypes, ','); 26 } 27 28 void ProperlySeededRandomGeneratorCheck::storeOptions( 29 ClangTidyOptions::OptionMap &Opts) { 30 Options.store(Opts, "DisallowedSeedTypes", RawDisallowedSeedTypes); 31 } 32 33 void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) { 34 auto RandomGeneratorEngineDecl = cxxRecordDecl(hasAnyName( 35 "::std::linear_congruential_engine", "::std::mersenne_twister_engine", 36 "::std::subtract_with_carry_engine", "::std::discard_block_engine", 37 "::std::independent_bits_engine", "::std::shuffle_order_engine")); 38 auto RandomGeneratorEngineTypeMatcher = hasType(hasUnqualifiedDesugaredType( 39 recordType(hasDeclaration(RandomGeneratorEngineDecl)))); 40 41 // std::mt19937 engine; 42 // engine.seed(); 43 // ^ 44 // engine.seed(1); 45 // ^ 46 // const int x = 1; 47 // engine.seed(x); 48 // ^ 49 Finder->addMatcher( 50 cxxMemberCallExpr( 51 has(memberExpr(has(declRefExpr(RandomGeneratorEngineTypeMatcher)), 52 member(hasName("seed")), 53 unless(hasDescendant(cxxThisExpr()))))) 54 .bind("seed"), 55 this); 56 57 // std::mt19937 engine; 58 // ^ 59 // std::mt19937 engine(1); 60 // ^ 61 // const int x = 1; 62 // std::mt19937 engine(x); 63 // ^ 64 Finder->addMatcher( 65 cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind("ctor"), this); 66 67 // srand(); 68 // ^ 69 // const int x = 1; 70 // srand(x); 71 // ^ 72 Finder->addMatcher( 73 callExpr(callee(functionDecl(hasAnyName("::srand", "::std::srand")))) 74 .bind("srand"), 75 this); 76 } 77 78 void ProperlySeededRandomGeneratorCheck::check( 79 const MatchFinder::MatchResult &Result) { 80 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor"); 81 if (Ctor) 82 checkSeed(Result, Ctor); 83 84 const auto *Func = Result.Nodes.getNodeAs<CXXMemberCallExpr>("seed"); 85 if (Func) 86 checkSeed(Result, Func); 87 88 const auto *Srand = Result.Nodes.getNodeAs<CallExpr>("srand"); 89 if (Srand) 90 checkSeed(Result, Srand); 91 } 92 93 template <class T> 94 void ProperlySeededRandomGeneratorCheck::checkSeed( 95 const MatchFinder::MatchResult &Result, const T *Func) { 96 if (Func->getNumArgs() == 0 || Func->getArg(0)->isDefaultArgument()) { 97 diag(Func->getExprLoc(), 98 "random number generator seeded with a default argument will generate " 99 "a predictable sequence of values"); 100 return; 101 } 102 103 Expr::EvalResult EVResult; 104 if (Func->getArg(0)->EvaluateAsInt(EVResult, *Result.Context)) { 105 diag(Func->getExprLoc(), 106 "random number generator seeded with a constant value will generate a " 107 "predictable sequence of values"); 108 return; 109 } 110 111 const std::string SeedType( 112 Func->getArg(0)->IgnoreCasts()->getType().getAsString()); 113 if (llvm::find(DisallowedSeedTypes, SeedType) != DisallowedSeedTypes.end()) { 114 diag(Func->getExprLoc(), 115 "random number generator seeded with a disallowed source of seed " 116 "value will generate a predictable sequence of values"); 117 return; 118 } 119 } 120 121 } // namespace cert 122 } // namespace tidy 123 } // namespace clang 124