xref: /llvm-project/clang-tools-extra/clang-tidy/cert/ProperlySeededRandomGeneratorCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
15bee05c5SAaron Ballman //===--- ProperlySeededRandomGeneratorCheck.cpp - clang-tidy---------------===//
25bee05c5SAaron Ballman //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65bee05c5SAaron Ballman //
75bee05c5SAaron Ballman //===----------------------------------------------------------------------===//
85bee05c5SAaron Ballman 
95bee05c5SAaron Ballman #include "ProperlySeededRandomGeneratorCheck.h"
105bee05c5SAaron Ballman #include "clang/AST/ASTContext.h"
115bee05c5SAaron Ballman #include "clang/ASTMatchers/ASTMatchFinder.h"
125bee05c5SAaron Ballman #include "llvm/ADT/STLExtras.h"
135bee05c5SAaron Ballman 
145bee05c5SAaron Ballman using namespace clang::ast_matchers;
155bee05c5SAaron Ballman 
16*7d2ea6c4SCarlos Galvez namespace clang::tidy::cert {
175bee05c5SAaron Ballman 
ProperlySeededRandomGeneratorCheck(StringRef Name,ClangTidyContext * Context)185bee05c5SAaron Ballman ProperlySeededRandomGeneratorCheck::ProperlySeededRandomGeneratorCheck(
195bee05c5SAaron Ballman     StringRef Name, ClangTidyContext *Context)
205bee05c5SAaron Ballman     : ClangTidyCheck(Name, Context),
215bee05c5SAaron Ballman       RawDisallowedSeedTypes(
225bee05c5SAaron Ballman           Options.get("DisallowedSeedTypes", "time_t,std::time_t")) {
235bee05c5SAaron Ballman   StringRef(RawDisallowedSeedTypes).split(DisallowedSeedTypes, ',');
245bee05c5SAaron Ballman }
255bee05c5SAaron Ballman 
storeOptions(ClangTidyOptions::OptionMap & Opts)265bee05c5SAaron Ballman void ProperlySeededRandomGeneratorCheck::storeOptions(
275bee05c5SAaron Ballman     ClangTidyOptions::OptionMap &Opts) {
285bee05c5SAaron Ballman   Options.store(Opts, "DisallowedSeedTypes", RawDisallowedSeedTypes);
295bee05c5SAaron Ballman }
305bee05c5SAaron Ballman 
registerMatchers(MatchFinder * Finder)315bee05c5SAaron Ballman void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) {
325bee05c5SAaron Ballman   auto RandomGeneratorEngineDecl = cxxRecordDecl(hasAnyName(
335bee05c5SAaron Ballman       "::std::linear_congruential_engine", "::std::mersenne_twister_engine",
345bee05c5SAaron Ballman       "::std::subtract_with_carry_engine", "::std::discard_block_engine",
355bee05c5SAaron Ballman       "::std::independent_bits_engine", "::std::shuffle_order_engine"));
365bee05c5SAaron Ballman   auto RandomGeneratorEngineTypeMatcher = hasType(hasUnqualifiedDesugaredType(
375bee05c5SAaron Ballman       recordType(hasDeclaration(RandomGeneratorEngineDecl))));
385bee05c5SAaron Ballman 
395bee05c5SAaron Ballman   // std::mt19937 engine;
405bee05c5SAaron Ballman   // engine.seed();
415bee05c5SAaron Ballman   //        ^
425bee05c5SAaron Ballman   // engine.seed(1);
435bee05c5SAaron Ballman   //        ^
445bee05c5SAaron Ballman   // const int x = 1;
455bee05c5SAaron Ballman   // engine.seed(x);
465bee05c5SAaron Ballman   //        ^
475bee05c5SAaron Ballman   Finder->addMatcher(
485bee05c5SAaron Ballman       cxxMemberCallExpr(
495bee05c5SAaron Ballman           has(memberExpr(has(declRefExpr(RandomGeneratorEngineTypeMatcher)),
505bee05c5SAaron Ballman                          member(hasName("seed")),
515bee05c5SAaron Ballman                          unless(hasDescendant(cxxThisExpr())))))
525bee05c5SAaron Ballman           .bind("seed"),
535bee05c5SAaron Ballman       this);
545bee05c5SAaron Ballman 
555bee05c5SAaron Ballman   // std::mt19937 engine;
565bee05c5SAaron Ballman   //              ^
575bee05c5SAaron Ballman   // std::mt19937 engine(1);
585bee05c5SAaron Ballman   //              ^
595bee05c5SAaron Ballman   // const int x = 1;
605bee05c5SAaron Ballman   // std::mt19937 engine(x);
615bee05c5SAaron Ballman   //              ^
625bee05c5SAaron Ballman   Finder->addMatcher(
63027899daSAlexander Kornienko       traverse(TK_AsIs,
64a72307c3SStephen Kelly                cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind("ctor")),
65a72307c3SStephen Kelly       this);
665bee05c5SAaron Ballman 
675bee05c5SAaron Ballman   // srand();
685bee05c5SAaron Ballman   // ^
695bee05c5SAaron Ballman   // const int x = 1;
705bee05c5SAaron Ballman   // srand(x);
715bee05c5SAaron Ballman   // ^
725bee05c5SAaron Ballman   Finder->addMatcher(
735bee05c5SAaron Ballman       callExpr(callee(functionDecl(hasAnyName("::srand", "::std::srand"))))
745bee05c5SAaron Ballman           .bind("srand"),
755bee05c5SAaron Ballman       this);
765bee05c5SAaron Ballman }
775bee05c5SAaron Ballman 
check(const MatchFinder::MatchResult & Result)785bee05c5SAaron Ballman void ProperlySeededRandomGeneratorCheck::check(
795bee05c5SAaron Ballman     const MatchFinder::MatchResult &Result) {
805bee05c5SAaron Ballman   const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
815bee05c5SAaron Ballman   if (Ctor)
825bee05c5SAaron Ballman     checkSeed(Result, Ctor);
835bee05c5SAaron Ballman 
845bee05c5SAaron Ballman   const auto *Func = Result.Nodes.getNodeAs<CXXMemberCallExpr>("seed");
855bee05c5SAaron Ballman   if (Func)
865bee05c5SAaron Ballman     checkSeed(Result, Func);
875bee05c5SAaron Ballman 
885bee05c5SAaron Ballman   const auto *Srand = Result.Nodes.getNodeAs<CallExpr>("srand");
895bee05c5SAaron Ballman   if (Srand)
905bee05c5SAaron Ballman     checkSeed(Result, Srand);
915bee05c5SAaron Ballman }
925bee05c5SAaron Ballman 
935bee05c5SAaron Ballman template <class T>
checkSeed(const MatchFinder::MatchResult & Result,const T * Func)945bee05c5SAaron Ballman void ProperlySeededRandomGeneratorCheck::checkSeed(
955bee05c5SAaron Ballman     const MatchFinder::MatchResult &Result, const T *Func) {
965bee05c5SAaron Ballman   if (Func->getNumArgs() == 0 || Func->getArg(0)->isDefaultArgument()) {
975bee05c5SAaron Ballman     diag(Func->getExprLoc(),
985bee05c5SAaron Ballman          "random number generator seeded with a default argument will generate "
995bee05c5SAaron Ballman          "a predictable sequence of values");
1005bee05c5SAaron Ballman     return;
1015bee05c5SAaron Ballman   }
1025bee05c5SAaron Ballman 
103cab8dd69SHans Wennborg   Expr::EvalResult EVResult;
104cab8dd69SHans Wennborg   if (Func->getArg(0)->EvaluateAsInt(EVResult, *Result.Context)) {
1055bee05c5SAaron Ballman     diag(Func->getExprLoc(),
1065bee05c5SAaron Ballman          "random number generator seeded with a constant value will generate a "
1075bee05c5SAaron Ballman          "predictable sequence of values");
1085bee05c5SAaron Ballman     return;
1095bee05c5SAaron Ballman   }
1105bee05c5SAaron Ballman 
1115bee05c5SAaron Ballman   const std::string SeedType(
1125bee05c5SAaron Ballman       Func->getArg(0)->IgnoreCasts()->getType().getAsString());
1137542e721SKazu Hirata   if (llvm::is_contained(DisallowedSeedTypes, SeedType)) {
1145bee05c5SAaron Ballman     diag(Func->getExprLoc(),
1155bee05c5SAaron Ballman          "random number generator seeded with a disallowed source of seed "
1165bee05c5SAaron Ballman          "value will generate a predictable sequence of values");
1175bee05c5SAaron Ballman     return;
1185bee05c5SAaron Ballman   }
1195bee05c5SAaron Ballman }
1205bee05c5SAaron Ballman 
121*7d2ea6c4SCarlos Galvez } // namespace clang::tidy::cert
122