19
19
20
20
#include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
21
21
#include " UninitializedObject.h"
22
+ #include " clang/ASTMatchers/ASTMatchFinder.h"
22
23
#include " clang/StaticAnalyzer/Core/BugReporter/BugType.h"
23
24
#include " clang/StaticAnalyzer/Core/Checker.h"
24
25
#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25
26
#include " clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
26
27
27
28
using namespace clang ;
28
29
using namespace clang ::ento;
30
+ using namespace clang ::ast_matchers;
29
31
30
32
// / We'll mark fields (and pointee of fields) that are confirmed to be
31
33
// / uninitialized as already analyzed.
@@ -118,6 +120,16 @@ static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
118
120
// / \p Pattern.
119
121
static bool shouldIgnoreRecord (const RecordDecl *RD, StringRef Pattern);
120
122
123
+ // / Checks _syntactically_ whether it is possible to access FD from the record
124
+ // / that contains it without a preceding assert (even if that access happens
125
+ // / inside a method). This is mainly used for records that act like unions, like
126
+ // / having multiple bit fields, with only a fraction being properly initialized.
127
+ // / If these fields are properly guarded with asserts, this method returns
128
+ // / false.
129
+ // /
130
+ // / Since this check is done syntactically, this method could be inaccurate.
131
+ static bool hasUnguardedAccess (const FieldDecl *FD, ProgramStateRef State);
132
+
121
133
// ===----------------------------------------------------------------------===//
122
134
// Methods for UninitializedObjectChecker.
123
135
// ===----------------------------------------------------------------------===//
@@ -234,6 +246,13 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
234
246
" One must also pass the pointee region as a parameter for "
235
247
" dereferenceable fields!" );
236
248
249
+ if (State->getStateManager ().getContext ().getSourceManager ().isInSystemHeader (
250
+ FR->getDecl ()->getLocation ()))
251
+ return false ;
252
+
253
+ if (Opts.IgnoreGuardedFields && !hasUnguardedAccess (FR->getDecl (), State))
254
+ return false ;
255
+
237
256
if (State->contains <AnalyzedRegions>(FR))
238
257
return false ;
239
258
@@ -246,13 +265,10 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
246
265
247
266
State = State->add <AnalyzedRegions>(FR);
248
267
249
- if (State->getStateManager ().getContext ().getSourceManager ().isInSystemHeader (
250
- FR->getDecl ()->getLocation ()))
251
- return false ;
252
-
253
268
UninitFieldMap::mapped_type NoteMsgBuf;
254
269
llvm::raw_svector_ostream OS (NoteMsgBuf);
255
270
Chain.printNoteMsg (OS);
271
+
256
272
return UninitFields.insert ({FR, std::move (NoteMsgBuf)}).second ;
257
273
}
258
274
@@ -441,8 +457,8 @@ static const TypedValueRegion *
441
457
getConstructedRegion (const CXXConstructorDecl *CtorDecl,
442
458
CheckerContext &Context) {
443
459
444
- Loc ThisLoc = Context. getSValBuilder (). getCXXThis (CtorDecl,
445
- Context.getStackFrame ());
460
+ Loc ThisLoc =
461
+ Context. getSValBuilder (). getCXXThis (CtorDecl, Context.getStackFrame ());
446
462
447
463
SVal ObjectV = Context.getState ()->getSVal (ThisLoc);
448
464
@@ -495,6 +511,75 @@ static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
495
511
return false ;
496
512
}
497
513
514
+ static const Stmt *getMethodBody (const CXXMethodDecl *M) {
515
+ if (isa<CXXConstructorDecl>(M))
516
+ return nullptr ;
517
+
518
+ if (!M->isDefined ())
519
+ return nullptr ;
520
+
521
+ return M->getDefinition ()->getBody ();
522
+ }
523
+
524
+ static bool hasUnguardedAccess (const FieldDecl *FD, ProgramStateRef State) {
525
+
526
+ if (FD->getAccess () == AccessSpecifier::AS_public)
527
+ return true ;
528
+
529
+ const auto *Parent = dyn_cast<CXXRecordDecl>(FD->getParent ());
530
+
531
+ if (!Parent)
532
+ return true ;
533
+
534
+ Parent = Parent->getDefinition ();
535
+ assert (Parent && " The record's definition must be avaible if an uninitialized"
536
+ " field of it was found!" );
537
+
538
+ ASTContext &AC = State->getStateManager ().getContext ();
539
+
540
+ auto FieldAccessM = memberExpr (hasDeclaration (equalsNode (FD))).bind (" access" );
541
+
542
+ auto AssertLikeM = callExpr (callee (functionDecl (
543
+ anyOf (hasName (" exit" ), hasName (" panic" ), hasName (" error" ),
544
+ hasName (" Assert" ), hasName (" assert" ), hasName (" ziperr" ),
545
+ hasName (" assfail" ), hasName (" db_error" ), hasName (" __assert" ),
546
+ hasName (" __assert2" ), hasName (" _wassert" ), hasName (" __assert_rtn" ),
547
+ hasName (" __assert_fail" ), hasName (" dtrace_assfail" ),
548
+ hasName (" yy_fatal_error" ), hasName (" _XCAssertionFailureHandler" ),
549
+ hasName (" _DTAssertionFailureHandler" ),
550
+ hasName (" _TSAssertionFailureHandler" )))));
551
+
552
+ auto NoReturnFuncM = callExpr (callee (functionDecl (isNoReturn ())));
553
+
554
+ auto GuardM =
555
+ stmt (anyOf (ifStmt (), switchStmt (), conditionalOperator (), AssertLikeM,
556
+ NoReturnFuncM))
557
+ .bind (" guard" );
558
+
559
+ for (const CXXMethodDecl *M : Parent->methods ()) {
560
+ const Stmt *MethodBody = getMethodBody (M);
561
+ if (!MethodBody)
562
+ continue ;
563
+
564
+ auto Accesses = match (stmt (hasDescendant (FieldAccessM)), *MethodBody, AC);
565
+ if (Accesses.empty ())
566
+ continue ;
567
+ const auto *FirstAccess = Accesses[0 ].getNodeAs <MemberExpr>(" access" );
568
+ assert (FirstAccess);
569
+
570
+ auto Guards = match (stmt (hasDescendant (GuardM)), *MethodBody, AC);
571
+ if (Guards.empty ())
572
+ return true ;
573
+ const auto *FirstGuard = Guards[0 ].getNodeAs <Stmt>(" guard" );
574
+ assert (FirstGuard);
575
+
576
+ if (FirstAccess->getBeginLoc () < FirstGuard->getBeginLoc ())
577
+ return true ;
578
+ }
579
+
580
+ return false ;
581
+ }
582
+
498
583
std::string clang::ento::getVariableName (const FieldDecl *Field) {
499
584
// If Field is a captured lambda variable, Field->getName() will return with
500
585
// an empty string. We can however acquire it's name from the lambda's
@@ -528,12 +613,16 @@ void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
528
613
ChOpts.IsPedantic =
529
614
AnOpts.getCheckerBooleanOption (" Pedantic" , /* DefaultVal*/ false , Chk);
530
615
ChOpts.ShouldConvertNotesToWarnings =
531
- AnOpts.getCheckerBooleanOption (" NotesAsWarnings" , /* DefaultVal*/ false , Chk);
616
+ AnOpts.getCheckerBooleanOption (" NotesAsWarnings" ,
617
+ /* DefaultVal*/ false , Chk);
532
618
ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption (
533
619
" CheckPointeeInitialization" , /* DefaultVal*/ false , Chk);
534
620
ChOpts.IgnoredRecordsWithFieldPattern =
535
621
AnOpts.getCheckerStringOption (" IgnoreRecordsWithField" ,
536
- /* DefaultVal*/ " " , Chk);
622
+ /* DefaultVal*/ " " , Chk);
623
+ ChOpts.IgnoreGuardedFields =
624
+ AnOpts.getCheckerBooleanOption (" IgnoreGuardedFields" ,
625
+ /* DefaultVal*/ false , Chk);
537
626
}
538
627
539
628
bool ento::shouldRegisterUninitializedObjectChecker (const LangOptions &LO) {
0 commit comments