boga95 created this revision. boga95 added a reviewer: clang-tools-extra. boga95 added a project: clang-tools-extra. Herald added subscribers: cfe-commits, mgorny.
This check flags all pseudo-random number engines and engine adaptors instantiations when it initialized or seeded with default argument or a constant expression. Pseudo-random number engines seeded with a predictable value may cause vulnerabilities e.g. in security protocols. This is a CERT security rule, see MSC51-CPP <https://wiki.sei.cmu.edu/confluence/display/cplusplus/MSC51-CPP.+Ensure+your+random+number+generator+is+properly+seeded>. Example: void foo() { std::mt19937 engine1; // Bad, always generate the same sequence std::mt19937 engine2(1); // Bad engine1.seed(); // Bad engine2.seed(1); // Bad std::time_t t; engine1.seed(std::time(&t)); // Bad, system time might be controlled by user std::random_device dev; std::mt19937 engine3(dev()); // Good } Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D44143 Files: clang-tidy/cert/CERTTidyModule.cpp clang-tidy/cert/CMakeLists.txt clang-tidy/cert/ProperlySeededRandomGeneratorCheck.cpp clang-tidy/cert/ProperlySeededRandomGeneratorCheck.h docs/ReleaseNotes.rst docs/clang-tidy/checks/cert-properly-seeded-random-generator.rst test/clang-tidy/cert-properly-seeded-random-generator.cpp
Index: test/clang-tidy/cert-properly-seeded-random-generator.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cert-properly-seeded-random-generator.cpp @@ -0,0 +1,172 @@ +// RUN: %check_clang_tidy %s cert-properly-seeded-random-generator %t -- -- -std=c++11 + +namespace std { + +template <class UIntType, UIntType a, UIntType c, UIntType m> +struct linear_congruential_engine { + linear_congruential_engine(int _ = 0); + void seed(int _ = 0); +}; +using default_random_engine = linear_congruential_engine<unsigned int, 1, 2, 3>; + +using size_t = int; +template <class UIntType, size_t w, size_t n, size_t m, size_t r, + UIntType a, size_t u, UIntType d, size_t s, + UIntType b, size_t t, + UIntType c, size_t l, UIntType f> +struct mersenne_twister_engine { + mersenne_twister_engine(int _ = 0); + void seed(int _ = 0); +}; +using mt19937 = mersenne_twister_engine<unsigned int, 32, 624, 397, 21, 0x9908b0df, 11, 0xffffffff, 7, 0x9d2c5680, 15, 0xefc60000, 18, 1812433253>; + +template <class UIntType, size_t w, size_t s, size_t r> +struct subtract_with_carry_engine { + subtract_with_carry_engine(int _ = 0); + void seed(int _ = 0); +}; +using ranlux24_base = subtract_with_carry_engine<unsigned int, 24, 10, 24>; + +template <class Engine, size_t p, size_t r> +struct discard_block_engine { + discard_block_engine(); + discard_block_engine(int _); + void seed(); + void seed(int _); +}; +using ranlux24 = discard_block_engine<ranlux24_base, 223, 23>; + +template <class Engine, size_t w, class UIntType> +struct independent_bits_engine { + independent_bits_engine(); + independent_bits_engine(int _); + void seed(); + void seed(int _); +}; +using independent_bits = independent_bits_engine<ranlux24_base, 223, int>; + +template <class Engine, size_t k> +struct shuffle_order_engine { + shuffle_order_engine(); + shuffle_order_engine(int _); + void seed(); + void seed(int _); +}; +using shuffle_order = shuffle_order_engine<ranlux24_base, 223>; + +struct random_device { + random_device(); + int operator()(); +}; +} // namespace std + +void f() { + const int seed = 2; + // One instantiation for every engine + std::default_random_engine engine1; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: random number generator must be seeded with a random_device instead of default argument + std::default_random_engine engine2(1); + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + std::default_random_engine engine3(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine1.seed(); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator must be seeded with a random_device instead of default argument [cert-properly-seeded-random-generator] + engine1.seed(1); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine1.seed(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + + std::mt19937 engine4; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: random number generator must be seeded with a random_device instead of default argument + std::mt19937 engine5(1); + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + std::mt19937 engine6(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine4.seed(); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator must be seeded with a random_device instead of default argument [cert-properly-seeded-random-generator] + engine4.seed(1); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine4.seed(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + + std::ranlux24_base engine7; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator must be seeded with a random_device instead of default argument + std::ranlux24_base engine8(1); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + std::ranlux24_base engine9(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine7.seed(); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator must be seeded with a random_device instead of default argument [cert-properly-seeded-random-generator] + engine7.seed(1); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine7.seed(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + + std::ranlux24 engine10; + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: random number generator must be seeded with a random_device instead of default argument + std::ranlux24 engine11(1); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + std::ranlux24 engine12(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine10.seed(); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator must be seeded with a random_device instead of default argument [cert-properly-seeded-random-generator] + engine10.seed(1); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine10.seed(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + + std::independent_bits engine13; + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: random number generator must be seeded with a random_device instead of default argument + std::independent_bits engine14(1); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + std::independent_bits engine15(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine13.seed(); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator must be seeded with a random_device instead of default argument [cert-properly-seeded-random-generator] + engine13.seed(1); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine13.seed(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + + std::shuffle_order engine16; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator must be seeded with a random_device instead of default argument + std::shuffle_order engine17(1); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + std::shuffle_order engine18(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine16.seed(); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator must be seeded with a random_device instead of default argument [cert-properly-seeded-random-generator] + engine16.seed(1); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] + engine16.seed(seed); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: random number generator must be seeded with a random_device instead of a constant [cert-properly-seeded-random-generator] +} + +struct A { + A(int _ = 0); + void seed(int _ = 0); +}; + +void g() { + int n = 1; + std::default_random_engine engine1(n); + std::mt19937 engine2(n); + std::ranlux24_base engine3(n); + std::ranlux24 engine4(n); + std::independent_bits engine5(n); + std::shuffle_order engine6(n); + + std::random_device dev; + std::default_random_engine engine7(dev()); + std::mt19937 engine8(dev()); + std::ranlux24_base engine9(dev()); + std::ranlux24 engine10(dev()); + std::independent_bits engine11(dev()); + std::shuffle_order engine12(dev()); + + A a1; + A a2(1); + a1.seed(); + a1.seed(1); + a1.seed(n); +} Index: docs/clang-tidy/checks/cert-properly-seeded-random-generator.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/cert-properly-seeded-random-generator.rst @@ -0,0 +1,27 @@ +.. title:: clang-tidy - cert-properly-seeded-random-generator + +cert-properly-seeded-random-generator +===================================== + +This check flags all pseudo-random number engines and engine adaptors +instantiations when it initialized or seeded with default argument or constant +expression. Pseudo-random number engines seeded with a predictable value may +cause vulnerabilities e.g. in security protocols. +This is a CERT security rule, see MSC51-CPP. + +Examples: + +.. code-block:: c++ + + void foo() { + std::mt19937 engine1; // Bad, always generate the same sequence + std::mt19937 engine2(1); // Bad + engine1.seed(); // Bad + engine2.seed(1); // Bad + + std::time_t t; + engine1.seed(std::time(&t)); // Bad, system time might be controlled by user + + std::random_device dev; + std::mt19937 engine3(dev()); // Good + } Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -57,6 +57,11 @@ Improvements to clang-tidy -------------------------- +- New `cert-properly-seeded-random-generator + <http://clang.llvm.org/extra/clang-tidy/checks/cert-properly-seeded-random-generator.html>`_ check + + Detects inappropriate seeding of C++ random generators. + - The 'misc-incorrect-roundings' check was renamed to `bugprone-incorrect-roundings <http://clang.llvm.org/extra/clang-tidy/checks/bugprone-incorrect-roundings.html>`_ Index: clang-tidy/cert/ProperlySeededRandomGeneratorCheck.h =================================================================== --- /dev/null +++ clang-tidy/cert/ProperlySeededRandomGeneratorCheck.h @@ -0,0 +1,41 @@ +//===--- ProperlySeededRandomGeneratorCheck.h - clang-tidy-------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PROPERLY_SEEDED_RANDOM_GENERATOR_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PROPERLY_SEEDED_RANDOM_GENERATOR_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace cert { + +/// Random number generator must be seeded properly. +/// +/// A random number generator initialized with default value or a +/// constant expression is a security vulnerability. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cert-properly-seeded-random-generator.html +class ProperlySeededRandomGeneratorCheck : public ClangTidyCheck { +public: + ProperlySeededRandomGeneratorCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + template<class T> + void checkSeed(const ast_matchers::MatchFinder::MatchResult &Result, + const T *Func); +}; + +} // namespace cert +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PROPERLY_SEEDED_RANDOM_GENERATOR_H Index: clang-tidy/cert/ProperlySeededRandomGeneratorCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/cert/ProperlySeededRandomGeneratorCheck.cpp @@ -0,0 +1,85 @@ +//===--- ProperlySeededRandomGeneratorCheck.cpp - clang-tidy---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProperlySeededRandomGeneratorCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cert { + +void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) { + auto RandomGeneratorEngineDecl = recordDecl( + hasAnyName("linear_congruential_engine", "mersenne_twister_engine", + "subtract_with_carry_engine", "discard_block_engine", + "independent_bits_engine", "shuffle_order_engine")); + auto RandomGeneratorEngineType = + qualType(hasCanonicalType(hasDeclaration(RandomGeneratorEngineDecl))); + + // std::mt19937 engine; + // engine.seed(); + // ^ + // engine.seed(1); + // ^ + // const int x = 1; + // engine.seed(x); + // ^ + Finder->addMatcher( + cxxMemberCallExpr( + has(memberExpr(has(declRefExpr(hasType(RandomGeneratorEngineType))), + 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( + cxxConstructExpr(hasType(RandomGeneratorEngineType)).bind("ctor"), 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); +} + + 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 must be seeded with " + "a random_device instead of default argument"); + return; + } + + llvm::APSInt Value; + if (Func->getArg(0)->EvaluateAsInt(Value, *Result.Context)) { + diag(Func->getExprLoc(), "random number generator must be seeded with " + "a random_device instead of a constant"); + return; + } +} + +} // namespace cert +} // namespace tidy +} // namespace clang Index: clang-tidy/cert/CMakeLists.txt =================================================================== --- clang-tidy/cert/CMakeLists.txt +++ clang-tidy/cert/CMakeLists.txt @@ -7,6 +7,7 @@ FloatLoopCounter.cpp LimitedRandomnessCheck.cpp PostfixOperatorCheck.cpp + ProperlySeededRandomGeneratorCheck.cpp SetLongJmpCheck.cpp StaticObjectExceptionCheck.cpp StrToNumCheck.cpp Index: clang-tidy/cert/CERTTidyModule.cpp =================================================================== --- clang-tidy/cert/CERTTidyModule.cpp +++ clang-tidy/cert/CERTTidyModule.cpp @@ -21,6 +21,7 @@ #include "FloatLoopCounter.h" #include "LimitedRandomnessCheck.h" #include "PostfixOperatorCheck.h" +#include "ProperlySeededRandomGeneratorCheck.h" #include "SetLongJmpCheck.h" #include "StaticObjectExceptionCheck.h" #include "StrToNumCheck.h" @@ -38,6 +39,8 @@ // DCL CheckFactories.registerCheck<PostfixOperatorCheck>( "cert-dcl21-cpp"); + CheckFactories.registerCheck<ProperlySeededRandomGeneratorCheck>( + "cert-properly-seeded-random-generator"); CheckFactories.registerCheck<VariadicFunctionDefCheck>("cert-dcl50-cpp"); CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>( "cert-dcl54-cpp");
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits