-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #39576 from jbytheway/detect_bad_rng_seeding
Add clang-tidy check to catch incorrect use of RNGs
- Loading branch information
Showing
13 changed files
with
230 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
#include "DeterminismCheck.h" | ||
|
||
#include <clang/AST/Decl.h> | ||
#include <clang/AST/DeclCXX.h> | ||
#include <clang/AST/Expr.h> | ||
#include <clang/AST/Type.h> | ||
#include <clang/ASTMatchers/ASTMatchFinder.h> | ||
#include <clang/ASTMatchers/ASTMatchers.h> | ||
#include <clang/ASTMatchers/ASTMatchersInternal.h> | ||
#include <clang/Basic/Diagnostic.h> | ||
|
||
using namespace clang::ast_matchers; | ||
|
||
namespace clang | ||
{ | ||
namespace tidy | ||
{ | ||
namespace cata | ||
{ | ||
void DeterminismCheck::registerMatchers( MatchFinder *Finder ) | ||
{ | ||
Finder->addMatcher( | ||
callExpr( | ||
callee( | ||
functionDecl( | ||
anyOf( hasName( "::rand" ), hasName( "::random" ), hasName( "::drand48" ), | ||
hasName( "::erand48" ), hasName( "::lrand48" ), hasName( "::mrand48" ), | ||
hasName( "::nrand48" ), hasName( "::jrand48" ) ) | ||
) | ||
) | ||
).bind( "call" ), this ); | ||
|
||
// As a heuristic we guess that any record type with a "seed" member | ||
// function is a random engine. Later in the code we also check for | ||
// "result_type". | ||
using TypeMatcher = clang::ast_matchers::internal::Matcher<Decl>; | ||
const TypeMatcher IsRngCoreDecl = cxxRecordDecl( hasMethod( hasName( "seed" ) ) ); | ||
const TypeMatcher IsRngTypedefDecl = | ||
typedefNameDecl( hasType( qualType( hasDeclaration( IsRngCoreDecl ) ) ) ); | ||
const TypeMatcher IsRngTypedef2Decl = | ||
typedefNameDecl( hasType( qualType( hasDeclaration( IsRngTypedefDecl ) ) ) ); | ||
const TypeMatcher IsRngDecl = anyOf( IsRngCoreDecl, IsRngTypedefDecl, IsRngTypedef2Decl ); | ||
Finder->addMatcher( cxxConstructExpr( hasType( IsRngDecl ) ).bind( "init" ), this ); | ||
} | ||
|
||
static void CheckCall( DeterminismCheck &Check, const MatchFinder::MatchResult &Result ) | ||
{ | ||
const CallExpr *Call = Result.Nodes.getNodeAs<clang::CallExpr>( "call" ); | ||
if( !Call ) { | ||
return; | ||
} | ||
const NamedDecl *Function = dyn_cast<NamedDecl>( Call->getCalleeDecl() ); | ||
if( !Function ) { | ||
return; | ||
} | ||
Check.diag( | ||
Call->getBeginLoc(), | ||
"Call to library random function %0. To ensure determinism for a fixed seed, " | ||
"use the common tools from rng.h rather than calling library random functions." ) | ||
<< Function; | ||
} | ||
|
||
static void CheckInit( DeterminismCheck &Check, const MatchFinder::MatchResult &Result ) | ||
{ | ||
const CXXConstructExpr *Construction = Result.Nodes.getNodeAs<CXXConstructExpr>( "init" ); | ||
if( !Construction ) { | ||
return; | ||
} | ||
const CXXRecordDecl *ConstructedTypeDecl = Construction->getConstructor()->getParent(); | ||
bool FoundResultType = false; | ||
for( const Decl *D : ConstructedTypeDecl->decls() ) { | ||
if( const TypedefNameDecl *ND = dyn_cast<TypedefNameDecl>( D ) ) { | ||
if( ND->getName() == "result_type" ) { | ||
FoundResultType = true; | ||
break; | ||
} | ||
} | ||
} | ||
if( !FoundResultType ) { | ||
return; | ||
} | ||
const QualType ConstructedType = Construction->getType(); | ||
Check.diag( | ||
Construction->getBeginLoc(), | ||
"Construction of library random engine %0. To ensure determinism for a fixed seed, " | ||
"use the common tools from rng.h rather than your own random number engines." ) | ||
<< ConstructedType; | ||
} | ||
|
||
void DeterminismCheck::check( const MatchFinder::MatchResult &Result ) | ||
{ | ||
CheckCall( *this, Result ); | ||
CheckInit( *this, Result ); | ||
} | ||
|
||
} // namespace cata | ||
} // namespace tidy | ||
} // namespace clang |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#ifndef CATA_TOOLS_CLANG_TIDY_PLUGIN_DETERMINISMCHECK_H | ||
#define CATA_TOOLS_CLANG_TIDY_PLUGIN_DETERMINISMCHECK_H | ||
|
||
#include <clang/ASTMatchers/ASTMatchFinder.h> | ||
#include <llvm/ADT/StringRef.h> | ||
|
||
#include "ClangTidy.h" | ||
|
||
namespace clang | ||
{ | ||
|
||
namespace tidy | ||
{ | ||
class ClangTidyContext; | ||
|
||
namespace cata | ||
{ | ||
|
||
class DeterminismCheck : public ClangTidyCheck | ||
{ | ||
public: | ||
DeterminismCheck( StringRef Name, ClangTidyContext *Context ) | ||
: ClangTidyCheck( Name, Context ) {} | ||
void registerMatchers( ast_matchers::MatchFinder *Finder ) override; | ||
void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; | ||
}; | ||
|
||
} // namespace cata | ||
} // namespace tidy | ||
} // namespace clang | ||
|
||
#endif // CATA_TOOLS_CLANG_TIDY_PLUGIN_DETERMINISMCHECK_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// RUN: %check_clang_tidy %s cata-determinism %t -- -plugins=%cata_plugin -- | ||
|
||
using size_t = unsigned long; | ||
|
||
namespace std | ||
{ | ||
|
||
using uint_fast32_t = unsigned; | ||
|
||
template < | ||
|
||
class UIntType, | ||
UIntType a, | ||
UIntType c, | ||
UIntType m | ||
> struct linear_congruential_engine | ||
{ | ||
using result_type = UIntType; | ||
explicit linear_congruential_engine( result_type value ); | ||
void seed( result_type value ); | ||
}; | ||
using minstd_rand0 = std::linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>; | ||
|
||
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 | ||
{ | ||
using result_type = UIntType; | ||
void seed( result_type value ); | ||
}; | ||
using mt19937 = std::mersenne_twister_engine<std::uint_fast32_t, 32, 624, 397, 31, | ||
0x9908b0df, 11, | ||
0xffffffff, 7, | ||
0x9d2c5680, 15, | ||
0xefc60000, 18, 1812433253>; | ||
} | ||
|
||
struct SomeOtherStruct { | ||
int i; | ||
void foo(); | ||
}; | ||
|
||
struct StructWithSeedButNotResultType { | ||
void seed(); | ||
}; | ||
|
||
using SomeOtherAlias = SomeOtherStruct; | ||
|
||
int rand(); | ||
|
||
using cata_default_random_engine = std::minstd_rand0; | ||
|
||
namespace std | ||
{ | ||
using ::rand; | ||
} | ||
|
||
namespace other | ||
{ | ||
int rand(); | ||
} | ||
|
||
void f() | ||
{ | ||
std::mt19937 gen0; | ||
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: Construction of library random engine 'std::mt19937' (aka 'mersenne_twister_engine<unsigned int, 32, 624, 397, 31, 2567483615U, 11, 4294967295U, 7, 2636928640U, 15, 4022730752U, 18, 1812433253>'). To ensure determinism for a fixed seed, use the common tools from rng.h rather than your own random number engines. [cata-determinism] | ||
cata_default_random_engine gen1( 0 ); | ||
// CHECK-MESSAGES: [[@LINE-1]]:32: warning: Construction of library random engine 'cata_default_random_engine' (aka 'linear_congruential_engine<unsigned int, 16807, 0, 2147483647>'). To ensure determinism for a fixed seed, use the common tools from rng.h rather than your own random number engines. [cata-determinism] | ||
SomeOtherStruct s0; | ||
StructWithSeedButNotResultType s1; | ||
SomeOtherAlias a; | ||
rand(); | ||
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: Call to library random function 'rand'. To ensure determinism for a fixed seed, use the common tools from rng.h rather than calling library random functions. [cata-determinism] | ||
std::rand(); | ||
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: Call to library random function 'rand'. To ensure determinism for a fixed seed, use the common tools from rng.h rather than calling library random functions. [cata-determinism] | ||
other::rand(); | ||
} |