24 using namespace clang;
31 enum Kind { Opened, Closed, OpenFailed, Escaped } K;
33 StreamState(
Kind k) : K(k) {}
35 bool isOpened()
const {
return K == Opened; }
36 bool isClosed()
const {
return K == Closed; }
40 bool operator==(
const StreamState &
X)
const {
return K == X.K; }
42 static StreamState getOpened() {
return StreamState(Opened); }
43 static StreamState getClosed() {
return StreamState(Closed); }
44 static StreamState getOpenFailed() {
return StreamState(OpenFailed); }
45 static StreamState getEscaped() {
return StreamState(Escaped); }
47 void Profile(llvm::FoldingSetNodeID &
ID)
const {
52 class StreamChecker :
public Checker<eval::Call,
53 check::DeadSymbols > {
54 mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
55 BT_doubleclose, BT_ResourceLeak;
58 bool evalCall(
const CallEvent &Call, CheckerContext &C)
const;
59 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C)
const;
62 using FnCheck = std::function<void(
const StreamChecker *,
const CallEvent &,
66 {{
"fopen"}, &StreamChecker::evalFopen},
67 {{
"freopen", 3}, &StreamChecker::evalFreopen},
68 {{
"tmpfile"}, &StreamChecker::evalFopen},
69 {{
"fclose", 1}, &StreamChecker::evalFclose},
71 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)},
73 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)},
74 {{
"fseek", 3}, &StreamChecker::evalFseek},
76 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
78 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
80 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
82 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
84 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
86 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
88 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
90 std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
93 void evalFopen(
const CallEvent &Call, CheckerContext &C)
const;
94 void evalFreopen(
const CallEvent &Call, CheckerContext &C)
const;
95 void evalFclose(
const CallEvent &Call, CheckerContext &C)
const;
96 void evalFseek(
const CallEvent &Call, CheckerContext &C)
const;
98 void checkArgNullStream(
const CallEvent &Call, CheckerContext &C,
100 bool checkNullStream(SVal SV, CheckerContext &C,
102 void checkFseekWhence(SVal SV, CheckerContext &C,
104 bool checkDoubleClose(
const CallEvent &Call, CheckerContext &C,
113 bool StreamChecker::evalCall(
const CallEvent &Call, CheckerContext &C)
const {
114 const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
120 if (!Call.isGlobalCFunction())
122 for (
auto P : Call.parameters()) {
128 const FnCheck *Callback = Callbacks.lookup(Call);
132 (*Callback)(
this, Call, C);
134 return C.isDifferent();
137 void StreamChecker::evalFopen(
const CallEvent &Call, CheckerContext &C)
const {
139 SValBuilder &svalBuilder = C.getSValBuilder();
140 const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
141 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
146 svalBuilder.conjureSymbolVal(
nullptr, CE, LCtx, C.blockCount())
147 .castAs<DefinedSVal>();
148 state = state->BindExpr(CE, C.getLocationContext(), RetVal);
150 ConstraintManager &CM = C.getConstraintManager();
154 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
157 assert(Sym &&
"RetVal must be a symbol here.");
158 stateNotNull = stateNotNull->set<StreamMap>(Sym, StreamState::getOpened());
159 stateNull = stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed());
161 C.addTransition(stateNotNull);
162 C.addTransition(stateNull);
165 void StreamChecker::evalFreopen(
const CallEvent &Call,
166 CheckerContext &C)
const {
169 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
178 checkNullStream(*StreamVal, C, State);
183 SymbolRef StreamSym = StreamVal->getAsSymbol();
193 State->BindExpr(CE, C.getLocationContext(), *StreamVal);
196 ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(),
197 C.getSValBuilder().makeNull());
200 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened());
202 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed());
204 C.addTransition(StateRetNotNull);
205 C.addTransition(StateRetNull);
208 void StreamChecker::evalFclose(
const CallEvent &Call, CheckerContext &C)
const {
210 if (checkDoubleClose(Call, C, State))
211 C.addTransition(State);
214 void StreamChecker::evalFseek(
const CallEvent &Call, CheckerContext &C)
const {
215 const Expr *AE2 = Call.getArgExpr(2);
221 bool StateChanged = checkNullStream(Call.getArgSVal(0), C,
State);
227 checkFseekWhence(State->getSVal(AE2, C.getLocationContext()), C, State);
229 if (!C.isDifferent() && StateChanged)
230 C.addTransition(State);
235 void StreamChecker::checkArgNullStream(
const CallEvent &Call, CheckerContext &C,
236 unsigned ArgI)
const {
238 if (checkNullStream(Call.getArgSVal(ArgI), C,
State))
239 C.addTransition(State);
242 bool StreamChecker::checkNullStream(SVal SV, CheckerContext &C,
248 ConstraintManager &CM = C.getConstraintManager();
250 std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *DV);
252 if (!StateNotNull && StateNull) {
253 if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
255 BT_nullfp.reset(
new BuiltinBug(
this,
"NULL stream pointer",
256 "Stream pointer might be NULL."));
257 C.emitReport(std::make_unique<PathSensitiveBugReport>(
258 *BT_nullfp, BT_nullfp->getDescription(), N));
264 State = StateNotNull;
271 void StreamChecker::checkFseekWhence(SVal SV, CheckerContext &C,
277 int64_t
X = CI->getValue().getSExtValue();
278 if (X >= 0 && X <= 2)
281 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
282 if (!BT_illegalwhence)
283 BT_illegalwhence.reset(
284 new BuiltinBug(
this,
"Illegal whence argument",
285 "The whence argument to fseek() should be " 286 "SEEK_SET, SEEK_END, or SEEK_CUR."));
287 C.emitReport(std::make_unique<PathSensitiveBugReport>(
288 *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
292 bool StreamChecker::checkDoubleClose(
const CallEvent &Call, CheckerContext &C,
294 SymbolRef Sym = Call.getArgSVal(0).getAsSymbol();
298 const StreamState *SS = State->get<StreamMap>(Sym);
306 if (SS->isClosed()) {
307 ExplodedNode *N = C.generateErrorNode();
310 BT_doubleclose.reset(
new BuiltinBug(
311 this,
"Double fclose",
"Try to close a file Descriptor already" 312 " closed. Cause undefined behaviour."));
313 C.emitReport(std::make_unique<PathSensitiveBugReport>(
314 *BT_doubleclose, BT_doubleclose->getDescription(), N));
320 State = State->set<StreamMap>(Sym, StreamState::getClosed());
325 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
326 CheckerContext &C)
const {
330 const StreamMapTy &Map = State->get<StreamMap>();
331 for (
const auto &I: Map) {
333 const StreamState &SS = I.second;
334 if (!SymReaper.isDead(Sym) || !SS.isOpened())
337 ExplodedNode *N = C.generateErrorNode();
341 if (!BT_ResourceLeak)
342 BT_ResourceLeak.reset(
343 new BuiltinBug(
this,
"Resource Leak",
344 "Opened File never closed. Potential Resource leak."));
345 C.emitReport(std::make_unique<PathSensitiveBugReport>(
346 *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
350 void ento::registerStreamChecker(CheckerManager &mgr) {
351 mgr.registerChecker<StreamChecker>();
354 bool ento::shouldRegisterStreamChecker(
const LangOptions &LO) {
A (possibly-)qualified type.
bool operator==(CanQual< T > x, CanQual< U > y)
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
constexpr XRayInstrMask Function
const SymExpr * SymbolRef
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
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
This represents one expression.
#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.
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
An immutable map from CallDescriptions to arbitrary data.
bool isPointerType() const