25 using namespace clang;
33 enum Kind { Opened, Closed } K;
34 StreamState(
Kind InK) : K(InK) { }
37 bool isOpened()
const {
return K == Opened; }
38 bool isClosed()
const {
return K == Closed; }
40 static StreamState getOpened() {
return StreamState(Opened); }
41 static StreamState getClosed() {
return StreamState(Closed); }
46 void Profile(llvm::FoldingSetNodeID &
ID)
const {
51 class SimpleStreamChecker :
public Checker<check::PostCall,
54 check::PointerEscape> {
55 CallDescription OpenFn, CloseFn;
57 std::unique_ptr<BugType> DoubleCloseBugType;
58 std::unique_ptr<BugType> LeakBugType;
60 void reportDoubleClose(
SymbolRef FileDescSym,
62 CheckerContext &C)
const;
65 ExplodedNode *ErrNode)
const;
67 bool guaranteedNotToCloseFile(
const CallEvent &Call)
const;
70 SimpleStreamChecker();
73 void checkPostCall(
const CallEvent &Call, CheckerContext &C)
const;
75 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
77 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C)
const;
93 class StopTrackingCallback final :
public SymbolVisitor {
99 bool VisitSymbol(
SymbolRef sym)
override {
100 state = state->remove<StreamMap>(sym);
106 SimpleStreamChecker::SimpleStreamChecker()
107 : OpenFn(
"fopen"), CloseFn(
"fclose", 1) {
109 DoubleCloseBugType.reset(
110 new BugType(
this,
"Double fclose",
"Unix Stream API Error"));
113 new BugType(
this,
"Resource Leak",
"Unix Stream API Error"));
115 LeakBugType->setSuppressOnSink(
true);
118 void SimpleStreamChecker::checkPostCall(
const CallEvent &Call,
119 CheckerContext &
C)
const {
120 if (!Call.isGlobalCFunction())
123 if (!Call.isCalled(OpenFn))
127 SymbolRef FileDesc = Call.getReturnValue().getAsSymbol();
133 State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
134 C.addTransition(State);
137 void SimpleStreamChecker::checkPreCall(
const CallEvent &Call,
138 CheckerContext &C)
const {
139 if (!Call.isGlobalCFunction())
142 if (!Call.isCalled(CloseFn))
146 SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol();
152 const StreamState *SS = State->get<StreamMap>(FileDesc);
153 if (SS && SS->isClosed()) {
154 reportDoubleClose(FileDesc, Call, C);
159 State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
160 C.addTransition(State);
165 if (IsSymDead && SS.isOpened()) {
168 ConstraintManager &CMgr = State->getConstraintManager();
169 ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym);
170 return !OpenFailed.isConstrainedTrue();
175 void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
176 CheckerContext &C)
const {
178 SymbolVector LeakedStreams;
179 StreamMapTy TrackedStreams = State->get<StreamMap>();
180 for (StreamMapTy::iterator I = TrackedStreams.begin(),
181 E = TrackedStreams.end(); I != E; ++I) {
183 bool IsSymDead = SymReaper.isDead(Sym);
186 if (
isLeaked(Sym, I->second, IsSymDead, State))
187 LeakedStreams.push_back(Sym);
191 State = State->remove<StreamMap>(Sym);
194 ExplodedNode *N = C.generateNonFatalErrorNode(State);
197 reportLeaks(LeakedStreams, C, N);
200 void SimpleStreamChecker::reportDoubleClose(
SymbolRef FileDescSym,
202 CheckerContext &C)
const {
204 ExplodedNode *ErrNode = C.generateErrorNode();
210 auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType,
211 "Closing a previously closed file stream", ErrNode);
212 R->addRange(Call.getSourceRange());
213 R->markInteresting(FileDescSym);
214 C.emitReport(std::move(R));
219 ExplodedNode *ErrNode)
const {
222 for (
SymbolRef LeakedStream : LeakedStreams) {
223 auto R = llvm::make_unique<BugReport>(*LeakBugType,
224 "Opened file is never closed; potential resource leak", ErrNode);
225 R->markInteresting(LeakedStream);
226 C.emitReport(std::move(R));
230 bool SimpleStreamChecker::guaranteedNotToCloseFile(
const CallEvent &Call)
const{
232 if (!Call.isInSystemHeader())
236 if (Call.argumentsMayEscape())
257 for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
264 State = State->remove<StreamMap>(Sym);
269 void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
270 mgr.registerChecker<SimpleStreamChecker>();
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)
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
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 ...