@@ -254,7 +254,8 @@ inline void assertStreamStateOpened(const StreamState *SS) {
254
254
}
255
255
256
256
class StreamChecker : public Checker <check::PreCall, eval::Call,
257
- check::DeadSymbols, check::PointerEscape> {
257
+ check::DeadSymbols, check::PointerEscape,
258
+ check::ASTDecl<TranslationUnitDecl>> {
258
259
BugType BT_FileNull{this , " NULL stream pointer" , " Stream handling error" };
259
260
BugType BT_UseAfterClose{this , " Closed stream" , " Stream handling error" };
260
261
BugType BT_UseAfterOpenFailed{this , " Invalid stream" ,
@@ -276,11 +277,21 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
276
277
const CallEvent *Call,
277
278
PointerEscapeKind Kind) const ;
278
279
280
+ // / Finds the declarations of 'FILE *stdin, *stdout, *stderr'.
281
+ void checkASTDecl (const TranslationUnitDecl *TU, AnalysisManager &,
282
+ BugReporter &) const ;
283
+
279
284
const BugType *getBT_StreamEof () const { return &BT_StreamEof; }
280
285
const BugType *getBT_IndeterminatePosition () const {
281
286
return &BT_IndeterminatePosition;
282
287
}
283
288
289
+ // / Assumes that the result of 'fopen' can't alias with the pointee of
290
+ // / 'stdin', 'stdout' or 'stderr'.
291
+ ProgramStateRef assumeNoAliasingWithStdStreams (ProgramStateRef State,
292
+ DefinedSVal RetVal,
293
+ CheckerContext &C) const ;
294
+
284
295
const NoteTag *constructSetEofNoteTag (CheckerContext &C,
285
296
SymbolRef StreamSym) const {
286
297
return C.getNoteTag ([this , StreamSym](PathSensitiveBugReport &BR) {
@@ -451,6 +462,10 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
451
462
// / The built-in va_list type is platform-specific
452
463
mutable QualType VaListType;
453
464
465
+ mutable const VarDecl *StdinDecl = nullptr ;
466
+ mutable const VarDecl *StdoutDecl = nullptr ;
467
+ mutable const VarDecl *StderrDecl = nullptr ;
468
+
454
469
void evalFopen (const FnDescription *Desc, const CallEvent &Call,
455
470
CheckerContext &C) const ;
456
471
@@ -887,6 +902,30 @@ bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
887
902
return C.isDifferent ();
888
903
}
889
904
905
+ ProgramStateRef StreamChecker::assumeNoAliasingWithStdStreams (
906
+ ProgramStateRef State, DefinedSVal RetVal, CheckerContext &C) const {
907
+ auto assumeRetNE = [&C, RetVal](ProgramStateRef State,
908
+ const VarDecl *Var) -> ProgramStateRef {
909
+ if (!Var)
910
+ return State;
911
+ const auto *LCtx = C.getLocationContext ();
912
+ auto &StoreMgr = C.getStoreManager ();
913
+ auto &SVB = C.getSValBuilder ();
914
+ SVal VarValue = State->getSVal (StoreMgr.getLValueVar (Var, LCtx));
915
+ auto NoAliasState =
916
+ SVB.evalBinOp (State, BO_NE, RetVal, VarValue, SVB.getConditionType ())
917
+ .castAs <DefinedOrUnknownSVal>();
918
+ return State->assume (NoAliasState, true );
919
+ };
920
+
921
+ assert (State);
922
+ State = assumeRetNE (State, StdinDecl);
923
+ State = assumeRetNE (State, StdoutDecl);
924
+ State = assumeRetNE (State, StderrDecl);
925
+ assert (State);
926
+ return State;
927
+ }
928
+
890
929
void StreamChecker::evalFopen (const FnDescription *Desc, const CallEvent &Call,
891
930
CheckerContext &C) const {
892
931
ProgramStateRef State = C.getState ();
@@ -899,6 +938,7 @@ void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
899
938
assert (RetSym && " RetVal must be a symbol here." );
900
939
901
940
State = State->BindExpr (CE, C.getLocationContext (), RetVal);
941
+ State = assumeNoAliasingWithStdStreams (State, RetVal, C);
902
942
903
943
// Bifurcate the state into two: one with a valid FILE* pointer, the other
904
944
// with a NULL.
@@ -2017,6 +2057,36 @@ ProgramStateRef StreamChecker::checkPointerEscape(
2017
2057
return State;
2018
2058
}
2019
2059
2060
+ static const VarDecl *
2061
+ getGlobalStreamPointerByName (const TranslationUnitDecl *TU, StringRef VarName) {
2062
+ ASTContext &Ctx = TU->getASTContext ();
2063
+ const auto &SM = Ctx.getSourceManager ();
2064
+ const QualType FileTy = Ctx.getFILEType ();
2065
+
2066
+ if (FileTy.isNull ())
2067
+ return nullptr ;
2068
+
2069
+ const QualType FilePtrTy = Ctx.getPointerType (FileTy).getCanonicalType ();
2070
+
2071
+ auto LookupRes = TU->lookup (&Ctx.Idents .get (VarName));
2072
+ for (const Decl *D : LookupRes) {
2073
+ if (auto *VD = dyn_cast_or_null<VarDecl>(D)) {
2074
+ if (SM.isInSystemHeader (VD->getLocation ()) && VD->hasExternalStorage () &&
2075
+ VD->getType ().getCanonicalType () == FilePtrTy) {
2076
+ return VD;
2077
+ }
2078
+ }
2079
+ }
2080
+ return nullptr ;
2081
+ }
2082
+
2083
+ void StreamChecker::checkASTDecl (const TranslationUnitDecl *TU,
2084
+ AnalysisManager &, BugReporter &) const {
2085
+ StdinDecl = getGlobalStreamPointerByName (TU, " stdin" );
2086
+ StdoutDecl = getGlobalStreamPointerByName (TU, " stdout" );
2087
+ StderrDecl = getGlobalStreamPointerByName (TU, " stderr" );
2088
+ }
2089
+
2020
2090
// ===----------------------------------------------------------------------===//
2021
2091
// Checker registration.
2022
2092
// ===----------------------------------------------------------------------===//
0 commit comments