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