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

1

2

3

4

5

6

7

8

9

10

11

12

27#include "llvm/ADT/Sequence.h"

28#include

29#include

30

31using namespace clang;

32using namespace ento;

33using namespace std::placeholders;

34

35

36

37

38

39namespace {

40

41struct FnDescription;

42

43

44

45

46

47

48

49struct StreamErrorState {

50

51 bool NoError = true;

52

53 bool FEof = false;

54

55 bool FError = false;

56

57 bool isNoError() const { return NoError && !FEof && !FError; }

58 bool isFEof() const { return !NoError && FEof && !FError; }

59 bool isFError() const { return !NoError && !FEof && FError; }

60

61 bool operator==(const StreamErrorState &ES) const {

62 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;

63 }

64

65 bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }

66

67 StreamErrorState operator|(const StreamErrorState &E) const {

68 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};

69 }

70

71 StreamErrorState operator&(const StreamErrorState &E) const {

72 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};

73 }

74

75 StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }

76

77

78 operator bool() const { return NoError || FEof || FError; }

79

80 LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }

81 LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &os) const {

82 os << "NoError: " << NoError << ", FEof: " << FEof

83 << ", FError: " << FError;

84 }

85

86 void Profile(llvm::FoldingSetNodeID &ID) const {

87 ID.AddBoolean(NoError);

88 ID.AddBoolean(FEof);

89 ID.AddBoolean(FError);

90 }

91};

92

93const StreamErrorState ErrorNone{true, false, false};

94const StreamErrorState ErrorFEof{false, true, false};

95const StreamErrorState ErrorFError{false, false, true};

96

97

98struct StreamState {

99

100

101 const FnDescription *LastOperation;

102

103

104 enum KindTy {

105 Opened,

106 Closed,

107 OpenFailed

108 } State;

109

110 StringRef getKindStr() const {

111 switch (State) {

112 case Opened:

113 return "Opened";

114 case Closed:

115 return "Closed";

116 case OpenFailed:

117 return "OpenFailed";

118 }

119 llvm_unreachable("Unknown StreamState!");

120 }

121

122

123

124 StreamErrorState const ErrorState;

125

126

127

128

129

130

131

132

133

134 bool const FilePositionIndeterminate = false;

135

136 StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,

137 bool IsFilePositionIndeterminate)

138 : LastOperation(L), State(S), ErrorState(ES),

139 FilePositionIndeterminate(IsFilePositionIndeterminate) {

140 assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&

141 "FilePositionIndeterminate should be false in FEof case.");

142 assert((State == Opened || ErrorState.isNoError()) &&

143 "ErrorState should be None in non-opened stream state.");

144 }

145

146 bool isOpened() const { return State == Opened; }

147 bool isClosed() const { return State == Closed; }

148 bool isOpenFailed() const { return State == OpenFailed; }

149

150 bool operator==(const StreamState &X) const {

151

152

153 return LastOperation == X.LastOperation && State == X.State &&

154 ErrorState == X.ErrorState &&

155 FilePositionIndeterminate == X.FilePositionIndeterminate;

156 }

157

158 static StreamState getOpened(const FnDescription *L,

159 const StreamErrorState &ES = ErrorNone,

160 bool IsFilePositionIndeterminate = false) {

161 return StreamState{L, Opened, ES, IsFilePositionIndeterminate};

162 }

163 static StreamState getClosed(const FnDescription *L) {

164 return StreamState{L, Closed, {}, false};

165 }

166 static StreamState getOpenFailed(const FnDescription *L) {

167 return StreamState{L, OpenFailed, {}, false};

168 }

169

170 LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }

171 LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &os) const;

172

173 void Profile(llvm::FoldingSetNodeID &ID) const {

174 ID.AddPointer(LastOperation);

175 ID.AddInteger(State);

176 ErrorState.Profile(ID);

177 ID.AddBoolean(FilePositionIndeterminate);

178 }

179};

180

181}

182

183

184

185

187

188

189

190

191

192namespace {

193

194class StreamChecker;

195using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,

197

198using ArgNoTy = unsigned int;

199static const ArgNoTy ArgNone = std::numeric_limits::max();

200

201const char *FeofNote = "Assuming stream reaches end-of-file here";

202const char *FerrorNote = "Assuming this stream operation fails";

203

204struct FnDescription {

205 FnCheck PreFn;

206 FnCheck EvalFn;

207 ArgNoTy StreamArgNo;

208};

209

210LLVM_DUMP_METHOD void StreamState::dumpToStream(llvm::raw_ostream &os) const {

211 os << "{Kind: " << getKindStr() << ", Last operation: " << LastOperation

212 << ", ErrorState: ";

213 ErrorState.dumpToStream(os);

214 os << ", FilePos: " << (FilePositionIndeterminate ? "Indeterminate" : "OK")

215 << '}';

216}

217

218

219

220SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {

221 assert(Desc && Desc->StreamArgNo != ArgNone &&

222 "Try to get a non-existing stream argument.");

223 return Call.getArgSVal(Desc->StreamArgNo);

224}

225

226

228 return C.getSValBuilder()

229 .conjureSymbolVal(nullptr, Elem, C.getLocationContext(),

230 C.blockCount())

231 .castAs();

232}

233

236 DefinedSVal RetVal = makeRetVal(C, Elem);

237 State = State->BindExpr(CE, C.getLocationContext(), RetVal);

238 State = State->assume(RetVal, true);

239 assert(State && "Assumption on new value should not fail.");

240 return State;

241}

242

244 CheckerContext &C, const CallExpr *CE) {

245 State = State->BindExpr(CE, C.getLocationContext(),

246 C.getSValBuilder().makeIntVal(Value, CE->getType()));

247 return State;

248}

249

250inline void assertStreamStateOpened(const StreamState *SS) {

251 assert(SS->isOpened() && "Stream is expected to be opened");

252}

253

254class StreamChecker : public Checker<check::PreCall, eval::Call,

255 check::DeadSymbols, check::PointerEscape,

256 check::ASTDecl> {

257 BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};

258 BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};

259 BugType BT_UseAfterOpenFailed{this, "Invalid stream",

260 "Stream handling error"};

261 BugType BT_IndeterminatePosition{this, "Invalid stream state",

262 "Stream handling error"};

263 BugType BT_IllegalWhence{this, "Illegal whence argument",

264 "Stream handling error"};

265 BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};

266 BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",

267 true};

268

269public:

270 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;

271 bool evalCall(const CallEvent &Call, CheckerContext &C) const;

272 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;

275 const CallEvent *Call,

277

278

279 void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &,

280 BugReporter &) const;

281

282 const BugType *getBT_StreamEof() const { return &BT_StreamEof; }

283 const BugType *getBT_IndeterminatePosition() const {

284 return &BT_IndeterminatePosition;

285 }

286

287

288

290 DefinedSVal RetVal,

291 CheckerContext &C) const;

292

293 const NoteTag *constructSetEofNoteTag(CheckerContext &C,

295 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {

297 &BR.getBugType() != this->getBT_StreamEof())

298 return "";

299

301

302 return FeofNote;

303 });

304 }

305

306 const NoteTag *constructSetErrorNoteTag(CheckerContext &C,

308 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {

310 &BR.getBugType() != this->getBT_IndeterminatePosition())

311 return "";

312

314

315 return FerrorNote;

316 });

317 }

318

319 const NoteTag *constructSetEofOrErrorNoteTag(CheckerContext &C,

321 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {

323 return "";

324

325 if (&BR.getBugType() == this->getBT_StreamEof()) {

327 return FeofNote;

328 }

329 if (&BR.getBugType() == this->getBT_IndeterminatePosition()) {

331 return FerrorNote;

332 }

333

334 return "";

335 });

336 }

337

338

339 bool TestMode = false;

340

341

342 bool PedanticMode = false;

343

344 const CallDescription FCloseDesc = {CDM::CLibrary, {"fclose"}, 1};

345

346private:

347 CallDescriptionMap FnDescriptions = {

348 {{CDM::CLibrary, {"fopen"}, 2},

349 {nullptr, &StreamChecker::evalFopen, ArgNone}},

350 {{CDM::CLibrary, {"fdopen"}, 2},

351 {nullptr, &StreamChecker::evalFopen, ArgNone}},

352 {{CDM::CLibrary, {"freopen"}, 3},

353 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},

354 {{CDM::CLibrary, {"tmpfile"}, 0},

355 {nullptr, &StreamChecker::evalFopen, ArgNone}},

356 {FCloseDesc, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},

357 {{CDM::CLibrary, {"fread"}, 4},

358 {&StreamChecker::preRead,

359 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},

360 {{CDM::CLibrary, {"fwrite"}, 4},

361 {&StreamChecker::preWrite,

362 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},

363 {{CDM::CLibrary, {"fgetc"}, 1},

364 {&StreamChecker::preRead,

365 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},

366 {{CDM::CLibrary, {"fgets"}, 3},

367 {&StreamChecker::preRead,

368 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},

369 {{CDM::CLibrary, {"getc"}, 1},

370 {&StreamChecker::preRead,

371 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},

372 {{CDM::CLibrary, {"fputc"}, 2},

373 {&StreamChecker::preWrite,

374 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},

375 {{CDM::CLibrary, {"fputs"}, 2},

376 {&StreamChecker::preWrite,

377 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},

378 {{CDM::CLibrary, {"putc"}, 2},

379 {&StreamChecker::preWrite,

380 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},

381 {{CDM::CLibrary, {"fprintf"}},

382 {&StreamChecker::preWrite,

383 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},

384 {{CDM::CLibrary, {"vfprintf"}, 3},

385 {&StreamChecker::preWrite,

386 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},

387 {{CDM::CLibrary, {"fscanf"}},

388 {&StreamChecker::preRead,

389 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},

390 {{CDM::CLibrary, {"vfscanf"}, 3},

391 {&StreamChecker::preRead,

392 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},

393 {{CDM::CLibrary, {"ungetc"}, 2},

394 {&StreamChecker::preWrite,

395 std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},

396 {{CDM::CLibrary, {"getdelim"}, 4},

397 {&StreamChecker::preRead,

398 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}},

399 {{CDM::CLibrary, {"getline"}, 3},

400 {&StreamChecker::preRead,

401 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}},

402 {{CDM::CLibrary, {"fseek"}, 3},

403 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},

404 {{CDM::CLibrary, {"fseeko"}, 3},

405 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},

406 {{CDM::CLibrary, {"ftell"}, 1},

407 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},

408 {{CDM::CLibrary, {"ftello"}, 1},

409 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},

410 {{CDM::CLibrary, {"fflush"}, 1},

411 {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},

412 {{CDM::CLibrary, {"rewind"}, 1},

413 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},

414 {{CDM::CLibrary, {"fgetpos"}, 2},

415 {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}},

416 {{CDM::CLibrary, {"fsetpos"}, 2},

417 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},

418 {{CDM::CLibrary, {"clearerr"}, 1},

419 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},

420 {{CDM::CLibrary, {"feof"}, 1},

421 {&StreamChecker::preDefault,

422 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),

423 0}},

424 {{CDM::CLibrary, {"ferror"}, 1},

425 {&StreamChecker::preDefault,

426 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),

427 0}},

428 {{CDM::CLibrary, {"fileno"}, 1},

429 {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}},

430 };

431

432 CallDescriptionMap FnTestDescriptions = {

433 {{CDM::SimpleFunc, {"StreamTesterChecker_make_feof_stream"}, 1},

434 {nullptr,

435 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof,

436 false),

437 0}},

438 {{CDM::SimpleFunc, {"StreamTesterChecker_make_ferror_stream"}, 1},

439 {nullptr,

440 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,

441 ErrorFError, false),

442 0}},

443 {{CDM::SimpleFunc,

444 {"StreamTesterChecker_make_ferror_indeterminate_stream"},

445 1},

446 {nullptr,

447 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,

448 ErrorFError, true),

449 0}},

450 };

451

452

453 mutable std::optional EofVal;

454

455 mutable int SeekSetVal = 0;

456

457 mutable int SeekCurVal = 1;

458

459 mutable int SeekEndVal = 2;

460

461 mutable QualType VaListType;

462

463 mutable const VarDecl *StdinDecl = nullptr;

464 mutable const VarDecl *StdoutDecl = nullptr;

465 mutable const VarDecl *StderrDecl = nullptr;

466

467 void evalFopen(const FnDescription *Desc, const CallEvent &Call,

468 CheckerContext &C) const;

469

470 void preFreopen(const FnDescription *Desc, const CallEvent &Call,

471 CheckerContext &C) const;

472 void evalFreopen(const FnDescription *Desc, const CallEvent &Call,

473 CheckerContext &C) const;

474

475 void evalFclose(const FnDescription *Desc, const CallEvent &Call,

476 CheckerContext &C) const;

477

478 void preRead(const FnDescription *Desc, const CallEvent &Call,

479 CheckerContext &C) const;

480

481 void preWrite(const FnDescription *Desc, const CallEvent &Call,

482 CheckerContext &C) const;

483

484 void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,

485 CheckerContext &C, bool IsFread) const;

486

487 void evalFgetx(const FnDescription *Desc, const CallEvent &Call,

488 CheckerContext &C, bool SingleChar) const;

489

490 void evalFputx(const FnDescription *Desc, const CallEvent &Call,

491 CheckerContext &C, bool IsSingleChar) const;

492

493 void evalFprintf(const FnDescription *Desc, const CallEvent &Call,

494 CheckerContext &C) const;

495

496 void evalFscanf(const FnDescription *Desc, const CallEvent &Call,

497 CheckerContext &C) const;

498

499 void evalUngetc(const FnDescription *Desc, const CallEvent &Call,

500 CheckerContext &C) const;

501

502 void evalGetdelim(const FnDescription *Desc, const CallEvent &Call,

503 CheckerContext &C) const;

504

505 void preFseek(const FnDescription *Desc, const CallEvent &Call,

506 CheckerContext &C) const;

507 void evalFseek(const FnDescription *Desc, const CallEvent &Call,

508 CheckerContext &C) const;

509

510 void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,

511 CheckerContext &C) const;

512

513 void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,

514 CheckerContext &C) const;

515

516 void evalFtell(const FnDescription *Desc, const CallEvent &Call,

517 CheckerContext &C) const;

518

519 void evalRewind(const FnDescription *Desc, const CallEvent &Call,

520 CheckerContext &C) const;

521

522 void preDefault(const FnDescription *Desc, const CallEvent &Call,

523 CheckerContext &C) const;

524

525 void evalClearerr(const FnDescription *Desc, const CallEvent &Call,

526 CheckerContext &C) const;

527

528 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,

529 CheckerContext &C,

530 const StreamErrorState &ErrorKind) const;

531

532 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,

533 CheckerContext &C, const StreamErrorState &ErrorKind,

534 bool Indeterminate) const;

535

536 void preFflush(const FnDescription *Desc, const CallEvent &Call,

537 CheckerContext &C) const;

538

539 void evalFflush(const FnDescription *Desc, const CallEvent &Call,

540 CheckerContext &C) const;

541

542 void evalFileno(const FnDescription *Desc, const CallEvent &Call,

543 CheckerContext &C) const;

544

545

546

547

548

549 ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,

550 CheckerContext &C,

552

553

554

555

556 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,

558

559

560

561

562

563

564

566 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,

568

569

570

571

572

573 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,

575

576

577

578

579

580 void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,

582

583

584

585

586 ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,

587 CheckerContext &C, ExplodedNode *Pred) const;

588

589

590

591 const FnDescription *lookupFn(const CallEvent &Call) const {

592

593

594 for (auto *P : Call.parameters()) {

595 QualType T = P->getType();

597 T.getCanonicalType() != VaListType)

598 return nullptr;

599 }

600

602 }

603

604

605

606 const NoteTag *constructLeakNoteTag(CheckerContext &C, SymbolRef StreamSym,

607 const std::string &Message) const {

608 return C.getNoteTag([this, StreamSym,

609 Message](PathSensitiveBugReport &BR) -> std::string {

612 return "";

613 });

614 }

615

616 void initMacroValues(const Preprocessor &PP) const {

617 if (EofVal)

618 return;

619

621 EofVal = *OptInt;

622 else

623 EofVal = -1;

624 if (const std::optional OptInt = tryExpandAsInteger("SEEK_SET", PP))

625 SeekSetVal = *OptInt;

626 if (const std::optional OptInt = tryExpandAsInteger("SEEK_END", PP))

627 SeekEndVal = *OptInt;

628 if (const std::optional OptInt = tryExpandAsInteger("SEEK_CUR", PP))

629 SeekCurVal = *OptInt;

630 }

631

632

633

634 static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,

636 CheckerContext &C);

637};

638

639struct StreamOperationEvaluator {

640 SValBuilder &SVB;

641 const ASTContext &ACtx;

642

644 const StreamState *SS = nullptr;

645 const CallExpr *CE = nullptr;

646 std::optional Elem;

647 StreamErrorState NewES;

648

649 StreamOperationEvaluator(CheckerContext &C)

650 : SVB(C.getSValBuilder()), ACtx(C.getASTContext()) {

651 ;

652 }

653

654 bool Init(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C,

656 StreamSym = getStreamArg(Desc, Call).getAsSymbol();

657 if (!StreamSym)

658 return false;

659 SS = State->get(StreamSym);

660 if (!SS)

661 return false;

662 NewES = SS->ErrorState;

663 CE = dyn_cast_or_null(Call.getOriginExpr());

664 if (!CE)

665 return false;

666 Elem = Call.getCFGElementRef();

667

668 assertStreamStateOpened(SS);

669

670 return true;

671 }

672

673 bool isStreamEof() const { return SS->ErrorState == ErrorFEof; }

674

675 NonLoc getZeroVal(const CallEvent &Call) {

677 }

678

680 const StreamState &NewSS) {

681 NewES = NewSS.ErrorState;

682 return State->set(StreamSym, NewSS);

683 }

684

686 NonLoc RetVal = makeRetVal(C, Elem.value()).castAs();

687 return State->BindExpr(CE, C.getLocationContext(), RetVal);

688 }

689

691 uint64_t Val) {

692 return State->BindExpr(CE, C.getLocationContext(),

694 }

695

697 SVal Val) {

698 return State->BindExpr(CE, C.getLocationContext(), Val);

699 }

700

702 CheckerContext &C) {

703 return State->BindExpr(CE, C.getLocationContext(),

704 C.getSValBuilder().makeNullWithType(CE->getType()));

705 }

706

709 NonLoc RHS) {

711 .getAs();

713 return nullptr;

714 return State->assume(*Cond, true);

715 }

716

718 makeRetValAndAssumeDual(ProgramStateRef State, CheckerContext &C) {

719 DefinedSVal RetVal = makeRetVal(C, Elem.value());

720 State = State->BindExpr(CE, C.getLocationContext(), RetVal);

721 return C.getConstraintManager().assumeDual(State, RetVal);

722 }

723

724 const NoteTag *getFailureNoteTag(const StreamChecker *Ch, CheckerContext &C) {

725 bool SetFeof = NewES.FEof && !SS->ErrorState.FEof;

726 bool SetFerror = NewES.FError && !SS->ErrorState.FError;

727 if (SetFeof && !SetFerror)

728 return Ch->constructSetEofNoteTag(C, StreamSym);

729 if (!SetFeof && SetFerror)

730 return Ch->constructSetErrorNoteTag(C, StreamSym);

731 if (SetFeof && SetFerror)

732 return Ch->constructSetEofOrErrorNoteTag(C, StreamSym);

733 return nullptr;

734 }

735};

736

737}

738

739

740

741

742

743namespace {

744class NoStreamStateChangeVisitor final : public NoOwnershipChangeVisitor {

745protected:

746

747

748

749

750

751 bool isClosingCallAsWritten(const CallExpr &Call) const {

752 const auto *StreamChk = static_cast<const StreamChecker *>(&Checker);

754 }

755

756 bool doesFnIntendToHandleOwnership(const Decl *Callee,

757 ASTContext &ACtx) final {

758 const FunctionDecl *FD = dyn_cast(Callee);

759

760

761

762

763

764

765

766 if (!FD || !FD->hasBody())

767 return false;

768 using namespace clang::ast_matchers;

769

770 auto Matches =

772 for (BoundNodes Match : Matches) {

773 if (const auto *Call = Match.getNodeAs("call"))

774 if (isClosingCallAsWritten(*Call))

775 return true;

776 }

777

778

779 return false;

780 }

781

782 bool hasResourceStateChanged(ProgramStateRef CallEnterState,

784 return CallEnterState->get(Sym) !=

785 CallExitEndState->get(Sym);

786 }

787

791 N->getState()->getStateManager().getContext().getSourceManager());

792 return std::make_shared(

793 L, "Returning without closing stream object or storing it for later "

794 "release");

795 }

796

797public:

798 NoStreamStateChangeVisitor(SymbolRef Sym, const StreamChecker *Checker)

799 : NoOwnershipChangeVisitor(Sym, Checker) {}

800};

801

802}

803

804const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,

806 CheckerContext &C) {

808

809

810 if (!State->get(StreamSym))

812

813 const ExplodedNode *Pred = N;

814 while (N) {

816 if (!State->get(StreamSym))

817 return Pred;

818 Pred = N;

820 }

821

822 return nullptr;

823}

824

827 if (const llvm::APSInt *Int = SVB.getKnownValue(State, V))

828 return Int->tryExtValue();

829 return std::nullopt;

830}

831

832

833

834

837 unsigned BlockCount, const SubRegion *Buffer,

838 QualType ElemType, int64_t StartIndex,

839 int64_t ElementCount) {

840 constexpr auto DoNotInvalidateSuperRegion =

841 RegionAndSymbolInvalidationTraits::InvalidationKinds::

842 TK_DoNotInvalidateSuperRegion;

843

845 const ASTContext &Ctx = State->getStateManager().getContext();

848

850 EscapingVals.reserve(ElementCount);

851

853 for (auto Idx : llvm::seq(StartIndex, StartIndex + ElementCount)) {

855 const auto *Element =

856 RegionManager.getElementRegion(ElemType, Index, Buffer, Ctx);

858 ITraits.setTrait(Element, DoNotInvalidateSuperRegion);

859 }

860 return State->invalidateRegions(

861 EscapingVals, Call.getCFGElementRef(), BlockCount, LCtx,

862 false,

863 nullptr, &Call, &ITraits);

864}

865

869 auto GetArgSVal = [&Call](int Idx) { return Call.getArgSVal(Idx); };

870 auto EscapingVals = to_vector(map_range(EscapingArgs, GetArgSVal));

871 State = State->invalidateRegions(EscapingVals, Call.getCFGElementRef(),

872 C.blockCount(), C.getLocationContext(),

873 false,

874 nullptr);

875 return State;

876}

877

878

879

880

881

882void StreamChecker::checkPreCall(const CallEvent &Call,

883 CheckerContext &C) const {

884 const FnDescription *Desc = lookupFn(Call);

885 if (!Desc || !Desc->PreFn)

886 return;

887

888 Desc->PreFn(this, Desc, Call, C);

889}

890

891bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {

892 const FnDescription *Desc = lookupFn(Call);

893 if (!Desc && TestMode)

894 Desc = FnTestDescriptions.lookup(Call);

895 if (!Desc || !Desc->EvalFn)

896 return false;

897

898 Desc->EvalFn(this, Desc, Call, C);

899

900 return C.isDifferent();

901}

902

903ProgramStateRef StreamChecker::assumeNoAliasingWithStdStreams(

904 ProgramStateRef State, DefinedSVal RetVal, CheckerContext &C) const {

907 if (!Var)

908 return State;

909 const auto *LCtx = C.getLocationContext();

910 auto &StoreMgr = C.getStoreManager();

911 auto &SVB = C.getSValBuilder();

912 SVal VarValue = State->getSVal(StoreMgr.getLValueVar(Var, LCtx));

913 auto NoAliasState =

915 .castAs();

916 return State->assume(NoAliasState, true);

917 };

918

919 assert(State);

920 State = assumeRetNE(State, StdinDecl);

921 State = assumeRetNE(State, StdoutDecl);

922 State = assumeRetNE(State, StderrDecl);

923 assert(State);

924 return State;

925}

926

927void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,

928 CheckerContext &C) const {

930 const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr());

931 if (!CE)

932 return;

933

934 DefinedSVal RetVal = makeRetVal(C, Call.getCFGElementRef());

936 assert(RetSym && "RetVal must be a symbol here.");

937

938 State = State->BindExpr(CE, C.getLocationContext(), RetVal);

939

940

941

943 std::tie(StateNotNull, StateNull) =

944 C.getConstraintManager().assumeDual(State, RetVal);

945

946 StateNotNull =

947 StateNotNull->set(RetSym, StreamState::getOpened(Desc));

948 StateNull =

949 StateNull->set(RetSym, StreamState::getOpenFailed(Desc));

950

951 StateNotNull = assumeNoAliasingWithStdStreams(StateNotNull, RetVal, C);

952

953 C.addTransition(StateNotNull,

954 constructLeakNoteTag(C, RetSym, "Stream opened here"));

955 C.addTransition(StateNull);

956}

957

958void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,

959 CheckerContext &C) const {

960

962 State = ensureStreamNonNull(getStreamArg(Desc, Call),

963 Call.getArgExpr(Desc->StreamArgNo), C, State);

964 if (!State)

965 return;

966

967 C.addTransition(State);

968}

969

970void StreamChecker::evalFreopen(const FnDescription *Desc,

971 const CallEvent &Call,

972 CheckerContext &C) const {

974

975 auto *CE = dyn_cast_or_null(Call.getOriginExpr());

976 if (!CE)

977 return;

978

979 std::optional StreamVal =

980 getStreamArg(Desc, Call).getAs();

981 if (!StreamVal)

982 return;

983

984 SymbolRef StreamSym = StreamVal->getAsSymbol();

985

986

987 if (!StreamSym)

988 return;

989

990

991 if (!State->get(StreamSym))

992 return;

993

994

995

996

997

999 State->BindExpr(CE, C.getLocationContext(), *StreamVal);

1000

1001

1003 State->BindExpr(CE, C.getLocationContext(),

1004 C.getSValBuilder().makeNullWithType(CE->getType()));

1005

1006 StateRetNotNull =

1007 StateRetNotNull->set(StreamSym, StreamState::getOpened(Desc));

1008 StateRetNull =

1009 StateRetNull->set(StreamSym, StreamState::getOpenFailed(Desc));

1010

1011 C.addTransition(StateRetNotNull,

1012 constructLeakNoteTag(C, StreamSym, "Stream reopened here"));

1013 C.addTransition(StateRetNull);

1014}

1015

1016void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,

1017 CheckerContext &C) const {

1019 StreamOperationEvaluator E(C);

1020 if (!E.Init(Desc, Call, C, State))

1021 return;

1022

1023

1024

1025

1026 State = E.setStreamState(State, StreamState::getClosed(Desc));

1027

1028

1029 C.addTransition(E.bindReturnValue(State, C, 0));

1030 C.addTransition(E.bindReturnValue(State, C, *EofVal));

1031}

1032

1033void StreamChecker::preRead(const FnDescription *Desc, const CallEvent &Call,

1034 CheckerContext &C) const {

1036 SVal StreamVal = getStreamArg(Desc, Call);

1037 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,

1038 State);

1039 if (!State)

1040 return;

1041 State = ensureStreamOpened(StreamVal, C, State);

1042 if (!State)

1043 return;

1044 State = ensureNoFilePositionIndeterminate(StreamVal, C, State);

1045 if (!State)

1046 return;

1047

1049 if (Sym && State->get(Sym)) {

1050 const StreamState *SS = State->get(Sym);

1051 if (SS->ErrorState & ErrorFEof)

1052 reportFEofWarning(Sym, C, State);

1053 } else {

1054 C.addTransition(State);

1055 }

1056}

1057

1058void StreamChecker::preWrite(const FnDescription *Desc, const CallEvent &Call,

1059 CheckerContext &C) const {

1061 SVal StreamVal = getStreamArg(Desc, Call);

1062 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,

1063 State);

1064 if (!State)

1065 return;

1066 State = ensureStreamOpened(StreamVal, C, State);

1067 if (!State)

1068 return;

1069 State = ensureNoFilePositionIndeterminate(StreamVal, C, State);

1070 if (!State)

1071 return;

1072

1073 C.addTransition(State);

1074}

1075

1077 if (!R)

1078 return {};

1079 if (const auto *ER = dyn_cast(R))

1080 return ER->getElementType();

1081 if (const auto *TR = dyn_cast(R))

1082 return TR->getValueType();

1083 if (const auto *SR = dyn_cast(R))

1084 return SR->getPointeeStaticType();

1085 return {};

1086}

1087

1090 if (!R)

1091 return std::nullopt;

1092

1093 auto Zero = [&SVB] {

1096 };

1097

1098 if (const auto *ER = dyn_cast(R))

1099 return ER->getIndex();

1101 return Zero();

1103 return Zero();

1104 return std::nullopt;

1105}

1106

1111

1112 const auto *Buffer =

1113 dyn_cast_or_null(Call.getArgSVal(0).getAsRegion());

1114

1115 const ASTContext &Ctx = C.getASTContext();

1117 std::optional StartElementIndex =

1119

1120

1121 if (const auto *ER = dyn_cast_or_null(Buffer))

1122 Buffer = dyn_cast(ER->getSuperRegion());

1123

1124 std::optional<int64_t> CountVal = getKnownValue(State, NMembVal);

1125 std::optional<int64_t> Size = getKnownValue(State, SizeVal);

1126 std::optional<int64_t> StartIndexVal =

1128

1129 if (!ElemTy.isNull() && CountVal && Size && StartIndexVal) {

1130 int64_t NumBytesRead = Size.value() * CountVal.value();

1132 if (ElemSizeInChars == 0 || NumBytesRead < 0)

1133 return nullptr;

1134

1135 bool IncompleteLastElement = (NumBytesRead % ElemSizeInChars) != 0;

1136 int64_t NumCompleteOrIncompleteElementsRead =

1137 NumBytesRead / ElemSizeInChars + IncompleteLastElement;

1138

1139 constexpr int MaxInvalidatedElementsLimit = 64;

1140 if (NumCompleteOrIncompleteElementsRead <= MaxInvalidatedElementsLimit) {

1142 ElemTy, *StartIndexVal,

1143 NumCompleteOrIncompleteElementsRead);

1144 }

1145 }

1146 return nullptr;

1147}

1148

1149void StreamChecker::evalFreadFwrite(const FnDescription *Desc,

1150 const CallEvent &Call, CheckerContext &C,

1151 bool IsFread) const {

1153 StreamOperationEvaluator E(C);

1154 if (!E.Init(Desc, Call, C, State))

1155 return;

1156

1157 std::optional SizeVal = Call.getArgSVal(1).getAs();

1158 if (!SizeVal)

1159 return;

1160 std::optional NMembVal = Call.getArgSVal(2).getAs();

1161 if (!NMembVal)

1162 return;

1163

1164

1165

1166

1167

1168

1169 if (State->isNull(*SizeVal).isConstrainedTrue() ||

1170 State->isNull(*NMembVal).isConstrainedTrue()) {

1171

1172

1173 C.addTransition(E.bindReturnValue(State, C, 0));

1174 return;

1175 }

1176

1177

1178

1179 if (IsFread && !E.isStreamEof()) {

1180

1181

1183 State, C, Call, *SizeVal, *NMembVal);

1184 State =

1185 InvalidatedState ? InvalidatedState : escapeArgs(State, C, Call, {0});

1186 }

1187

1188

1189

1190 if (!IsFread || !E.isStreamEof()) {

1192 State->BindExpr(E.CE, C.getLocationContext(), *NMembVal);

1193 StateNotFailed =

1194 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));

1195 C.addTransition(StateNotFailed);

1196 }

1197

1198

1199

1200 if (!IsFread && !PedanticMode)

1201 return;

1202

1203 NonLoc RetVal = makeRetVal(C, E.Elem.value()).castAs();

1205 State->BindExpr(E.CE, C.getLocationContext(), RetVal);

1206 StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal);

1207 if (!StateFailed)

1208 return;

1209

1210 StreamErrorState NewES;

1211 if (IsFread)

1212 NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;

1213 else

1214 NewES = ErrorFError;

1215

1216

1217 StateFailed = E.setStreamState(

1218 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));

1219 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));

1220}

1221

1222void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,

1223 CheckerContext &C, bool SingleChar) const {

1224

1225

1226

1228 StreamOperationEvaluator E(C);

1229 if (!E.Init(Desc, Call, C, State))

1230 return;

1231

1232 if (!E.isStreamEof()) {

1233

1234

1236 if (SingleChar) {

1237

1238 NonLoc RetVal = makeRetVal(C, E.Elem.value()).castAs();

1240 State->BindExpr(E.CE, C.getLocationContext(), RetVal);

1241

1242

1243 StateNotFailed = StateNotFailed->assumeInclusiveRange(

1244 RetVal,

1245 E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy),

1246 E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy),

1247 true);

1248 if (!StateNotFailed)

1249 return;

1250 C.addTransition(StateNotFailed);

1251 } else {

1252

1253 std::optional GetBuf =

1254 Call.getArgSVal(0).getAs();

1255 if (!GetBuf)

1256 return;

1258 State->BindExpr(E.CE, C.getLocationContext(), *GetBuf);

1259 StateNotFailed =

1260 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));

1261 C.addTransition(StateNotFailed);

1262 }

1263 }

1264

1265

1267 if (SingleChar)

1268 StateFailed = E.bindReturnValue(State, C, *EofVal);

1269 else

1270 StateFailed = E.bindNullReturnValue(State, C);

1271

1272

1273

1274 StreamErrorState NewES =

1275 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;

1276 StateFailed = E.setStreamState(

1277 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));

1278 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));

1279}

1280

1281void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,

1282 CheckerContext &C, bool IsSingleChar) const {

1283

1284

1285

1287 StreamOperationEvaluator E(C);

1288 if (!E.Init(Desc, Call, C, State))

1289 return;

1290

1291 if (IsSingleChar) {

1292

1293 std::optional PutVal = Call.getArgSVal(0).getAs();

1294 if (!PutVal)

1295 return;

1297 State->BindExpr(E.CE, C.getLocationContext(), *PutVal);

1298 StateNotFailed =

1299 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));

1300 C.addTransition(StateNotFailed);

1301 } else {

1302

1303 NonLoc RetVal = makeRetVal(C, E.Elem.value()).castAs();

1305 State->BindExpr(E.CE, C.getLocationContext(), RetVal);

1306 StateNotFailed =

1307 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));

1308 if (!StateNotFailed)

1309 return;

1310 StateNotFailed =

1311 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));

1312 C.addTransition(StateNotFailed);

1313 }

1314

1315 if (!PedanticMode)

1316 return;

1317

1318

1319

1320 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);

1321 StateFailed = E.setStreamState(

1322 StateFailed, StreamState::getOpened(Desc, ErrorFError, true));

1323 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));

1324}

1325

1326void StreamChecker::evalFprintf(const FnDescription *Desc,

1327 const CallEvent &Call,

1328 CheckerContext &C) const {

1329 if (Call.getNumArgs() < 2)

1330 return;

1331

1333 StreamOperationEvaluator E(C);

1334 if (!E.Init(Desc, Call, C, State))

1335 return;

1336

1337 NonLoc RetVal = makeRetVal(C, E.Elem.value()).castAs();

1338 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);

1340 E.SVB

1341 .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy),

1342 E.SVB.getConditionType())

1343 .getAs();

1345 return;

1347 std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);

1348

1349 StateNotFailed =

1350 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));

1351 C.addTransition(StateNotFailed);

1352

1353 if (!PedanticMode)

1354 return;

1355

1356

1357

1358 StateFailed = E.setStreamState(

1359 StateFailed, StreamState::getOpened(Desc, ErrorFError, true));

1360 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));

1361}

1362

1363void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,

1364 CheckerContext &C) const {

1365 if (Call.getNumArgs() < 2)

1366 return;

1367

1369 StreamOperationEvaluator E(C);

1370 if (!E.Init(Desc, Call, C, State))

1371 return;

1372

1373

1374

1375

1376

1377

1378

1379

1380

1381 if (!E.isStreamEof()) {

1382 NonLoc RetVal = makeRetVal(C, E.Elem.value()).castAs();

1384 State->BindExpr(E.CE, C.getLocationContext(), RetVal);

1385 StateNotFailed =

1386 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));

1387 if (!StateNotFailed)

1388 return;

1389

1390 if (auto const *Callee = Call.getCalleeIdentifier();

1391 !Callee || Callee->getName() != "vfscanf") {

1392 SmallVector EscArgs;

1393 for (auto EscArg : llvm::seq(2u, Call.getNumArgs()))

1394 EscArgs.push_back(EscArg);

1395 StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs);

1396 }

1397

1398 if (StateNotFailed)

1399 C.addTransition(StateNotFailed);

1400 }

1401

1402

1403

1404

1405

1406

1407

1408 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);

1409 StreamErrorState NewES =

1410 E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError;

1411 StateFailed = E.setStreamState(

1412 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));

1413 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));

1414}

1415

1416void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call,

1417 CheckerContext &C) const {

1419 StreamOperationEvaluator E(C);

1420 if (!E.Init(Desc, Call, C, State))

1421 return;

1422

1423

1424 std::optional PutVal = Call.getArgSVal(0).getAs();

1425 if (!PutVal)

1426 return;

1427 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, *PutVal);

1428 StateNotFailed =

1429 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));

1430 C.addTransition(StateNotFailed);

1431

1432

1433

1434

1435

1436

1437

1438 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);

1439 StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc));

1440 C.addTransition(StateFailed);

1441}

1442

1443void StreamChecker::evalGetdelim(const FnDescription *Desc,

1444 const CallEvent &Call,

1445 CheckerContext &C) const {

1447 StreamOperationEvaluator E(C);

1448 if (!E.Init(Desc, Call, C, State))

1449 return;

1450

1451

1452

1453

1454

1455

1456

1457 if (!E.isStreamEof()) {

1458

1459

1461

1462

1463 NonLoc RetVal = makeRetVal(C, E.Elem.value()).castAs();

1464 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, RetVal);

1465 StateNotFailed =

1466 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));

1467

1468

1471 StateNotFailed = StateNotFailed->assume(

1472 NewLinePtr->castAs(), true);

1473

1474

1475

1476 SVal SizePtrSval = Call.getArgSVal(1);

1479 StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,

1480 NVal->castAs(), RetVal);

1481 StateNotFailed = E.bindReturnValue(StateNotFailed, C, RetVal);

1482 }

1483 if (!StateNotFailed)

1484 return;

1485 C.addTransition(StateNotFailed);

1486 }

1487

1488

1489

1490

1491 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);

1492 StreamErrorState NewES =

1493 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;

1494 StateFailed = E.setStreamState(

1495 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));

1496

1498 StateFailed = StateFailed->bindLoc(*NewLinePtr, UndefinedVal(),

1499 C.getLocationContext());

1500 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));

1501}

1502

1503void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,

1504 CheckerContext &C) const {

1506 SVal StreamVal = getStreamArg(Desc, Call);

1507 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,

1508 State);

1509 if (!State)

1510 return;

1511 State = ensureStreamOpened(StreamVal, C, State);

1512 if (!State)

1513 return;

1514 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);

1515 if (!State)

1516 return;

1517

1518 C.addTransition(State);

1519}

1520

1521void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,

1522 CheckerContext &C) const {

1524 StreamOperationEvaluator E(C);

1525 if (!E.Init(Desc, Call, C, State))

1526 return;

1527

1528

1529 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, 0);

1530

1531 StateNotFailed =

1532 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));

1533 C.addTransition(StateNotFailed);

1534

1535 if (!PedanticMode)

1536 return;

1537

1538

1539

1540

1541

1542

1543

1544 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);

1545 StateFailed = E.setStreamState(

1546 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));

1547 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));

1548}

1549

1550void StreamChecker::evalFgetpos(const FnDescription *Desc,

1551 const CallEvent &Call,

1552 CheckerContext &C) const {

1554 StreamOperationEvaluator E(C);

1555 if (!E.Init(Desc, Call, C, State))

1556 return;

1557

1559 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);

1560 StateNotFailed = escapeArgs(StateNotFailed, C, Call, {1});

1561

1562

1563

1564

1565 C.addTransition(StateNotFailed);

1566 C.addTransition(StateFailed);

1567}

1568

1569void StreamChecker::evalFsetpos(const FnDescription *Desc,

1570 const CallEvent &Call,

1571 CheckerContext &C) const {

1573 StreamOperationEvaluator E(C);

1574 if (!E.Init(Desc, Call, C, State))

1575 return;

1576

1578 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);

1579

1580 StateNotFailed = E.setStreamState(

1581 StateNotFailed, StreamState::getOpened(Desc, ErrorNone, false));

1582 C.addTransition(StateNotFailed);

1583

1584 if (!PedanticMode)

1585 return;

1586

1587

1588

1589

1590

1591 StateFailed = E.setStreamState(

1592 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));

1593

1594 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));

1595}

1596

1597void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,

1598 CheckerContext &C) const {

1600 StreamOperationEvaluator E(C);

1601 if (!E.Init(Desc, Call, C, State))

1602 return;

1603

1604 NonLoc RetVal = makeRetVal(C, E.Elem.value()).castAs();

1606 State->BindExpr(E.CE, C.getLocationContext(), RetVal);

1607 StateNotFailed =

1608 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));

1609 if (!StateNotFailed)

1610 return;

1611

1612 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);

1613

1614

1615

1616

1617 C.addTransition(StateNotFailed);

1618 C.addTransition(StateFailed);

1619}

1620

1621void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,

1622 CheckerContext &C) const {

1624 StreamOperationEvaluator E(C);

1625 if (!E.Init(Desc, Call, C, State))

1626 return;

1627

1628 State =

1629 E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone, false));

1630 C.addTransition(State);

1631}

1632

1633void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call,

1634 CheckerContext &C) const {

1636 SVal StreamVal = getStreamArg(Desc, Call);

1637 std::optional Stream = StreamVal.getAs();

1638 if (!Stream)

1639 return;

1640

1642 std::tie(StateNotNull, StateNull) =

1643 C.getConstraintManager().assumeDual(State, *Stream);

1644 if (StateNotNull && !StateNull)

1645 ensureStreamOpened(StreamVal, C, StateNotNull);

1646}

1647

1648void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call,

1649 CheckerContext &C) const {

1651 SVal StreamVal = getStreamArg(Desc, Call);

1652 std::optional Stream = StreamVal.getAs();

1653 if (!Stream)

1654 return;

1655

1656

1658 std::tie(StateNotNull, StateNull) =

1659 C.getConstraintManager().assumeDual(State, *Stream);

1660 if (StateNotNull && StateNull)

1661 return;

1662 if (StateNotNull && !StateNull)

1663 State = StateNotNull;

1664 else

1665 State = StateNull;

1666

1667 const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr());

1668 if (!CE)

1669 return;

1670

1671

1672 ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);

1674

1675

1676 auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym,

1677 const StreamState *SS) {

1678 if (SS->ErrorState & ErrorFError) {

1679 StreamErrorState NewES =

1680 (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;

1681 StreamState NewSS = StreamState::getOpened(Desc, NewES, false);

1682 StateNotFailed = StateNotFailed->set(Sym, NewSS);

1683 }

1684 };

1685

1686 if (StateNotNull && !StateNull) {

1687

1689 const StreamState *SS = State->get(StreamSym);

1690 if (SS) {

1691 assert(SS->isOpened() && "Stream is expected to be opened");

1692 ClearErrorInNotFailed(StreamSym, SS);

1693 } else

1694 return;

1695 }

1696 } else {

1697

1698 const StreamMapTy &Map = StateNotFailed->get();

1699 for (const auto &I : Map) {

1701 const StreamState &SS = I.second;

1702 if (SS.isOpened())

1703 ClearErrorInNotFailed(Sym, &SS);

1704 }

1705 }

1706

1707 C.addTransition(StateNotFailed);

1708 C.addTransition(StateFailed);

1709}

1710

1711void StreamChecker::evalClearerr(const FnDescription *Desc,

1712 const CallEvent &Call,

1713 CheckerContext &C) const {

1715 StreamOperationEvaluator E(C);

1716 if (!E.Init(Desc, Call, C, State))

1717 return;

1718

1719

1720 State = E.setStreamState(

1721 State,

1722 StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate));

1723 C.addTransition(State);

1724}

1725

1726void StreamChecker::evalFeofFerror(const FnDescription *Desc,

1727 const CallEvent &Call, CheckerContext &C,

1728 const StreamErrorState &ErrorKind) const {

1730 StreamOperationEvaluator E(C);

1731 if (!E.Init(Desc, Call, C, State))

1732 return;

1733

1734 if (E.SS->ErrorState & ErrorKind) {

1735

1736

1737

1739 bindAndAssumeTrue(State, C, E.CE, E.Elem.value());

1740 C.addTransition(E.setStreamState(

1741 TrueState, StreamState::getOpened(Desc, ErrorKind,

1742 E.SS->FilePositionIndeterminate &&

1743 !ErrorKind.isFEof())));

1744 }

1745 if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) {

1746

1747

1748

1749 ProgramStateRef FalseState = E.bindReturnValue(State, C, 0);

1750 C.addTransition(E.setStreamState(

1751 FalseState,

1752 StreamState::getOpened(

1753 Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof())));

1754 }

1755}

1756

1757void StreamChecker::evalFileno(const FnDescription *Desc, const CallEvent &Call,

1758 CheckerContext &C) const {

1759

1760

1761

1762

1763

1764

1765

1766

1767

1769 StreamOperationEvaluator E(C);

1770 if (!E.Init(Desc, Call, C, State))

1771 return;

1772

1773 NonLoc RetVal = makeRetVal(C, E.Elem.value()).castAs();

1774 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);

1775 State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(Call));

1776 if (!State)

1777 return;

1778

1779 C.addTransition(State);

1780}

1781

1782void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,

1783 CheckerContext &C) const {

1785 SVal StreamVal = getStreamArg(Desc, Call);

1786 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,

1787 State);

1788 if (!State)

1789 return;

1790 State = ensureStreamOpened(StreamVal, C, State);

1791 if (!State)

1792 return;

1793

1794 C.addTransition(State);

1795}

1796

1797void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,

1798 const CallEvent &Call, CheckerContext &C,

1799 const StreamErrorState &ErrorKind,

1800 bool Indeterminate) const {

1802 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();

1803 assert(StreamSym && "Operation not permitted on non-symbolic stream value.");

1804 const StreamState *SS = State->get(StreamSym);

1805 assert(SS && "Stream should be tracked by the checker.");

1806 State = State->set(

1807 StreamSym,

1808 StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));

1809 C.addTransition(State);

1810}

1811

1813StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,

1814 CheckerContext &C,

1816 auto Stream = StreamVal.getAs();

1817 if (!Stream)

1818 return State;

1819

1820 ConstraintManager &CM = C.getConstraintManager();

1821

1823 std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);

1824

1825 if (!StateNotNull && StateNull) {

1826 if (ExplodedNode *N = C.generateErrorNode(StateNull)) {

1827 auto R = std::make_unique(

1828 BT_FileNull, "Stream pointer might be NULL.", N);

1829 if (StreamE)

1831 C.emitReport(std::move(R));

1832 }

1833 return nullptr;

1834 }

1835

1836 return StateNotNull;

1837}

1838

1839namespace {

1840class StreamClosedVisitor final : public BugReporterVisitor {

1842 bool Satisfied = false;

1843

1844public:

1845 explicit StreamClosedVisitor(SymbolRef StreamSym) : StreamSym(StreamSym) {}

1846

1847 static void *getTag() {

1848 static int Tag = 0;

1849 return &Tag;

1850 }

1851

1852 void Profile(llvm::FoldingSetNodeID &ID) const override {

1853 ID.AddPointer(getTag());

1854 ID.AddPointer(StreamSym);

1855 }

1856

1858 BugReporterContext &BRC,

1859 PathSensitiveBugReport &BR) override {

1860 if (Satisfied)

1861 return nullptr;

1862 const StreamState *PredSS =

1864 if (PredSS && PredSS->isClosed())

1865 return nullptr;

1866

1868 if (!S)

1869 return nullptr;

1870 Satisfied = true;

1873 llvm::StringLiteral Msg = "Stream is closed here";

1874 return std::make_shared(Pos, Msg);

1875 }

1876};

1877}

1878

1879ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,

1880 CheckerContext &C,

1883 if (!Sym)

1884 return State;

1885

1886 const StreamState *SS = State->get(Sym);

1887 if (!SS)

1888 return State;

1889

1890 if (SS->isClosed()) {

1891

1892

1893 if (ExplodedNode *N = C.generateErrorNode()) {

1894 auto R = std::make_unique(

1895 BT_UseAfterClose, "Use of a stream that might be already closed", N);

1896 R->addVisitor(Sym);

1897 C.emitReport(std::move(R));

1898 return nullptr;

1899 }

1900

1901 return State;

1902 }

1903

1904 if (SS->isOpenFailed()) {

1905

1906

1907

1908

1909 ExplodedNode *N = C.generateErrorNode();

1910 if (N) {

1911 C.emitReport(std::make_unique(

1912 BT_UseAfterOpenFailed,

1913 "Stream might be invalid after "

1914 "(re-)opening it has failed. "

1915 "Can cause undefined behaviour.",

1916 N));

1917 return nullptr;

1918 }

1919 }

1920

1921 return State;

1922}

1923

1924ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(

1925 SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {

1926 static const char *BugMessage =

1927 "File position of the stream might be 'indeterminate' "

1928 "after a failed operation. "

1929 "Can cause undefined behavior.";

1930

1932 if (!Sym)

1933 return State;

1934

1935 const StreamState *SS = State->get(Sym);

1936 if (!SS)

1937 return State;

1938

1939 assert(SS->isOpened() && "First ensure that stream is opened.");

1940

1941 if (SS->FilePositionIndeterminate) {

1942 if (SS->ErrorState & ErrorFEof) {

1943

1944

1945

1946 ExplodedNode *N = C.generateNonFatalErrorNode(State);

1947 if (!N)

1948 return nullptr;

1949

1950 auto R = std::make_unique(

1951 BT_IndeterminatePosition, BugMessage, N);

1952 R->markInteresting(Sym);

1953 C.emitReport(std::move(R));

1954 return State->set(

1955 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));

1956 }

1957

1958

1959

1960 if (ExplodedNode *N = C.generateErrorNode(State)) {

1961 auto R = std::make_unique(

1962 BT_IndeterminatePosition, BugMessage, N);

1963 R->markInteresting(Sym);

1964 C.emitReport(std::move(R));

1965 }

1966

1967 return nullptr;

1968 }

1969

1970 return State;

1971}

1972

1974StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,

1976 std::optionalnonloc::ConcreteInt CI =

1977 WhenceVal.getAsnonloc::ConcreteInt();

1978 if (!CI)

1979 return State;

1980

1981 int64_t X = CI->getValue()->getSExtValue();

1982 if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)

1983 return State;

1984

1985 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {

1986 C.emitReport(std::make_unique(

1987 BT_IllegalWhence,

1988 "The whence argument to fseek() should be "

1989 "SEEK_SET, SEEK_END, or SEEK_CUR.",

1990 N));

1991 return nullptr;

1992 }

1993

1994 return State;

1995}

1996

1997void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,

1999 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {

2000 auto R = std::make_unique(

2001 BT_StreamEof,

2002 "Read function called when stream is in EOF state. "

2003 "Function has no effect.",

2004 N);

2005 R->markInteresting(StreamSym);

2006 C.emitReport(std::move(R));

2007 return;

2008 }

2009 C.addTransition(State);

2010}

2011

2012ExplodedNode *

2013StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,

2014 CheckerContext &C, ExplodedNode *Pred) const {

2015 ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);

2016 if (!Err)

2017 return Pred;

2018

2019 for (SymbolRef LeakSym : LeakedSyms) {

2020

2021

2022

2023

2024

2025

2026

2027

2028

2029

2030

2031 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);

2032 assert(StreamOpenNode && "Could not find place of stream opening.");

2033

2034 PathDiagnosticLocation LocUsedForUniqueing;

2037 StreamStmt, C.getSourceManager(),

2039

2040 std::unique_ptr R =

2041 std::make_unique(

2042 BT_ResourceLeak,

2043 "Opened stream never closed. Potential resource leak.", Err,

2044 LocUsedForUniqueing,

2046 R->markInteresting(LeakSym);

2047 R->addVisitor(LeakSym, this);

2048 C.emitReport(std::move(R));

2049 }

2050

2051 return Err;

2052}

2053

2054void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,

2055 CheckerContext &C) const {

2057

2058 llvm::SmallVector<SymbolRef, 2> LeakedSyms;

2059

2060 const StreamMapTy &Map = State->get();

2061 for (const auto &I : Map) {

2063 const StreamState &SS = I.second;

2064 if (!SymReaper.isDead(Sym))

2065 continue;

2066 if (SS.isOpened())

2067 LeakedSyms.push_back(Sym);

2068 State = State->remove(Sym);

2069 }

2070

2071 ExplodedNode *N = C.getPredecessor();

2072 if (!LeakedSyms.empty())

2073 N = reportLeaks(LeakedSyms, C, N);

2074

2075 C.addTransition(State, N);

2076}

2077

2081

2082

2083

2085 return State;

2086

2088

2089

2090

2091

2092

2093

2094

2095 State = State->remove(Sym);

2096 }

2097 return State;

2098}

2099

2100static const VarDecl *

2105

2106 if (FileTy.isNull())

2107 return nullptr;

2108

2110

2112 for (const Decl *D : LookupRes) {

2113 if (auto *VD = dyn_cast_or_null(D)) {

2114 if (SM.isInSystemHeader(VD->getLocation()) && VD->hasExternalStorage() &&

2115 VD->getType().getCanonicalType() == FilePtrTy) {

2116 return VD;

2117 }

2118 }

2119 }

2120 return nullptr;

2121}

2122

2123void StreamChecker::checkASTDecl(const TranslationUnitDecl *TU,

2124 AnalysisManager &Mgr, BugReporter &) const {

2130}

2131

2132

2133

2134

2135

2136void ento::registerStreamChecker(CheckerManager &Mgr) {

2138 Checker->PedanticMode =

2139 Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "Pedantic");

2140}

2141

2142bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {

2143 return true;

2144}

2145

2146void ento::registerStreamTesterChecker(CheckerManager &Mgr) {

2147 auto *Checker = Mgr.getChecker();

2148 Checker->TestMode = true;

2149}

2150

2151bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {

2152 return true;

2153}

static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)

#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)

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

static const VarDecl * getGlobalStreamPointerByName(const TranslationUnitDecl *TU, StringRef VarName)

Definition StreamChecker.cpp:2101

static ProgramStateRef tryToInvalidateFReadBufferByElements(ProgramStateRef State, CheckerContext &C, const CallEvent &Call, NonLoc SizeVal, NonLoc NMembVal)

Definition StreamChecker.cpp:1108

static QualType getPointeeType(const MemRegion *R)

Definition StreamChecker.cpp:1076

static std::optional< int64_t > getKnownValue(ProgramStateRef State, SVal V)

Definition StreamChecker.cpp:825

static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C, const CallEvent &Call, ArrayRef< unsigned int > EscapingArgs)

Definition StreamChecker.cpp:866

static std::optional< NonLoc > getStartIndex(SValBuilder &SVB, const MemRegion *R)

Definition StreamChecker.cpp:1088

static ProgramStateRef escapeByStartIndexAndCount(ProgramStateRef State, const CallEvent &Call, unsigned BlockCount, const SubRegion *Buffer, QualType ElemType, int64_t StartIndex, int64_t ElementCount)

Invalidate only the requested elements instead of the whole buffer.

Definition StreamChecker.cpp:836

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

SourceManager & getSourceManager()

QualType getBuiltinVaListType() const

Retrieve the type of the __builtin_va_list type.

QualType getFILEType() const

Retrieve the C FILE type.

QualType getPointerType(QualType T) const

Return the uniqued reference to the type for a pointer to the specified type.

CharUnits getTypeSizeInChars(QualType T) const

Return the size of the specified (complete) type T, in characters.

BinaryOperatorKind Opcode

QualType getCallReturnType(const ASTContext &Ctx) const

getCallReturnType - Get the return type of the call expr.

QuantityType getQuantity() const

getQuantity - Get the raw integer representation of this quantity.

lookup_result lookup(DeclarationName Name) const

lookup - Find the declarations (if any) with the given Name in this context.

Decl - This represents one declaration (or definition), e.g.

Stmt * getBody(const FunctionDecl *&Definition) const

Retrieve the body (definition) of the function.

bool hasBody(const FunctionDecl *&Definition) const

Returns true if the function has a body.

IdentifierInfo & get(StringRef Name)

Return the identifier token info for the specified named identifier.

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

const Decl * getDecl() const

A (possibly-)qualified type.

bool isNull() const

Return true if this QualType doesn't point to a type yet.

QualType getCanonicalType() const

The top declaration context.

ASTContext & getASTContext() const

bool isPointerType() const

bool isIntegralOrEnumerationType() const

Determine whether this type is an integral or enumeration type.

Preprocessor & getPreprocessor() override

APSIntPtr getIntValue(uint64_t X, bool isUnsigned)

const BugType & getBugType() const

const SourceManager & getSourceManager() const

const T * lookup(const CallEvent &Call) const

bool matchesAsWritten(const CallExpr &CE) const

Returns true if the CallExpr is a call to a function that matches the CallDescription.

Represents an abstract call to a function or method along a particular path.

const AnalyzerOptions & getAnalyzerOptions() const

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

Register a single-part checker (derived from Checker): construct its singleton instance,...

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

If the the singleton instance of a checker class is not yet constructed, then construct it (with the ...

ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)

Returns a pair of states (StTrue, StFalse) where the given condition is assumed to be true or false,...

std::pair< ProgramStateRef, ProgramStateRef > ProgramStatePair

const ProgramStateRef & getState() const

const Stmt * getStmtForDiagnostics() const

If the node's program point corresponds to a statement, retrieve that statement.

ProgramPoint getLocation() const

getLocation - Returns the edge associated with the given node.

const LocationContext * getLocationContext() const

ExplodedNode * getFirstPred()

MemRegion - The root abstract class for all memory regions.

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.

void markNotInteresting(SymbolRef sym)

bool isInteresting(SymbolRef sym) const

SValBuilder & getSValBuilder()

Information about invalidation for a particular region/symbol.

void setTrait(SymbolRef Sym, InvalidationKinds IK)

DefinedOrUnknownSVal makeZeroVal(QualType type)

Construct an SVal representing '0' for the specified type.

virtual const llvm::APSInt * getKnownValue(ProgramStateRef state, SVal val)=0

Evaluates a given SVal.

BasicValueFactory & getBasicValueFactory()

ProgramStateManager & getStateManager()

NonLoc makeArrayIndex(uint64_t idx)

nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)

virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy)=0

Create a new value which represents a binary expression with two non- location operands.

QualType getConditionType() const

SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)

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

SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const

If this SVal wraps a symbol return that SymbolRef.

std::optional< T > getAs() const

Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.

T castAs() const

Convert to the specified SVal type, asserting that this SVal is of the desired type.

SubRegion - A region that subsets another larger region.

MemRegionManager & getMemRegionManager() const override

bool isDead(SymbolRef sym)

Returns whether or not a symbol has been confirmed dead.

Value representing integer constant.

__inline void unsigned int _2

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.

internal::Matcher< T > findAll(const internal::Matcher< T > &Matcher)

Matches if the node or any descendant matches.

bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})

Attempts to add visitors to track expression value back to its point of origin.

PointerEscapeKind

Describes the different reasons a pointer escapes during analysis.

@ PSK_DirectEscapeOnCall

The pointer has been passed to a function call directly.

llvm::DenseSet< SymbolRef > InvalidatedSymbols

IntrusiveRefCntPtr< const ProgramState > ProgramStateRef

const SymExpr * SymbolRef

std::optional< SVal > getPointeeVal(SVal PtrSVal, ProgramStateRef State)

std::optional< int > tryExpandAsInteger(StringRef Macro, const Preprocessor &PP)

Try to parse the value of a defined preprocessor macro.

std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef

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

@ Match

This is not an overload because the signature exactly matches an existing declaration.

bool isa(CodeGen::Address addr)

CFGBlock::ConstCFGElementRef ConstCFGElementRef

bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)

DiagnosticLevelMask operator&(DiagnosticLevelMask LHS, DiagnosticLevelMask RHS)

DiagnosticLevelMask operator~(DiagnosticLevelMask M)

const FunctionProtoType * T

DiagnosticLevelMask operator|(DiagnosticLevelMask LHS, DiagnosticLevelMask RHS)

bool operator!=(CanQual< T > x, CanQual< U > y)

int const char * function