1919
2020#include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
2121#include " UninitializedObject.h"
22+ #include " clang/ASTMatchers/ASTMatchFinder.h"
2223#include " clang/StaticAnalyzer/Core/BugReporter/BugType.h"
2324#include " clang/StaticAnalyzer/Core/Checker.h"
2425#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2526#include " clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
2627
2728using namespace clang ;
2829using namespace clang ::ento;
30+ using namespace clang ::ast_matchers;
2931
3032// / We'll mark fields (and pointee of fields) that are confirmed to be
3133// / uninitialized as already analyzed.
@@ -118,6 +120,16 @@ static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
118120// / \p Pattern.
119121static bool shouldIgnoreRecord (const RecordDecl *RD, StringRef Pattern);
120122
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+
121133// ===----------------------------------------------------------------------===//
122134// Methods for UninitializedObjectChecker.
123135// ===----------------------------------------------------------------------===//
@@ -234,6 +246,13 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
234246 " One must also pass the pointee region as a parameter for "
235247 " dereferenceable fields!" );
236248
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+
237256 if (State->contains <AnalyzedRegions>(FR))
238257 return false ;
239258
@@ -246,13 +265,10 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
246265
247266 State = State->add <AnalyzedRegions>(FR);
248267
249- if (State->getStateManager ().getContext ().getSourceManager ().isInSystemHeader (
250- FR->getDecl ()->getLocation ()))
251- return false ;
252-
253268 UninitFieldMap::mapped_type NoteMsgBuf;
254269 llvm::raw_svector_ostream OS (NoteMsgBuf);
255270 Chain.printNoteMsg (OS);
271+
256272 return UninitFields.insert ({FR, std::move (NoteMsgBuf)}).second ;
257273}
258274
@@ -441,8 +457,8 @@ static const TypedValueRegion *
441457getConstructedRegion (const CXXConstructorDecl *CtorDecl,
442458 CheckerContext &Context) {
443459
444- Loc ThisLoc = Context. getSValBuilder (). getCXXThis (CtorDecl,
445- Context.getStackFrame ());
460+ Loc ThisLoc =
461+ Context. getSValBuilder (). getCXXThis (CtorDecl, Context.getStackFrame ());
446462
447463 SVal ObjectV = Context.getState ()->getSVal (ThisLoc);
448464
@@ -495,6 +511,75 @@ static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
495511 return false ;
496512}
497513
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+
498583std::string clang::ento::getVariableName (const FieldDecl *Field) {
499584 // If Field is a captured lambda variable, Field->getName() will return with
500585 // an empty string. We can however acquire it's name from the lambda's
@@ -528,12 +613,16 @@ void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
528613 ChOpts.IsPedantic =
529614 AnOpts.getCheckerBooleanOption (" Pedantic" , /* DefaultVal*/ false , Chk);
530615 ChOpts.ShouldConvertNotesToWarnings =
531- AnOpts.getCheckerBooleanOption (" NotesAsWarnings" , /* DefaultVal*/ false , Chk);
616+ AnOpts.getCheckerBooleanOption (" NotesAsWarnings" ,
617+ /* DefaultVal*/ false , Chk);
532618 ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption (
533619 " CheckPointeeInitialization" , /* DefaultVal*/ false , Chk);
534620 ChOpts.IgnoredRecordsWithFieldPattern =
535621 AnOpts.getCheckerStringOption (" IgnoreRecordsWithField" ,
536- /* DefaultVal*/ " " , Chk);
622+ /* DefaultVal*/ " " , Chk);
623+ ChOpts.IgnoreGuardedFields =
624+ AnOpts.getCheckerBooleanOption (" IgnoreGuardedFields" ,
625+ /* DefaultVal*/ false , Chk);
537626}
538627
539628bool ento::shouldRegisterUninitializedObjectChecker (const LangOptions &LO) {
0 commit comments