SimpleStreamChecker.cpp revision edd07f40ce13eb64537e9bd3af2bec4847a90fb2
1d65e55d691655462880ffd51c10784955ab6a362Anna Zaks//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--// 2d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 3d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// The LLVM Compiler Infrastructure 4d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 5d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// This file is distributed under the University of Illinois Open Source 6d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// License. See LICENSE.TXT for details. 7d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 8d65e55d691655462880ffd51c10784955ab6a362Anna Zaks//===----------------------------------------------------------------------===// 9d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 10d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// Defines a checker for proper use of fopen/fclose APIs. 11d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// - If a file has been closed with fclose, it should not be accessed again. 12d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// Accessing a closed file results in undefined behavior. 13d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// - If a file was opened with fopen, it must be closed with fclose before 14d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// the execution ends. Failing to do so results in a resource leak. 15d65e55d691655462880ffd51c10784955ab6a362Anna Zaks// 16d65e55d691655462880ffd51c10784955ab6a362Anna Zaks//===----------------------------------------------------------------------===// 17d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 18d65e55d691655462880ffd51c10784955ab6a362Anna Zaks#include "ClangSACheckers.h" 19d65e55d691655462880ffd51c10784955ab6a362Anna Zaks#include "clang/StaticAnalyzer/Core/Checker.h" 20d65e55d691655462880ffd51c10784955ab6a362Anna Zaks#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 21d65e55d691655462880ffd51c10784955ab6a362Anna Zaks#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 22d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 23d65e55d691655462880ffd51c10784955ab6a362Anna Zaksusing namespace clang; 24d65e55d691655462880ffd51c10784955ab6a362Anna Zaksusing namespace ento; 25d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 26d65e55d691655462880ffd51c10784955ab6a362Anna Zaksnamespace { 27d65e55d691655462880ffd51c10784955ab6a362Anna Zakstypedef llvm::SmallVector<SymbolRef, 2> SymbolVector; 28d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 29d65e55d691655462880ffd51c10784955ab6a362Anna Zaksstruct StreamState { 3032133cfb333510ba94aff040067713c0b32d58c5Anna Zaksprivate: 31d65e55d691655462880ffd51c10784955ab6a362Anna Zaks enum Kind { Opened, Closed } K; 32d65e55d691655462880ffd51c10784955ab6a362Anna Zaks StreamState(Kind InK) : K(InK) { } 33d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 3432133cfb333510ba94aff040067713c0b32d58c5Anna Zakspublic: 35d65e55d691655462880ffd51c10784955ab6a362Anna Zaks bool isOpened() const { return K == Opened; } 36d65e55d691655462880ffd51c10784955ab6a362Anna Zaks bool isClosed() const { return K == Closed; } 37d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 38d65e55d691655462880ffd51c10784955ab6a362Anna Zaks static StreamState getOpened() { return StreamState(Opened); } 39d65e55d691655462880ffd51c10784955ab6a362Anna Zaks static StreamState getClosed() { return StreamState(Closed); } 40d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 41d65e55d691655462880ffd51c10784955ab6a362Anna Zaks bool operator==(const StreamState &X) const { 42d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return K == X.K; 43d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 44d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void Profile(llvm::FoldingSetNodeID &ID) const { 45d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ID.AddInteger(K); 46d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 47d65e55d691655462880ffd51c10784955ab6a362Anna Zaks}; 48d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 4932f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Roseclass SimpleStreamChecker : public Checker<check::PostStmt<CallExpr>, 5032f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose check::PreStmt<CallExpr>, 5132f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose check::DeadSymbols > { 52d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 53d65e55d691655462880ffd51c10784955ab6a362Anna Zaks mutable IdentifierInfo *IIfopen, *IIfclose; 54d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 5532f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose OwningPtr<BugType> DoubleCloseBugType; 5632f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose OwningPtr<BugType> LeakBugType; 57d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 58d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void initIdentifierInfo(ASTContext &Ctx) const; 59d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 60d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void reportDoubleClose(SymbolRef FileDescSym, 61d65e55d691655462880ffd51c10784955ab6a362Anna Zaks const CallExpr *Call, 62d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const; 63d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 6432f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose void reportLeaks(SymbolVector LeakedStreams, 6532f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose CheckerContext &C, 6632f38c11642ddeaeb6c4ffb1e589ab444c825f6eJordan Rose ExplodedNode *ErrNode) const; 67d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 68d65e55d691655462880ffd51c10784955ab6a362Anna Zakspublic: 6932133cfb333510ba94aff040067713c0b32d58c5Anna Zaks SimpleStreamChecker(); 70d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 71d65e55d691655462880ffd51c10784955ab6a362Anna Zaks /// Process fopen. 72d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void checkPostStmt(const CallExpr *Call, CheckerContext &C) const; 73d65e55d691655462880ffd51c10784955ab6a362Anna Zaks /// Process fclose. 74d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void checkPreStmt(const CallExpr *Call, CheckerContext &C) const; 75d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 76d65e55d691655462880ffd51c10784955ab6a362Anna Zaks void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 77d65e55d691655462880ffd51c10784955ab6a362Anna Zaks}; 78d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 79d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} // end anonymous namespace 80d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 81d65e55d691655462880ffd51c10784955ab6a362Anna Zaks/// The state of the checker is a map from tracked stream symbols to their 82ac150f2619efcadbf23acd6e86695b5412723eb1Anna Zaks/// state. Let's store it in the ProgramState. 83ac150f2619efcadbf23acd6e86695b5412723eb1Anna ZaksREGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 84d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 8532133cfb333510ba94aff040067713c0b32d58c5Anna ZaksSimpleStreamChecker::SimpleStreamChecker() : IIfopen(0), IIfclose(0) { 8632133cfb333510ba94aff040067713c0b32d58c5Anna Zaks // Initialize the bug types. 8732133cfb333510ba94aff040067713c0b32d58c5Anna Zaks DoubleCloseBugType.reset(new BugType("Double fclose", 8832133cfb333510ba94aff040067713c0b32d58c5Anna Zaks "Unix Stream API Error")); 8932133cfb333510ba94aff040067713c0b32d58c5Anna Zaks 9032133cfb333510ba94aff040067713c0b32d58c5Anna Zaks LeakBugType.reset(new BugType("Resource Leak", 9132133cfb333510ba94aff040067713c0b32d58c5Anna Zaks "Unix Stream API Error")); 9232133cfb333510ba94aff040067713c0b32d58c5Anna Zaks // Sinks are higher importance bugs as well as calls to assert() or exit(0). 9332133cfb333510ba94aff040067713c0b32d58c5Anna Zaks LeakBugType->setSuppressOnSink(true); 9432133cfb333510ba94aff040067713c0b32d58c5Anna Zaks} 9532133cfb333510ba94aff040067713c0b32d58c5Anna Zaks 96d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::checkPostStmt(const CallExpr *Call, 97d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 98d65e55d691655462880ffd51c10784955ab6a362Anna Zaks initIdentifierInfo(C.getASTContext()); 99d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 100d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (C.getCalleeIdentifier(Call) != IIfopen) 101d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 102d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 103d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Get the symbolic value corresponding to the file handle. 104d65e55d691655462880ffd51c10784955ab6a362Anna Zaks SymbolRef FileDesc = C.getSVal(Call).getAsSymbol(); 105d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (!FileDesc) 106d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 107d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 108d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Generate the next transition (an edge in the exploded graph). 109d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ProgramStateRef State = C.getState(); 110d65e55d691655462880ffd51c10784955ab6a362Anna Zaks State = State->set<StreamMap>(FileDesc, StreamState::getOpened()); 111d65e55d691655462880ffd51c10784955ab6a362Anna Zaks C.addTransition(State); 112d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 113d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 114d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::checkPreStmt(const CallExpr *Call, 115d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 116d65e55d691655462880ffd51c10784955ab6a362Anna Zaks initIdentifierInfo(C.getASTContext()); 117d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 118360b29c52a4c10f9d4c031d84d962ed2a4d58263Anna Zaks if (C.getCalleeIdentifier(Call) != IIfclose || Call->getNumArgs() != 1) 119d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 120d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 121d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Get the symbolic value corresponding to the file handle. 122d65e55d691655462880ffd51c10784955ab6a362Anna Zaks SymbolRef FileDesc = C.getSVal(Call->getArg(0)).getAsSymbol(); 123d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (!FileDesc) 124d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 125d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 126d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Check if the stream has already been closed. 127d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ProgramStateRef State = C.getState(); 128d65e55d691655462880ffd51c10784955ab6a362Anna Zaks const StreamState *SS = State->get<StreamMap>(FileDesc); 129bbb751a1788c461bc9765ec3387536cad6b52619Anna Zaks if (SS && SS->isClosed()) { 130d65e55d691655462880ffd51c10784955ab6a362Anna Zaks reportDoubleClose(FileDesc, Call, C); 131bbb751a1788c461bc9765ec3387536cad6b52619Anna Zaks return; 132bbb751a1788c461bc9765ec3387536cad6b52619Anna Zaks } 133d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 134d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Generate the next transition, in which the stream is closed. 135d65e55d691655462880ffd51c10784955ab6a362Anna Zaks State = State->set<StreamMap>(FileDesc, StreamState::getClosed()); 136d65e55d691655462880ffd51c10784955ab6a362Anna Zaks C.addTransition(State); 137d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 138d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 139edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaksstatic bool isLeaked(SymbolRef Sym, const StreamState &SS, 140edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks bool IsSymDead, ProgramStateRef State) { 141edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks if (IsSymDead && SS.isOpened()) { 142edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks // If a symbol is NULL, assume that fopen failed on this path. 143edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks // A symbol should only be considered leaked if it is non-null. 144edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks ConstraintManager &CMgr = State->getConstraintManager(); 145edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym); 146edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks return !OpenFailed.isConstrainedTrue(); 147edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks } 148edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks return false; 149edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks} 150edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks 151d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 152d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 153d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ProgramStateRef State = C.getState(); 154d65e55d691655462880ffd51c10784955ab6a362Anna Zaks SymbolVector LeakedStreams; 155edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks StreamMapTy TrackedStreams = State->get<StreamMap>(); 156360b29c52a4c10f9d4c031d84d962ed2a4d58263Anna Zaks for (StreamMapTy::iterator I = TrackedStreams.begin(), 157ec8d420d4fa57fc6b5a5a2b1446742e976a7ba00Jordan Rose E = TrackedStreams.end(); I != E; ++I) { 158d65e55d691655462880ffd51c10784955ab6a362Anna Zaks SymbolRef Sym = I->first; 159edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks bool IsSymDead = SymReaper.isDead(Sym); 160edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks 161edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks // Collect leaked symbols. 162edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks if (isLeaked(Sym, I->second, IsSymDead, State)) 163edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks LeakedStreams.push_back(Sym); 164edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks 165edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks // Remove the dead symbol from the streams map. 166edd07f40ce13eb64537e9bd3af2bec4847a90fb2Anna Zaks if (IsSymDead) 167d65e55d691655462880ffd51c10784955ab6a362Anna Zaks State = State->remove<StreamMap>(Sym); 168d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 169d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 17032133cfb333510ba94aff040067713c0b32d58c5Anna Zaks ExplodedNode *N = C.addTransition(State); 17132133cfb333510ba94aff040067713c0b32d58c5Anna Zaks reportLeaks(LeakedStreams, C, N); 172d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 173d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 174d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, 175d65e55d691655462880ffd51c10784955ab6a362Anna Zaks const CallExpr *CallExpr, 176d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 177d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // We reached a bug, stop exploring the path here by generating a sink. 178d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ExplodedNode *ErrNode = C.generateSink(); 17932133cfb333510ba94aff040067713c0b32d58c5Anna Zaks // If we've already reached this node on another path, return. 180d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (!ErrNode) 181d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 182d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 183d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Generate the report. 184d65e55d691655462880ffd51c10784955ab6a362Anna Zaks BugReport *R = new BugReport(*DoubleCloseBugType, 185d65e55d691655462880ffd51c10784955ab6a362Anna Zaks "Closing a previously closed file stream", ErrNode); 186d65e55d691655462880ffd51c10784955ab6a362Anna Zaks R->addRange(CallExpr->getSourceRange()); 187d65e55d691655462880ffd51c10784955ab6a362Anna Zaks R->markInteresting(FileDescSym); 188785950e59424dca7ce0081bebf13c0acd2c4fff6Jordan Rose C.emitReport(R); 189d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 190d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 19132133cfb333510ba94aff040067713c0b32d58c5Anna Zaksvoid SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams, 19232133cfb333510ba94aff040067713c0b32d58c5Anna Zaks CheckerContext &C, 19332133cfb333510ba94aff040067713c0b32d58c5Anna Zaks ExplodedNode *ErrNode) const { 194d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Attach bug reports to the leak node. 195bdbb17b81ca02f0279909836668420351b7f24c1Anna Zaks // TODO: Identify the leaked file descriptor. 196d65e55d691655462880ffd51c10784955ab6a362Anna Zaks for (llvm::SmallVector<SymbolRef, 2>::iterator 197d65e55d691655462880ffd51c10784955ab6a362Anna Zaks I = LeakedStreams.begin(), E = LeakedStreams.end(); I != E; ++I) { 198d65e55d691655462880ffd51c10784955ab6a362Anna Zaks BugReport *R = new BugReport(*LeakBugType, 199d65e55d691655462880ffd51c10784955ab6a362Anna Zaks "Opened file is never closed; potential resource leak", ErrNode); 200bdbb17b81ca02f0279909836668420351b7f24c1Anna Zaks R->markInteresting(*I); 201785950e59424dca7ce0081bebf13c0acd2c4fff6Jordan Rose C.emitReport(R); 202d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 203d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 204d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 205d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const { 206d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (IIfopen) 207d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 208d65e55d691655462880ffd51c10784955ab6a362Anna Zaks IIfopen = &Ctx.Idents.get("fopen"); 209d65e55d691655462880ffd51c10784955ab6a362Anna Zaks IIfclose = &Ctx.Idents.get("fclose"); 210d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 211d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 212d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid ento::registerSimpleStreamChecker(CheckerManager &mgr) { 213d65e55d691655462880ffd51c10784955ab6a362Anna Zaks mgr.registerChecker<SimpleStreamChecker>(); 214d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 215