| //===--- ProperlySeededRandomGeneratorCheck.cpp - clang-tidy---------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ProperlySeededRandomGeneratorCheck.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "llvm/ADT/STLExtras.h" |
| |
| using namespace clang::ast_matchers; |
| |
| namespace clang { |
| namespace tidy { |
| namespace cert { |
| |
| ProperlySeededRandomGeneratorCheck::ProperlySeededRandomGeneratorCheck( |
| StringRef Name, ClangTidyContext *Context) |
| : ClangTidyCheck(Name, Context), |
| RawDisallowedSeedTypes( |
| Options.get("DisallowedSeedTypes", "time_t,std::time_t")) { |
| StringRef(RawDisallowedSeedTypes).split(DisallowedSeedTypes, ','); |
| } |
| |
| void ProperlySeededRandomGeneratorCheck::storeOptions( |
| ClangTidyOptions::OptionMap &Opts) { |
| Options.store(Opts, "DisallowedSeedTypes", RawDisallowedSeedTypes); |
| } |
| |
| void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) { |
| auto RandomGeneratorEngineDecl = cxxRecordDecl(hasAnyName( |
| "::std::linear_congruential_engine", "::std::mersenne_twister_engine", |
| "::std::subtract_with_carry_engine", "::std::discard_block_engine", |
| "::std::independent_bits_engine", "::std::shuffle_order_engine")); |
| auto RandomGeneratorEngineTypeMatcher = hasType(hasUnqualifiedDesugaredType( |
| recordType(hasDeclaration(RandomGeneratorEngineDecl)))); |
| |
| // std::mt19937 engine; |
| // engine.seed(); |
| // ^ |
| // engine.seed(1); |
| // ^ |
| // const int x = 1; |
| // engine.seed(x); |
| // ^ |
| Finder->addMatcher( |
| cxxMemberCallExpr( |
| has(memberExpr(has(declRefExpr(RandomGeneratorEngineTypeMatcher)), |
| member(hasName("seed")), |
| unless(hasDescendant(cxxThisExpr()))))) |
| .bind("seed"), |
| this); |
| |
| // std::mt19937 engine; |
| // ^ |
| // std::mt19937 engine(1); |
| // ^ |
| // const int x = 1; |
| // std::mt19937 engine(x); |
| // ^ |
| Finder->addMatcher( |
| traverse(TK_AsIs, |
| cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind("ctor")), |
| this); |
| |
| // srand(); |
| // ^ |
| // const int x = 1; |
| // srand(x); |
| // ^ |
| Finder->addMatcher( |
| callExpr(callee(functionDecl(hasAnyName("::srand", "::std::srand")))) |
| .bind("srand"), |
| this); |
| } |
| |
| void ProperlySeededRandomGeneratorCheck::check( |
| const MatchFinder::MatchResult &Result) { |
| const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor"); |
| if (Ctor) |
| checkSeed(Result, Ctor); |
| |
| const auto *Func = Result.Nodes.getNodeAs<CXXMemberCallExpr>("seed"); |
| if (Func) |
| checkSeed(Result, Func); |
| |
| const auto *Srand = Result.Nodes.getNodeAs<CallExpr>("srand"); |
| if (Srand) |
| checkSeed(Result, Srand); |
| } |
| |
| template <class T> |
| void ProperlySeededRandomGeneratorCheck::checkSeed( |
| const MatchFinder::MatchResult &Result, const T *Func) { |
| if (Func->getNumArgs() == 0 || Func->getArg(0)->isDefaultArgument()) { |
| diag(Func->getExprLoc(), |
| "random number generator seeded with a default argument will generate " |
| "a predictable sequence of values"); |
| return; |
| } |
| |
| Expr::EvalResult EVResult; |
| if (Func->getArg(0)->EvaluateAsInt(EVResult, *Result.Context)) { |
| diag(Func->getExprLoc(), |
| "random number generator seeded with a constant value will generate a " |
| "predictable sequence of values"); |
| return; |
| } |
| |
| const std::string SeedType( |
| Func->getArg(0)->IgnoreCasts()->getType().getAsString()); |
| if (llvm::find(DisallowedSeedTypes, SeedType) != DisallowedSeedTypes.end()) { |
| diag(Func->getExprLoc(), |
| "random number generator seeded with a disallowed source of seed " |
| "value will generate a predictable sequence of values"); |
| return; |
| } |
| } |
| |
| } // namespace cert |
| } // namespace tidy |
| } // namespace clang |