balazske created this revision.
Herald added subscribers: manas, steakhal, ASDenysPetrov, martong, gamesh411, 
dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, szepet, 
baloghadamsoftware, xazax.hun, whisperity.
Herald added a reviewer: Szelethus.
balazske requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Recognize initial value of standard streams (stdin, ...)
and assume that a new opened stream is not equal to these.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D106644

Files:
  clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
  clang/test/Analysis/stream.c

Index: clang/test/Analysis/stream.c
===================================================================
--- clang/test/Analysis/stream.c
+++ clang/test/Analysis/stream.c
@@ -267,3 +267,12 @@
 } // expected-warning {{Opened stream never closed. Potential resource leak}}
 // FIXME: This warning should be placed at the `return` above.
 // See https://reviews.llvm.org/D83120 about details.
+
+void check_fopen_is_not_std(FILE *F, int Std) {
+  if (!Std)
+    F = fopen("file", "r");
+  if (!F)
+    return;
+  if (F != stdin && F != stdout && F != stderr)
+    fclose(F); // no warning: the opened file can not be equal to std streams
+}
Index: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -10,6 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/ParentMapContext.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
@@ -157,6 +158,11 @@
 // StreamChecker class and utility functions.
 //===----------------------------------------------------------------------===//
 
+// This map holds the state of a stream.
+// The stream is identified with a SymbolRef that is created when a stream
+// opening function is modeled by the checker.
+REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
+
 namespace {
 
 class StreamChecker;
@@ -206,8 +212,47 @@
   return State;
 }
 
-class StreamChecker : public Checker<check::PreCall, eval::Call,
-                                     check::DeadSymbols, check::PointerEscape> {
+const VarDecl *findStdStreamDecl(StringRef StdName, CheckerContext &C) {
+  ASTContext &ACtx = C.getASTContext();
+
+  IdentifierInfo &II = ACtx.Idents.get(StdName);
+  auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
+  QualType FILEType = ACtx.getFILEType();
+  if (!FILEType.isNull())
+    FILEType = ACtx.getPointerType(FILEType);
+
+  for (Decl *D : LookupRes) {
+    D = D->getCanonicalDecl();
+    if (!C.getSourceManager().isInSystemHeader(D->getLocation()))
+      continue;
+    if (auto *VD = dyn_cast<VarDecl>(D)) {
+      if (!FILEType.isNull() && !ACtx.hasSameType(FILEType, VD->getType()))
+        continue;
+      return VD;
+    }
+  }
+
+  return nullptr;
+}
+
+SymbolRef findStdStream(StringRef StdName, CheckerContext &C) {
+  const LocationContext *LCtx = C.getLocationContext();
+
+  const VarDecl *StdDecl = findStdStreamDecl(StdName, C);
+  if (!StdDecl)
+    return nullptr;
+
+  const VarRegion *RegionOfStdStreamVar =
+      C.getState()->getRegion(StdDecl, LCtx);
+  if (!RegionOfStdStreamVar)
+    return nullptr;
+  SVal StdVal = C.getState()->getSVal(RegionOfStdStreamVar, StdDecl->getType());
+  return StdVal.getAsSymbol();
+}
+
+class StreamChecker
+    : public Checker<check::BeginFunction, check::PreCall, eval::Call,
+                     check::DeadSymbols, check::PointerEscape> {
   BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
   BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
   BugType BT_UseAfterOpenFailed{this, "Invalid stream",
@@ -221,6 +266,7 @@
                           /*SuppressOnSink =*/true};
 
 public:
+  void checkBeginFunction(CheckerContext &C) const;
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
@@ -285,6 +331,10 @@
         0}},
   };
 
+  mutable SymbolRef OrigStdin;
+  mutable SymbolRef OrigStdout;
+  mutable SymbolRef OrigStderr;
+
   void evalFopen(const FnDescription *Desc, const CallEvent &Call,
                  CheckerContext &C) const;
 
@@ -414,16 +464,15 @@
 
 } // end anonymous namespace
 
-// This map holds the state of a stream.
-// The stream is identified with a SymbolRef that is created when a stream
-// opening function is modeled by the checker.
-REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
-
 inline void assertStreamStateOpened(const StreamState *SS) {
   assert(SS->isOpened() &&
          "Previous create of error node for non-opened stream failed?");
 }
 
+//===----------------------------------------------------------------------===//
+// Methods of StreamChecker.
+//===----------------------------------------------------------------------===//
+
 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
                                                       SymbolRef StreamSym,
                                                       CheckerContext &C) {
@@ -445,9 +494,14 @@
   return nullptr;
 }
 
-//===----------------------------------------------------------------------===//
-// Methods of StreamChecker.
-//===----------------------------------------------------------------------===//
+void StreamChecker::checkBeginFunction(CheckerContext &C) const {
+  if (!C.inTopFrame())
+    return;
+
+  OrigStdin = findStdStream("stdin", C);
+  OrigStdout = findStdStream("stdout", C);
+  OrigStderr = findStdStream("stderr", C);
+}
 
 void StreamChecker::checkPreCall(const CallEvent &Call,
                                  CheckerContext &C) const {
@@ -494,6 +548,21 @@
   StateNull =
       StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
 
+  auto AssumeRetValNotEq = [&C, &StateNotNull, RetVal](SymbolRef StdStream) {
+    SVal NotEqS = C.getSValBuilder().evalBinOp(
+        StateNotNull, BO_NE, RetVal,
+        C.getSValBuilder().makeSymbolVal(StdStream),
+        C.getASTContext().getLogicalOperationType());
+    Optional<DefinedOrUnknownSVal> NotEqDef =
+        NotEqS.getAs<DefinedOrUnknownSVal>();
+    if (!NotEqDef)
+      return;
+    StateNotNull = StateNotNull->assume(*NotEqDef, true);
+  };
+  AssumeRetValNotEq(OrigStdin);
+  AssumeRetValNotEq(OrigStdout);
+  AssumeRetValNotEq(OrigStderr);
+
   C.addTransition(StateNotNull,
                   constructNoteTag(C, RetSym, {&BT_ResourceLeak}));
   C.addTransition(StateNull, constructNoteTag(C, RetSym, {&BT_FileNull}));
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to