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::tidy::cert { |
17 | |
18 | ProperlySeededRandomGeneratorCheck::ProperlySeededRandomGeneratorCheck( |
19 | StringRef Name, ClangTidyContext *Context) |
20 | : ClangTidyCheck(Name, Context), |
21 | RawDisallowedSeedTypes( |
22 | Options.get(LocalName: "DisallowedSeedTypes" , Default: "time_t,std::time_t" )) { |
23 | StringRef(RawDisallowedSeedTypes).split(A&: DisallowedSeedTypes, Separator: ','); |
24 | } |
25 | |
26 | void ProperlySeededRandomGeneratorCheck::storeOptions( |
27 | ClangTidyOptions::OptionMap &Opts) { |
28 | Options.store(Options&: Opts, LocalName: "DisallowedSeedTypes" , Value: RawDisallowedSeedTypes); |
29 | } |
30 | |
31 | void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) { |
32 | auto RandomGeneratorEngineDecl = cxxRecordDecl(hasAnyName( |
33 | "::std::linear_congruential_engine" , "::std::mersenne_twister_engine" , |
34 | "::std::subtract_with_carry_engine" , "::std::discard_block_engine" , |
35 | "::std::independent_bits_engine" , "::std::shuffle_order_engine" )); |
36 | auto RandomGeneratorEngineTypeMatcher = hasType(InnerMatcher: hasUnqualifiedDesugaredType( |
37 | InnerMatcher: recordType(hasDeclaration(InnerMatcher: RandomGeneratorEngineDecl)))); |
38 | |
39 | // std::mt19937 engine; |
40 | // engine.seed(); |
41 | // ^ |
42 | // engine.seed(1); |
43 | // ^ |
44 | // const int x = 1; |
45 | // engine.seed(x); |
46 | // ^ |
47 | Finder->addMatcher( |
48 | NodeMatch: cxxMemberCallExpr( |
49 | has(memberExpr(has(declRefExpr(RandomGeneratorEngineTypeMatcher)), |
50 | member(InnerMatcher: hasName(Name: "seed" )), |
51 | unless(hasDescendant(cxxThisExpr()))))) |
52 | .bind(ID: "seed" ), |
53 | Action: this); |
54 | |
55 | // std::mt19937 engine; |
56 | // ^ |
57 | // std::mt19937 engine(1); |
58 | // ^ |
59 | // const int x = 1; |
60 | // std::mt19937 engine(x); |
61 | // ^ |
62 | Finder->addMatcher( |
63 | NodeMatch: traverse(TK: TK_AsIs, |
64 | InnerMatcher: cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind(ID: "ctor" )), |
65 | Action: this); |
66 | |
67 | // srand(); |
68 | // ^ |
69 | // const int x = 1; |
70 | // srand(x); |
71 | // ^ |
72 | Finder->addMatcher( |
73 | NodeMatch: callExpr(callee(InnerMatcher: functionDecl(hasAnyName("::srand" , "::std::srand" )))) |
74 | .bind(ID: "srand" ), |
75 | Action: this); |
76 | } |
77 | |
78 | void ProperlySeededRandomGeneratorCheck::check( |
79 | const MatchFinder::MatchResult &Result) { |
80 | const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructExpr>(ID: "ctor" ); |
81 | if (Ctor) |
82 | checkSeed(Result, Func: Ctor); |
83 | |
84 | const auto *Func = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ID: "seed" ); |
85 | if (Func) |
86 | checkSeed(Result, Func); |
87 | |
88 | const auto *Srand = Result.Nodes.getNodeAs<CallExpr>(ID: "srand" ); |
89 | if (Srand) |
90 | checkSeed(Result, Func: 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::is_contained(Range&: DisallowedSeedTypes, Element: SeedType)) { |
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 clang::tidy::cert |
122 | |