https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/70539
None >From 10bd2b51df19b6a2a76ad326462528d7aebfc548 Mon Sep 17 00:00:00 2001 From: Ben Shi <benn...@tencent.com> Date: Sat, 28 Oct 2023 14:41:47 +0800 Subject: [PATCH] [clang][analyzer] Add 'tmpfile' as an open function to SimpleStreamChecker --- .../Checkers/SimpleStreamChecker.cpp | 64 ++++++++++--------- ...ystem-header-simulator-for-simple-stream.h | 1 + clang/test/Analysis/simple-stream-checks.c | 24 +++++++ 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 32d95e944195390..178ebd00ac6377c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -1,4 +1,4 @@ -//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--// +//===-- SimpleStreamChecker.cpp -----------------------------------*- C++ -*--// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <array> #include <utility> using namespace clang; @@ -31,7 +32,7 @@ typedef SmallVector<SymbolRef, 2> SymbolVector; struct StreamState { private: enum Kind { Opened, Closed } K; - StreamState(Kind InK) : K(InK) { } + StreamState(Kind InK) : K(InK) {} public: bool isOpened() const { return K == Opened; } @@ -40,25 +41,20 @@ struct StreamState { static StreamState getOpened() { return StreamState(Opened); } static StreamState getClosed() { return StreamState(Closed); } - bool operator==(const StreamState &X) const { - return K == X.K; - } - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(K); - } + bool operator==(const StreamState &X) const { return K == X.K; } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } }; -class SimpleStreamChecker : public Checker<check::PostCall, - check::PreCall, - check::DeadSymbols, - check::PointerEscape> { - CallDescription OpenFn, CloseFn; +class SimpleStreamChecker + : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, + check::PointerEscape> { + std::array<CallDescription, 2> OpenFns; + CallDescription CloseFn; std::unique_ptr<BugType> DoubleCloseBugType; std::unique_ptr<BugType> LeakBugType; - void reportDoubleClose(SymbolRef FileDescSym, - const CallEvent &Call, + void reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, CheckerContext &C) const; void reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &C, @@ -78,9 +74,9 @@ class SimpleStreamChecker : public Checker<check::PostCall, /// Stop tracking addresses which escape. ProgramStateRef checkPointerEscape(ProgramStateRef State, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind) const; + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; }; } // end anonymous namespace @@ -90,15 +86,15 @@ class SimpleStreamChecker : public Checker<check::PostCall, REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) SimpleStreamChecker::SimpleStreamChecker() - : OpenFn({"fopen"}), CloseFn({"fclose"}, 1) { + : OpenFns({CallDescription({"fopen"}), CallDescription({"tmpfile"}, 0)}), + CloseFn({"fclose"}, 1) { // Initialize the bug types. DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); // Sinks are higher importance bugs as well as calls to assert() or exit(0). - LeakBugType.reset( - new BugType(this, "Resource Leak", "Unix Stream API Error", - /*SuppressOnSink=*/true)); + LeakBugType.reset(new BugType(this, "Resource Leak", "Unix Stream API Error", + /*SuppressOnSink=*/true)); } void SimpleStreamChecker::checkPostCall(const CallEvent &Call, @@ -106,7 +102,14 @@ void SimpleStreamChecker::checkPostCall(const CallEvent &Call, if (!Call.isGlobalCFunction()) return; - if (!OpenFn.matches(Call)) + // Search for a matched open function. + bool Opened = false; + for (auto &Fn : OpenFns) + if (Fn.matches(Call)) { + Opened = true; + break; + } + if (!Opened) return; // Get the symbolic value corresponding to the file handle. @@ -146,8 +149,8 @@ void SimpleStreamChecker::checkPreCall(const CallEvent &Call, C.addTransition(State); } -static bool isLeaked(SymbolRef Sym, const StreamState &SS, - bool IsSymDead, ProgramStateRef State) { +static bool isLeaked(SymbolRef Sym, const StreamState &SS, bool IsSymDead, + ProgramStateRef State) { if (IsSymDead && SS.isOpened()) { // If a symbol is NULL, assume that fopen failed on this path. // A symbol should only be considered leaked if it is non-null. @@ -212,7 +215,8 @@ void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams, } } -bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ +bool SimpleStreamChecker::guaranteedNotToCloseFile( + const CallEvent &Call) const { // If it's not in a system header, assume it might close a file. if (!Call.isInSystemHeader()) return false; @@ -229,11 +233,9 @@ bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ // If the pointer we are tracking escaped, do not track the symbol as // we cannot reason about it anymore. -ProgramStateRef -SimpleStreamChecker::checkPointerEscape(ProgramStateRef State, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind) const { +ProgramStateRef SimpleStreamChecker::checkPointerEscape( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind) const { // If we know that the call cannot close a file, there is nothing to do. if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) { return State; diff --git a/clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h b/clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h index b65b7a6b0e7b020..194a8a269e722ee 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h +++ b/clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h @@ -13,6 +13,7 @@ int fputc(int, FILE *); int fputs(const char * restrict, FILE * restrict) __asm("_" "fputs" ); int fclose(FILE *); void exit(int); +FILE *tmpfile(void); // The following is a fake system header function typedef struct __FileStruct { diff --git a/clang/test/Analysis/simple-stream-checks.c b/clang/test/Analysis/simple-stream-checks.c index 767ffaf53fcbfd1..705dde84b893fed 100644 --- a/clang/test/Analysis/simple-stream-checks.c +++ b/clang/test/Analysis/simple-stream-checks.c @@ -94,3 +94,27 @@ void testOverwrite(void) { FILE *fp = fopen("myfile.txt", "w"); fp = 0; } // expected-warning {{Opened file is never closed; potential resource leak}} + +void testTmpfileNotClose(void) { + FILE *fp = tmpfile(); + fputs("abc", fp); + return; // expected-warning {{Opened file is never closed; potential resource leak}} +} + +void testTmpfileClose(void) { + FILE *fp = tmpfile(); + fclose(fp); + return; // no warning +} + +void testTmpfileEscaped(void) { + FILE *fp = tmpfile(); + myfclose(fp); + return; // no warning +} + +void testTmpfileNotEscaped(void) { + FILE *fp = tmpfile(); + passConstPointer(fp); + return; // expected-warning {{Opened file is never closed; potential resource leak}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits