clang: lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

28

29using namespace clang;

32

33

34

36

37namespace {

38

39class UninitializedObjectChecker

40 : public Checker<check::EndFunction, check::DeadSymbols> {

41 const BugType BT_uninitField{this, "Uninitialized fields"};

42

43public:

44

46

49};

50

51

52

53class RegularField final : public FieldNode {

54public:

56

57 void printNoteMsg(llvm::raw_ostream &Out) const override {

58 Out << "uninitialized field ";

59 }

60

61 void printPrefix(llvm::raw_ostream &Out) const override {}

62

63 void printNode(llvm::raw_ostream &Out) const override {

65 }

66

67 void printSeparator(llvm::raw_ostream &Out) const override { Out << '.'; }

68};

69

70

71

72

73

74class BaseClass final : public FieldNode {

76

77public:

79 assert(T.isNull());

81 }

82

83 void printNoteMsg(llvm::raw_ostream &Out) const override {

84 llvm_unreachable("This node can never be the final node in the "

85 "fieldchain!");

86 }

87

88 void printPrefix(llvm::raw_ostream &Out) const override {}

89

90 void printNode(llvm::raw_ostream &Out) const override {

91 Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::";

92 }

93

94 void printSeparator(llvm::raw_ostream &Out) const override {}

95

96 bool isBase() const override { return true; }

97};

98

99}

100

101

102

103

104

108

109

110

111

114

115

116

118

119

120

121

122

123

124

125

126

128

129

130

131

132

133void UninitializedObjectChecker::checkEndFunction(

135

136 const auto *CtorDecl = dyn_cast_or_null(

137 Context.getLocationContext()->getDecl());

138 if (!CtorDecl)

139 return;

140

141 if (!CtorDecl->isUserProvided())

142 return;

143

144 if (CtorDecl->getParent()->isUnion())

145 return;

146

147

149 return;

150

152 if (!R)

153 return;

154

156

157 std::pair<ProgramStateRef, const UninitFieldMap &> UninitInfo =

158 F.getResults();

159

161 const UninitFieldMap &UninitFields = UninitInfo.second;

162

163 if (UninitFields.empty()) {

164 Context.addTransition(UpdatedState);

165 return;

166 }

167

168

169

170 ExplodedNode *Node = Context.generateNonFatalErrorNode(UpdatedState);

172 return;

173

175 const Stmt *CallSite = Context.getStackFrame()->getCallSite();

176 if (CallSite)

179

180

181

182 if (Opts.ShouldConvertNotesToWarnings) {

183 for (const auto &Pair : UninitFields) {

184

185 auto Report = std::make_unique(

186 BT_uninitField, Pair.second, Node, LocUsedForUniqueing,

187 Node->getLocationContext()->getDecl());

188 Context.emitReport(std::move(Report));

189 }

190 return;

191 }

192

194 llvm::raw_svector_ostream WarningOS(WarningBuf);

195 WarningOS << UninitFields.size() << " uninitialized field"

196 << (UninitFields.size() == 1 ? "" : "s")

197 << " at the end of the constructor call";

198

199 auto Report = std::make_unique(

200 BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,

201 Node->getLocationContext()->getDecl());

202

203 for (const auto &Pair : UninitFields) {

204 Report->addNote(Pair.second,

207 }

208 Context.emitReport(std::move(Report));

209}

210

211void UninitializedObjectChecker::checkDeadSymbols(SymbolReaper &SR,

214 for (const MemRegion *R : State->get()) {

216 State = State->remove(R);

217 }

218}

219

220

221

222

223

227 : State(State), ObjectR(R), Opts(Opts) {

228

229 isNonUnionUninit(ObjectR, FieldChainInfo(ChainFactory));

230

231

232

234 UninitFields.clear();

235}

236

237bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,

240

242 "One must also pass the pointee region as a parameter for "

243 "dereferenceable fields!");

244

245 if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(

247 return false;

248

250 return false;

251

252 if (State->contains(FR))

253 return false;

254

255 if (PointeeR) {

256 if (State->contains(PointeeR)) {

257 return false;

258 }

259 State = State->add(PointeeR);

260 }

261

262 State = State->add(FR);

263

264 UninitFieldMap::mapped_type NoteMsgBuf;

265 llvm::raw_svector_ostream OS(NoteMsgBuf);

267

268 return UninitFields.insert({FR, std::move(NoteMsgBuf)}).second;

269}

270

271bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,

275 "This method only checks non-union record objects!");

276

278

279 if (!RD) {

280 IsAnyFieldInitialized = true;

281 return true;

282 }

283

286 IsAnyFieldInitialized = true;

287 return false;

288 }

289

290 bool ContainsUninitField = false;

291

292

294

295 const auto FieldVal =

297 const auto *FR = FieldVal.getRegionAs<FieldRegion>();

299

300

301

302

304 return false;

305

307 if (isNonUnionUninit(FR, LocalChain.add(RegularField(FR))))

308 ContainsUninitField = true;

309 continue;

310 }

311

313 if (isUnionUninit(FR)) {

314 if (addFieldToUninits(LocalChain.add(RegularField(FR))))

315 ContainsUninitField = true;

316 } else

317 IsAnyFieldInitialized = true;

318 continue;

319 }

320

322 IsAnyFieldInitialized = true;

323 continue;

324 }

325

326 SVal V = State->getSVal(FieldVal);

327

329 if (isDereferencableUninit(FR, LocalChain))

330 ContainsUninitField = true;

331 continue;

332 }

333

335 if (isPrimitiveUninit(V)) {

336 if (addFieldToUninits(LocalChain.add(RegularField(FR))))

337 ContainsUninitField = true;

338 }

339 continue;

340 }

341

342 llvm_unreachable("All cases are handled!");

343 }

344

345

346

347 const auto *CXXRD = dyn_cast(RD);

348 if (!CXXRD)

349 return ContainsUninitField;

350

352 const auto *BaseRegion = State->getLValue(BaseSpec, R)

354 .getRegionAs();

355

356

357

359 if (isNonUnionUninit(BaseRegion, LocalChain.replaceHead(

360 BaseClass(BaseSpec.getType()))))

361 ContainsUninitField = true;

362 } else {

363 if (isNonUnionUninit(BaseRegion,

364 LocalChain.add(BaseClass(BaseSpec.getType()))))

365 ContainsUninitField = true;

366 }

367 }

368

369 return ContainsUninitField;

370}

371

372bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {

374 "This method only checks union objects!");

375

376 return false;

377}

378

379bool FindUninitializedFields::isPrimitiveUninit(SVal V) {

380 if (V.isUndef())

381 return true;

382

383 IsAnyFieldInitialized = true;

384 return false;

385}

386

387

388

389

390

393 if (Node.isSameRegion(FR))

394 return true;

395 }

396 return false;

397}

398

399

400

401

402

403static void printTail(llvm::raw_ostream &Out,

405

406

407

408

409

410

411

412

413

414

415

416

417

418

421 return;

422

424

426 Out << '\'';

427

429 Node.printPrefix(Out);

430

431 Out << "this->";

434 Out << '\'';

435}

436

439 if (L.isEmpty())

440 return;

441

443

444 L.getHead().printNode(Out);

445 L.getHead().printSeparator(Out);

446}

447

448

449

450

451

455

456 Loc ThisLoc =

457 Context.getSValBuilder().getCXXThis(CtorDecl, Context.getStackFrame());

458

459 SVal ObjectV = Context.getState()->getSVal(ThisLoc);

460

463 return nullptr;

464

465 return R;

466}

467

470

472 if (!CurrRegion)

473 return false;

474

475 const LocationContext *LC = Context.getLocationContext();

477

478

479 const auto *OtherCtor = dyn_cast(LC->getDecl());

480 if (!OtherCtor)

481 continue;

482

485 if (!OtherRegion)

486 continue;

487

488

489

491 return true;

492 }

493

494 return false;

495}

496

498 llvm::Regex R(Pattern);

499

501 if (R.match(FD->getType().getAsString()))

502 return true;

503 if (R.match(FD->getName()))

504 return true;

505 }

506

507 return false;

508}

509

511 if (isa(M))

512 return nullptr;

513

515 return nullptr;

516

518}

519

521

523 return true;

524

525 const auto *Parent = dyn_cast(FD->getParent());

526

528 return true;

529

531 assert(Parent && "The record's definition must be avaible if an uninitialized"

532 " field of it was found!");

533

534 ASTContext &AC = State->getStateManager().getContext();

535

537

539 hasAnyName("exit", "panic", "error", "Assert", "assert", "ziperr",

540 "assfail", "db_error", "__assert", "__assert2", "_wassert",

541 "__assert_rtn", "__assert_fail", "dtrace_assfail",

542 "yy_fatal_error", "_XCAssertionFailureHandler",

543 "_DTAssertionFailureHandler", "_TSAssertionFailureHandler"))));

544

546

547 auto GuardM =

549 NoReturnFuncM))

550 .bind("guard");

551

554 if (!MethodBody)

555 continue;

556

558 if (Accesses.empty())

559 continue;

560 const auto *FirstAccess = Accesses[0].getNodeAs<MemberExpr>("access");

561 assert(FirstAccess);

562

564 if (Guards.empty())

565 return true;

566 const auto *FirstGuard = Guards[0].getNodeAs<Stmt>("guard");

567 assert(FirstGuard);

568

569 if (FirstAccess->getBeginLoc() < FirstGuard->getBeginLoc())

570 return true;

571 }

572

573 return false;

574}

575

577

578

579

580 const auto *CXXParent = dyn_cast(Field->getParent());

581

582 if (CXXParent && CXXParent->isLambda()) {

583 assert(CXXParent->captures_begin());

584 auto It = CXXParent->captures_begin() + Field->getFieldIndex();

585

586 if (It->capturesVariable())

587 return llvm::Twine("/*captured variable*/" +

588 It->getCapturedVar()->getName())

589 .str();

590

591 if (It->capturesThis())

592 return "/*'this' capture*/";

593

594 llvm_unreachable("No other capture type is expected!");

595 }

596

597 return std::string(Field->getName());

598}

599

600void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {

601 auto Chk = Mgr.registerChecker();

602

605

606 ChOpts.IsPedantic = AnOpts.getCheckerBooleanOption(Chk, "Pedantic");

608 Chk, "NotesAsWarnings");

610 Chk, "CheckPointeeInitialization");

612 std::string(AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField"));

614 AnOpts.getCheckerBooleanOption(Chk, "IgnoreGuardedFields");

615

616 std::string ErrorMsg;

619 "a valid regex, building failed with error message "

620 "\"" + ErrorMsg + "\"");

621}

622

623bool ento::shouldRegisterUninitializedObjectChecker(const CheckerManager &mgr) {

624 return true;

625}

#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)

Declares an immutable set of type NameTy, suitable for placement into the ProgramState.

static const Stmt * getMethodBody(const CXXMethodDecl *M)

static const TypedValueRegion * getConstructedRegion(const CXXConstructorDecl *CtorDecl, CheckerContext &Context)

Returns the region that was constructed by CtorDecl, or nullptr if that isn't possible.

static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State)

Checks syntactically whether it is possible to access FD from the record that contains it without a p...

static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, CheckerContext &Context)

Checks whether the object constructed by Ctor will be analyzed later (e.g.

static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern)

Checks whether RD contains a field with a name or type name that matches Pattern.

static void printTail(llvm::raw_ostream &Out, const FieldChainInfo::FieldChain L)

Prints every element except the last to Out.

Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...

SourceManager & getSourceManager()

Stores options for the analyzer from the command line.

Represents a base class of a C++ class.

Represents a C++ constructor within a class.

Represents a static or instance method of a struct/union/class.

SourceLocation getLocation() const

AccessSpecifier getAccess() const

Represents a member of a struct/union/class.

const RecordDecl * getParent() const

Returns the parent of this field declaration, which is the struct in which this field is defined.

Stmt * getBody(const FunctionDecl *&Definition) const

Retrieve the body (definition) of the function.

FunctionDecl * getDefinition()

Get the definition for this declaration.

bool isDefined(const FunctionDecl *&Definition, bool CheckForPendingFriendDefinition=false) const

Returns true if the function has a definition that does not need to be instantiated.

It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...

const Decl * getDecl() const

const LocationContext * getParent() const

It might return null.

MemberExpr - [C99 6.5.2.3] Structure and Union Members.

A (possibly-)qualified type.

Represents a struct/union/class.

field_range fields() const

RecordDecl * getDefinition() const

Returns the RecordDecl that actually defines this struct/union/class.

ReturnStmt - This represents a return, optionally of an expression: return; return 4;.

Stmt - This represents one statement.

CXXRecordDecl * getAsCXXRecordDecl() const

Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...

bool isStructureOrClassType() const

bool isRecordType() const

RecordDecl * getAsRecordDecl() const

Retrieves the RecordDecl this type refers to.

const AnalyzerOptions & getAnalyzerOptions() const

CHECKER * registerChecker(AT &&... Args)

Used to register checkers.

void reportInvalidCheckerOptionValue(const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) const

Emits an error through a DiagnosticsEngine about an invalid user supplied checker option value.

Represents a field chain.

bool contains(const FieldRegion *FR) const

llvm::ImmutableList< const FieldNode & > FieldChain

const FieldNode & getHead() const

const FieldRegion * getUninitRegion() const

FieldChainInfo replaceHead(const FieldNodeT &FN)

Constructs a new FieldChainInfo object with FN as the new head of the list.

FieldChainInfo add(const FieldNodeT &FN)

Constructs a new FieldChainInfo object with FN appended.

void printNoteMsg(llvm::raw_ostream &Out) const

A lightweight polymorphic wrapper around FieldRegion *.

virtual bool isBase() const

virtual void printSeparator(llvm::raw_ostream &Out) const =0

Print the separator.

virtual void printPrefix(llvm::raw_ostream &Out) const =0

Print any prefixes before the fieldchain. Could contain casts, etc.

virtual void printNoteMsg(llvm::raw_ostream &Out) const =0

If this is the last element of the fieldchain, this method will print the note message associated wit...

const FieldDecl * getDecl() const

virtual void printNode(llvm::raw_ostream &Out) const =0

Print the node. Should contain the name of the field stored in FR.

LLVM_ATTRIBUTE_RETURNS_NONNULL const FieldDecl * getDecl() const override

Searches for and stores uninitialized fields in a non-union object.

FindUninitializedFields(ProgramStateRef State, const TypedValueRegion *const R, const UninitObjCheckerOptions &Opts)

Constructs the FindUninitializedField object, searches for and stores uninitialized fields in R.

bool isAnyFieldInitialized()

Returns whether the analyzed region contains at least one initialized field.

MemRegion - The root abstract class for all memory regions.

const RegionTy * getAs() const

static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)

Create a location for the beginning of the declaration.

static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)

Create a location corresponding to the given declaration.

SVal - This represents a symbolic expression, which can be either an L-value or an R-value.

const MemRegion * getAsRegion() const

bool isSubRegionOf(const MemRegion *R) const override

Check if the region is a subregion of the given region.

A class responsible for cleaning up unused symbols.

bool isLiveRegion(const MemRegion *region)

TypedValueRegion - An abstract class representing regions having a typed value.

virtual QualType getValueType() const =0

const internal::ArgumentAdaptingMatcherFunc< internal::HasDescendantMatcher > hasDescendant

Matches AST nodes that have descendant AST nodes that match the provided matcher.

const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr

Matches call expressions.

SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)

Returns the results of matching Matcher on Node.

const internal::VariadicFunction< internal::Matcher< NamedDecl >, StringRef, internal::hasAnyNameFunc > hasAnyName

Matches NamedDecl nodes that have any of the specified names.

const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl

Matches function declarations.

const internal::VariadicDynCastAllOfMatcher< Stmt, SwitchStmt > switchStmt

Matches switch statements.

const internal::VariadicDynCastAllOfMatcher< Stmt, MemberExpr > memberExpr

Matches member expressions.

internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)

Matches a node if the declaration associated with that node matches the given matcher.

const internal::VariadicAllOfMatcher< Stmt > stmt

Matches statements.

const internal::VariadicDynCastAllOfMatcher< Stmt, ConditionalOperator > conditionalOperator

Matches conditional operator expressions.

const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf

Matches if any of the given matchers matches.

const internal::VariadicDynCastAllOfMatcher< Stmt, IfStmt > ifStmt

Matches if statements.

std::string getVariableName(const FieldDecl *Field)

Returns with Field's name.

bool isPrimitiveType(const QualType &T)

Returns true if T is a primitive type.

@ OS

Indicates that the tracking object is a descendant of a referenced-counted OSObject,...

bool isDereferencableType(const QualType &T)

std::map< const FieldRegion *, llvm::SmallString< 50 > > UninitFieldMap

The JSON file list parser is used to communicate input to InstallAPI.

const FunctionProtoType * T

bool ShouldConvertNotesToWarnings

std::string IgnoredRecordsWithFieldPattern

bool CheckPointeeInitialization