24 using namespace clang;
32 enum Kind { Opened, Closed } K;
33 StreamState(
Kind InK) : K(InK) { }
36 bool isOpened()
const {
return K == Opened; }
37 bool isClosed()
const {
return K == Closed; }
39 static StreamState getOpened() {
return StreamState(Opened); }
40 static StreamState getClosed() {
return StreamState(Closed); }
45 void Profile(llvm::FoldingSetNodeID &
ID)
const {
50 class SimpleStreamChecker :
public Checker<check::PostCall,
53 check::PointerEscape> {
56 std::unique_ptr<BugType> DoubleCloseBugType;
57 std::unique_ptr<BugType> LeakBugType;
59 void reportDoubleClose(
SymbolRef FileDescSym,
61 CheckerContext &C)
const;
64 ExplodedNode *ErrNode)
const;
66 bool guaranteedNotToCloseFile(
const CallEvent &Call)
const;
69 SimpleStreamChecker();
72 void checkPostCall(
const CallEvent &Call, CheckerContext &C)
const;
74 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
76 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C)
const;
92 class StopTrackingCallback final :
public SymbolVisitor {
98 bool VisitSymbol(
SymbolRef sym)
override {
99 state = state->remove<StreamMap>(sym);
105 SimpleStreamChecker::SimpleStreamChecker()
106 : OpenFn(
"fopen"), CloseFn(
"fclose", 1) {
108 DoubleCloseBugType.reset(
109 new BugType(
this,
"Double fclose",
"Unix Stream API Error"));
113 new BugType(
this,
"Resource Leak",
"Unix Stream API Error",
117 void SimpleStreamChecker::checkPostCall(
const CallEvent &Call,
118 CheckerContext &
C)
const {
119 if (!Call.isGlobalCFunction())
122 if (!Call.isCalled(OpenFn))
126 SymbolRef FileDesc = Call.getReturnValue().getAsSymbol();
132 State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
133 C.addTransition(State);
136 void SimpleStreamChecker::checkPreCall(
const CallEvent &Call,
137 CheckerContext &C)
const {
138 if (!Call.isGlobalCFunction())
141 if (!Call.isCalled(CloseFn))
145 SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol();
151 const StreamState *SS = State->get<StreamMap>(FileDesc);
152 if (SS && SS->isClosed()) {
153 reportDoubleClose(FileDesc, Call, C);
158 State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
159 C.addTransition(State);
164 if (IsSymDead && SS.isOpened()) {
167 ConstraintManager &CMgr = State->getConstraintManager();
168 ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym);
169 return !OpenFailed.isConstrainedTrue();
174 void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
175 CheckerContext &C)
const {
177 SymbolVector LeakedStreams;
178 StreamMapTy TrackedStreams = State->get<StreamMap>();
179 for (StreamMapTy::iterator I = TrackedStreams.begin(),
180 E = TrackedStreams.end(); I != E; ++I) {
182 bool IsSymDead = SymReaper.isDead(Sym);
185 if (
isLeaked(Sym, I->second, IsSymDead, State))
186 LeakedStreams.push_back(Sym);
190 State = State->remove<StreamMap>(Sym);
193 ExplodedNode *N = C.generateNonFatalErrorNode(State);
196 reportLeaks(LeakedStreams, C, N);
199 void SimpleStreamChecker::reportDoubleClose(
SymbolRef FileDescSym,
201 CheckerContext &C)
const {
203 ExplodedNode *ErrNode = C.generateErrorNode();
209 auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType,
210 "Closing a previously closed file stream", ErrNode);
211 R->addRange(Call.getSourceRange());
212 R->markInteresting(FileDescSym);
213 C.emitReport(std::move(R));
218 ExplodedNode *ErrNode)
const {
221 for (
SymbolRef LeakedStream : LeakedStreams) {
222 auto R = llvm::make_unique<BugReport>(*LeakBugType,
223 "Opened file is never closed; potential resource leak", ErrNode);
224 R->markInteresting(LeakedStream);
225 C.emitReport(std::move(R));
229 bool SimpleStreamChecker::guaranteedNotToCloseFile(
const CallEvent &Call)
const{
231 if (!Call.isInSystemHeader())
235 if (Call.argumentsMayEscape())
256 for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
263 State = State->remove<StreamMap>(Sym);
268 void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
269 mgr.registerChecker<SimpleStreamChecker>();
273 bool ento::shouldRegisterSimpleStreamChecker(
const LangOptions &LO) {
bool operator==(CanQual< T > x, CanQual< U > y)
llvm::DenseSet< SymbolRef > InvalidatedSymbols
const SymExpr * SymbolRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
static bool isLeaked(SymbolRef Sym, const StreamState &SS, bool IsSymDead, ProgramStateRef State)
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
i32 captured_struct **param SharedsTy A type which contains references the shared variables *param Shareds Context with the list of shared variables from the p *TaskFunction *param Data Additional data for task generation like final * state
This class represents a description of a function call using the number of arguments and the name of ...
The pointer has been passed to a function call directly.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Dataflow Directional Tag Classes.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...