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";

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 assert(CE && "Expecting a call expression.");

229

231 return C.getSValBuilder()

232 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())

234}

235

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

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

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

242 return State;

243}

244

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

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

249 return State;

250}

251

252inline void assertStreamStateOpened(const StreamState *SS) {

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

254}

255

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

257 check::DeadSymbols, check::PointerEscape,

258 check::ASTDecl> {

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

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

261 BugType BT_UseAfterOpenFailed{this, "Invalid stream",

262 "Stream handling error"};

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

264 "Stream handling error"};

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

266 "Stream handling error"};

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

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

269 true};

270

271public:

279

280

283

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

285 const BugType *getBT_IndeterminatePosition() const {

286 return &BT_IndeterminatePosition;

287 }

288

289

290

294

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

300 return "";

301

303

304 return FeofNote;

305 });

306 }

307

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

313 return "";

314

316

317 return FerrorNote;

318 });

319 }

320

325 return "";

326

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

328 BR.markNotInteresting(StreamSym);

329 return FeofNote;

330 }

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

332 BR.markNotInteresting(StreamSym);

333 return FerrorNote;

334 }

335

336 return "";

337 });

338 }

339

340

341 bool TestMode = false;

342

343

344 bool PedanticMode = false;

345

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

347

348private:

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

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

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

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

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

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

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

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

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

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

360 {&StreamChecker::preRead,

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

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

363 {&StreamChecker::preWrite,

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

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

366 {&StreamChecker::preRead,

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

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

369 {&StreamChecker::preRead,

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

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

372 {&StreamChecker::preRead,

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

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

375 {&StreamChecker::preWrite,

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

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

378 {&StreamChecker::preWrite,

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

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

381 {&StreamChecker::preWrite,

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

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

384 {&StreamChecker::preWrite,

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

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

387 {&StreamChecker::preWrite,

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

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

390 {&StreamChecker::preRead,

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

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

393 {&StreamChecker::preRead,

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

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

396 {&StreamChecker::preWrite,

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

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

399 {&StreamChecker::preRead,

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

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

402 {&StreamChecker::preRead,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

423 {&StreamChecker::preDefault,

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

425 0}},

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

427 {&StreamChecker::preDefault,

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

429 0}},

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

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

432 };

433

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

436 {nullptr,

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

438 false),

439 0}},

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

441 {nullptr,

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

443 ErrorFError, false),

444 0}},

445 {{CDM::SimpleFunc,

446 {"StreamTesterChecker_make_ferror_indeterminate_stream"},

447 1},

448 {nullptr,

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

450 ErrorFError, true),

451 0}},

452 };

453

454

455 mutable std::optional EofVal;

456

457 mutable int SeekSetVal = 0;

458

459 mutable int SeekCurVal = 1;

460

461 mutable int SeekEndVal = 2;

462

464

465 mutable const VarDecl *StdinDecl = nullptr;

466 mutable const VarDecl *StdoutDecl = nullptr;

467 mutable const VarDecl *StderrDecl = nullptr;

468

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

471

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

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

476

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

479

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

482

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

485

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

488

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

491

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

494

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

497

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

500

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

503

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

506

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

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

511

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

514

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

517

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

520

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

523

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

526

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

529

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

532 const StreamErrorState &ErrorKind) const;

533

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

536 bool Indeterminate) const;

537

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

540

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

543

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

546

547

548

549

550

554

555

556

557

560

561

562

563

564

565

566

570

571

572

573

574

577

578

579

580

581

584

585

586

587

590

591

592

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

594

595

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

599 T.getCanonicalType() != VaListType)

600 return nullptr;

601 }

602

604 }

605

606

607

609 const std::string &Message) const {

610 return C.getNoteTag([this, StreamSym,

613 return Message;

614 return "";

615 });

616 }

617

618 void initMacroValues(const Preprocessor &PP) const {

619 if (EofVal)

620 return;

621

623 EofVal = *OptInt;

624 else

625 EofVal = -1;

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

627 SeekSetVal = *OptInt;

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

629 SeekEndVal = *OptInt;

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

631 SeekCurVal = *OptInt;

632 }

633

634

635

639};

640

641struct StreamOperationEvaluator {

644

646 const StreamState *SS = nullptr;

647 const CallExpr *CE = nullptr;

648 StreamErrorState NewES;

649

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

652 ;

653 }

654

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

658 if (!StreamSym)

659 return false;

660 SS = State->get(StreamSym);

661 if (!SS)

662 return false;

663 NewES = SS->ErrorState;

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

665 if (!CE)

666 return false;

667

668 assertStreamStateOpened(SS);

669

670 return true;

671 }

672

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

674

677 }

678

680 const StreamState &NewSS) {

681 NewES = NewSS.ErrorState;

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

683 }

684

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

688 }

689

691 uint64_t Val) {

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

694 }

695

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

699 }

700

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

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

705 }

706

712 if (!Cond)

713 return nullptr;

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

715 }

716

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

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

722 }

723

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 {

745protected:

746

747

748

749

750

751 bool isClosingCallAsWritten(const CallExpr &Call) const {

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

753 return StreamChk->FCloseDesc.matchesAsWritten(Call);

754 }

755

758 const FunctionDecl *FD = dyn_cast(Callee);

759

760

761

762

763

764

765

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

767 return false;

769

770 auto Matches =

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

774 if (isClosingCallAsWritten(*Call))

775 return true;

776 }

777

778

779 return false;

780 }

781

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)

800};

801

802}

803

808

809

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

812

814 while (N) {

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

817 return Pred;

818 Pred = N;

820 }

821

822 return nullptr;

823}

824

826 SValBuilder &SVB = State->getStateManager().getSValBuilder();

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();

846 SValBuilder &SVB = State->getStateManager().getSValBuilder();

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.getOriginExpr(), 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.getOriginExpr(),

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

873 false,

874 nullptr);

875 return State;

876}

877

878

879

880

881

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

884 const FnDescription *Desc = lookupFn(Call);

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

886 return;

887

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

889}

890

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(

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,

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

931 if (!CE)

932 return;

933

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,

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,

974

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

976 if (!CE)

977 return;

978

979 std::optional StreamVal =

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,

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,

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,

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();

1100 if (isa(R))

1101 return Zero();

1102 if (isa(R))

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,

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<NonLoc>();

1158 if (!SizeVal)

1159 return;

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

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

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,

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

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 =

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,

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<NonLoc>();

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

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,

1329 if (Call.getNumArgs() < 2)

1330 return;

1331

1333 StreamOperationEvaluator E(C);

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

1335 return;

1336

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

1339 auto Cond =

1340 E.SVB

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

1342 E.SVB.getConditionType())

1343 .getAs();

1344 if (!Cond)

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,

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()) {

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") {

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,

1419 StreamOperationEvaluator E(C);

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

1421 return;

1422

1423

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

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,

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

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

1465 StateNotFailed =

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

1467

1468

1470 if (NewLinePtr && isa(*NewLinePtr))

1471 StateNotFailed = StateNotFailed->assume(

1473

1474

1475

1476 SVal SizePtrSval = Call.getArgSVal(1);

1478 if (NVal && isa(*NVal)) {

1479 StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,

1480 NVal->castAs<NonLoc>(), RetVal);

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

1482 }

1483 if (!StateNotFailed)

1484 return;

1485 C.addTransition(StateNotFailed);

1486 }

1487

1488

1489

1490

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,

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,

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

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,

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,

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,

1600 StreamOperationEvaluator E(C);

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

1602 return;

1603

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

1613

1614

1615

1616

1617 C.addTransition(StateNotFailed);

1618 C.addTransition(StateFailed);

1619}

1620

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

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,

1636 SVal StreamVal = getStreamArg(Desc, Call);

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

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,

1651 SVal StreamVal = getStreamArg(Desc, Call);

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

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,

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,

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 C.addTransition(E.setStreamState(

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

1741 E.SS->FilePositionIndeterminate &&

1742 !ErrorKind.isFEof())));

1743 }

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

1745

1746

1747

1749 C.addTransition(E.setStreamState(

1750 FalseState,

1751 StreamState::getOpened(

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

1753 }

1754}

1755

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

1758

1759

1760

1761

1762

1763

1764

1765

1766

1768 StreamOperationEvaluator E(C);

1769 if (E.Init(Desc, Call, C, State))

1770 return;

1771

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

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

1775 if (!State)

1776 return;

1777

1778 C.addTransition(State);

1779}

1780

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

1784 SVal StreamVal = getStreamArg(Desc, Call);

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

1786 State);

1787 if (!State)

1788 return;

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

1790 if (!State)

1791 return;

1792

1793 C.addTransition(State);

1794}

1795

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

1798 const StreamErrorState &ErrorKind,

1799 bool Indeterminate) const {

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

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

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

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

1805 State = State->set(

1806 StreamSym,

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

1808 C.addTransition(State);

1809}

1810

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

1816 if (!Stream)

1817 return State;

1818

1820

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

1823

1824 if (!StateNotNull && StateNull) {

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

1826 auto R = std::make_unique(

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

1828 if (StreamE)

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

1831 }

1832 return nullptr;

1833 }

1834

1835 return StateNotNull;

1836}

1837

1838namespace {

1841 bool Satisfied = false;

1842

1843public:

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

1845

1846 static void *getTag() {

1847 static int Tag = 0;

1848 return &Tag;

1849 }

1850

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

1852 ID.AddPointer(getTag());

1853 ID.AddPointer(StreamSym);

1854 }

1855

1859 if (Satisfied)

1860 return nullptr;

1861 const StreamState *PredSS =

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

1864 return nullptr;

1865

1867 if (!S)

1868 return nullptr;

1869 Satisfied = true;

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

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

1874 }

1875};

1876}

1877

1882 if (!Sym)

1883 return State;

1884

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

1886 if (!SS)

1887 return State;

1888

1889 if (SS->isClosed()) {

1890

1891

1893 auto R = std::make_unique(

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

1895 R->addVisitor(Sym);

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

1897 return nullptr;

1898 }

1899

1900 return State;

1901 }

1902

1903 if (SS->isOpenFailed()) {

1904

1905

1906

1907

1909 if (N) {

1910 C.emitReport(std::make_unique(

1911 BT_UseAfterOpenFailed,

1912 "Stream might be invalid after "

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

1914 "Can cause undefined behaviour.",

1915 N));

1916 return nullptr;

1917 }

1918 }

1919

1920 return State;

1921}

1922

1923ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(

1925 static const char *BugMessage =

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

1927 "after a failed operation. "

1928 "Can cause undefined behavior.";

1929

1931 if (!Sym)

1932 return State;

1933

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

1935 if (!SS)

1936 return State;

1937

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

1939

1940 if (SS->FilePositionIndeterminate) {

1941 if (SS->ErrorState & ErrorFEof) {

1942

1943

1944

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

1946 if (!N)

1947 return nullptr;

1948

1949 auto R = std::make_unique(

1950 BT_IndeterminatePosition, BugMessage, N);

1951 R->markInteresting(Sym);

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

1953 return State->set(

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

1955 }

1956

1957

1958

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

1960 auto R = std::make_unique(

1961 BT_IndeterminatePosition, BugMessage, N);

1962 R->markInteresting(Sym);

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

1964 }

1965

1966 return nullptr;

1967 }

1968

1969 return State;

1970}

1971

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

1975 std::optionalnonloc::ConcreteInt CI =

1977 if (!CI)

1978 return State;

1979

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

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

1982 return State;

1983

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

1985 C.emitReport(std::make_unique(

1986 BT_IllegalWhence,

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

1988 "SEEK_SET, SEEK_END, or SEEK_CUR.",

1989 N));

1990 return nullptr;

1991 }

1992

1993 return State;

1994}

1995

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

1999 auto R = std::make_unique(

2000 BT_StreamEof,

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

2002 "Function has no effect.",

2003 N);

2004 R->markInteresting(StreamSym);

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

2006 return;

2007 }

2008 C.addTransition(State);

2009}

2010

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

2015 if (!Err)

2016 return Pred;

2017

2018 for (SymbolRef LeakSym : LeakedSyms) {

2019

2020

2021

2022

2023

2024

2025

2026

2027

2028

2029

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

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

2032

2036 StreamStmt, C.getSourceManager(),

2038

2039 std::unique_ptr R =

2040 std::make_unique(

2041 BT_ResourceLeak,

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

2043 LocUsedForUniqueing,

2045 R->markInteresting(LeakSym);

2046 R->addVisitor(LeakSym, this);

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

2048 }

2049

2050 return Err;

2051}

2052

2053void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,

2056

2058

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

2060 for (const auto &I : Map) {

2062 const StreamState &SS = I.second;

2063 if (!SymReaper.isDead(Sym))

2064 continue;

2065 if (SS.isOpened())

2066 LeakedSyms.push_back(Sym);

2067 State = State->remove(Sym);

2068 }

2069

2071 if (!LeakedSyms.empty())

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

2073

2074 C.addTransition(State, N);

2075}

2076

2080

2081

2082

2084 return State;

2085

2087

2088

2089

2090

2091

2092

2093

2094 State = State->remove(Sym);

2095 }

2096 return State;

2097}

2098

2104

2105 if (FileTy.isNull())

2106 return nullptr;

2107

2109

2111 for (const Decl *D : LookupRes) {

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

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

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

2115 return VD;

2116 }

2117 }

2118 }

2119 return nullptr;

2120}

2121

2129}

2130

2131

2132

2133

2134

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

2139}

2140

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

2142 return true;

2143}

2144

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

2147 Checker->TestMode = true;

2148}

2149

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

2151 return true;

2152}

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)

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

static QualType getPointeeType(const MemRegion *R)

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

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

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

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.

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.

bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const

Interprets an option's string value as a boolean.

CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).

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.

This represents one expression.

Represents a function declaration or definition.

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

Engages in a tight little dance with the lexer to efficiently preprocess tokens.

A (possibly-)qualified type.

bool isNull() const

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

QualType getCanonicalType() const

Stmt - This represents one statement.

The top declaration context.

ASTContext & getASTContext() const

bool isPointerType() const

bool isIntegralOrEnumerationType() const

Determine whether this type is an integral or enumeration type.

Represents a variable declaration or definition.

Maps string IDs to AST nodes matched by parts of a matcher.

Preprocessor & getPreprocessor() override

APSIntPtr getIntValue(uint64_t X, bool isUnsigned)

const BugType & getBugType() const

const SourceManager & getSourceManager() const

BugReporterVisitors are used to add custom diagnostics along a path.

virtual void Profile(llvm::FoldingSetNodeID &ID) const =0

virtual PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, PathSensitiveBugReport &BR)=0

Return a diagnostic piece which should be associated with the given node.

BugReporter is a utility class for generating PathDiagnostics for analysis.

An immutable map from CallDescriptions to arbitrary data.

const T * lookup(const CallEvent &Call) const

A CallDescription is a pattern that can be used to match calls based on the qualified name and the ar...

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

const AnalyzerOptions & getAnalyzerOptions() const

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

Used to register checkers.

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.

virtual bool doesFnIntendToHandleOwnership(const Decl *Callee, ASTContext &ACtx)=0

Heuristically guess whether the callee intended to free the resource.

const CheckerBase & Checker

virtual PathDiagnosticPieceRef emitNote(const ExplodedNode *N)=0

virtual bool hasResourceStateChanged(ProgramStateRef CallEnterState, ProgramStateRef CallExitEndState)=0

The tag upon which the TagVisitor reacts.

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

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()

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

A class responsible for cleaning up unused symbols.

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

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.

if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))

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

DiagnosticLevelMask operator&(DiagnosticLevelMask LHS, DiagnosticLevelMask RHS)

DiagnosticLevelMask operator~(DiagnosticLevelMask M)

DiagnosticLevelMask operator|(DiagnosticLevelMask LHS, DiagnosticLevelMask RHS)

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

const FunctionProtoType * T