SimpleStreamChecker.cpp revision 32133cfb333510ba94aff040067713c0b32d58c5
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 49d65e55d691655462880ffd51c10784955ab6a362Anna Zaksclass SimpleStreamChecker: public Checker<check::PostStmt<CallExpr>, 50d65e55d691655462880ffd51c10784955ab6a362Anna Zaks check::PreStmt<CallExpr>, 5132133cfb333510ba94aff040067713c0b32d58c5Anna Zaks check::DeadSymbols > { 52d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 53d65e55d691655462880ffd51c10784955ab6a362Anna Zaks mutable IdentifierInfo *IIfopen, *IIfclose; 54d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 55d65e55d691655462880ffd51c10784955ab6a362Anna Zaks mutable OwningPtr<BugType> DoubleCloseBugType; 56d65e55d691655462880ffd51c10784955ab6a362Anna Zaks mutable 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 6432133cfb333510ba94aff040067713c0b32d58c5Anna Zaks void reportLeaks(SymbolVector LeakedStreams, 6532133cfb333510ba94aff040067713c0b32d58c5Anna Zaks CheckerContext &C, 6632133cfb333510ba94aff040067713c0b32d58c5Anna Zaks 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); 129d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (SS && SS->isClosed()) 130d65e55d691655462880ffd51c10784955ab6a362Anna Zaks reportDoubleClose(FileDesc, Call, C); 131d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 132d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Generate the next transition, in which the stream is closed. 133d65e55d691655462880ffd51c10784955ab6a362Anna Zaks State = State->set<StreamMap>(FileDesc, StreamState::getClosed()); 134d65e55d691655462880ffd51c10784955ab6a362Anna Zaks C.addTransition(State); 135d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 136d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 137d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 138d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 139d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ProgramStateRef State = C.getState(); 140360b29c52a4c10f9d4c031d84d962ed2a4d58263Anna Zaks StreamMapTy TrackedStreams = State->get<StreamMap>(); 141d65e55d691655462880ffd51c10784955ab6a362Anna Zaks SymbolVector LeakedStreams; 142360b29c52a4c10f9d4c031d84d962ed2a4d58263Anna Zaks for (StreamMapTy::iterator I = TrackedStreams.begin(), 143d65e55d691655462880ffd51c10784955ab6a362Anna Zaks E = TrackedStreams.end(); I != E; ++I) { 144d65e55d691655462880ffd51c10784955ab6a362Anna Zaks SymbolRef Sym = I->first; 145d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (SymReaper.isDead(Sym)) { 146d65e55d691655462880ffd51c10784955ab6a362Anna Zaks const StreamState &SS = I->second; 14732133cfb333510ba94aff040067713c0b32d58c5Anna Zaks if (SS.isOpened()) { 14832133cfb333510ba94aff040067713c0b32d58c5Anna Zaks // If a symbolic region is NULL, assume that allocation failed on 14932133cfb333510ba94aff040067713c0b32d58c5Anna Zaks // this path and do not report a leak. 15032133cfb333510ba94aff040067713c0b32d58c5Anna Zaks if (!State->getConstraintManager().isNull(State, Sym).isTrue()) 15132133cfb333510ba94aff040067713c0b32d58c5Anna Zaks LeakedStreams.push_back(Sym); 15232133cfb333510ba94aff040067713c0b32d58c5Anna Zaks } 153d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 154d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Remove the dead symbol from the streams map. 155d65e55d691655462880ffd51c10784955ab6a362Anna Zaks State = State->remove<StreamMap>(Sym); 156d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 157d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 158d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 15932133cfb333510ba94aff040067713c0b32d58c5Anna Zaks ExplodedNode *N = C.addTransition(State); 16032133cfb333510ba94aff040067713c0b32d58c5Anna Zaks reportLeaks(LeakedStreams, C, N); 161d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 162d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 163d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, 164d65e55d691655462880ffd51c10784955ab6a362Anna Zaks const CallExpr *CallExpr, 165d65e55d691655462880ffd51c10784955ab6a362Anna Zaks CheckerContext &C) const { 166d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // We reached a bug, stop exploring the path here by generating a sink. 167d65e55d691655462880ffd51c10784955ab6a362Anna Zaks ExplodedNode *ErrNode = C.generateSink(); 16832133cfb333510ba94aff040067713c0b32d58c5Anna Zaks // If we've already reached this node on another path, return. 169d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (!ErrNode) 170d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 171d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 172d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Generate the report. 173d65e55d691655462880ffd51c10784955ab6a362Anna Zaks BugReport *R = new BugReport(*DoubleCloseBugType, 174d65e55d691655462880ffd51c10784955ab6a362Anna Zaks "Closing a previously closed file stream", ErrNode); 175d65e55d691655462880ffd51c10784955ab6a362Anna Zaks R->addRange(CallExpr->getSourceRange()); 176d65e55d691655462880ffd51c10784955ab6a362Anna Zaks R->markInteresting(FileDescSym); 177d65e55d691655462880ffd51c10784955ab6a362Anna Zaks C.EmitReport(R); 178d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 179d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 18032133cfb333510ba94aff040067713c0b32d58c5Anna Zaksvoid SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams, 18132133cfb333510ba94aff040067713c0b32d58c5Anna Zaks CheckerContext &C, 18232133cfb333510ba94aff040067713c0b32d58c5Anna Zaks ExplodedNode *ErrNode) const { 183d65e55d691655462880ffd51c10784955ab6a362Anna Zaks // Attach bug reports to the leak node. 184bdbb17b81ca02f0279909836668420351b7f24c1Anna Zaks // TODO: Identify the leaked file descriptor. 185d65e55d691655462880ffd51c10784955ab6a362Anna Zaks for (llvm::SmallVector<SymbolRef, 2>::iterator 186d65e55d691655462880ffd51c10784955ab6a362Anna Zaks I = LeakedStreams.begin(), E = LeakedStreams.end(); I != E; ++I) { 187d65e55d691655462880ffd51c10784955ab6a362Anna Zaks BugReport *R = new BugReport(*LeakBugType, 188d65e55d691655462880ffd51c10784955ab6a362Anna Zaks "Opened file is never closed; potential resource leak", ErrNode); 189bdbb17b81ca02f0279909836668420351b7f24c1Anna Zaks R->markInteresting(*I); 190d65e55d691655462880ffd51c10784955ab6a362Anna Zaks C.EmitReport(R); 191d65e55d691655462880ffd51c10784955ab6a362Anna Zaks } 192d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 193d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 194d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const { 195d65e55d691655462880ffd51c10784955ab6a362Anna Zaks if (IIfopen) 196d65e55d691655462880ffd51c10784955ab6a362Anna Zaks return; 197d65e55d691655462880ffd51c10784955ab6a362Anna Zaks IIfopen = &Ctx.Idents.get("fopen"); 198d65e55d691655462880ffd51c10784955ab6a362Anna Zaks IIfclose = &Ctx.Idents.get("fclose"); 199d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 200d65e55d691655462880ffd51c10784955ab6a362Anna Zaks 201d65e55d691655462880ffd51c10784955ab6a362Anna Zaksvoid ento::registerSimpleStreamChecker(CheckerManager &mgr) { 202d65e55d691655462880ffd51c10784955ab6a362Anna Zaks mgr.registerChecker<SimpleStreamChecker>(); 203d65e55d691655462880ffd51c10784955ab6a362Anna Zaks} 204