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 traverse(ast_type_traits::TK_AsIs, 66 cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind("ctor")), 67 this); 68 69 // srand(); 70 // ^ 71 // const int x = 1; 72 // srand(x); 73 // ^ 74 Finder->addMatcher( 75 callExpr(callee(functionDecl(hasAnyName("::srand", "::std::srand")))) 76 .bind("srand"), 77 this); 78 } 79 80 void ProperlySeededRandomGeneratorCheck::check( 81 const MatchFinder::MatchResult &Result) { 82 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor"); 83 if (Ctor) 84 checkSeed(Result, Ctor); 85 86 const auto *Func = Result.Nodes.getNodeAs<CXXMemberCallExpr>("seed"); 87 if (Func) 88 checkSeed(Result, Func); 89 90 const auto *Srand = Result.Nodes.getNodeAs<CallExpr>("srand"); 91 if (Srand) 92 checkSeed(Result, Srand); 93 } 94 95 template <class T> 96 void ProperlySeededRandomGeneratorCheck::checkSeed( 97 const MatchFinder::MatchResult &Result, const T *Func) { 98 if (Func->getNumArgs() == 0 || Func->getArg(0)->isDefaultArgument()) { 99 diag(Func->getExprLoc(), 100 "random number generator seeded with a default argument will generate " 101 "a predictable sequence of values"); 102 return; 103 } 104 105 Expr::EvalResult EVResult; 106 if (Func->getArg(0)->EvaluateAsInt(EVResult, *Result.Context)) { 107 diag(Func->getExprLoc(), 108 "random number generator seeded with a constant value will generate a " 109 "predictable sequence of values"); 110 return; 111 } 112 113 const std::string SeedType( 114 Func->getArg(0)->IgnoreCasts()->getType().getAsString()); 115 if (llvm::find(DisallowedSeedTypes, SeedType) != DisallowedSeedTypes.end()) { 116 diag(Func->getExprLoc(), 117 "random number generator seeded with a disallowed source of seed " 118 "value will generate a predictable sequence of values"); 119 return; 120 } 121 } 122 123 } // namespace cert 124 } // namespace tidy 125 } // namespace clang 126