23 using namespace clang;
29 enum Kind { Opened, Closed, OpenFailed, Escaped } K;
32 StreamState(
Kind k,
const Stmt *s) : K(k), S(s) {}
34 bool isOpened()
const {
return K == Opened; }
35 bool isClosed()
const {
return K == Closed; }
40 return K == X.K && S == X.S;
43 static StreamState getOpened(
const Stmt *s) {
return StreamState(Opened, s); }
44 static StreamState getClosed(
const Stmt *s) {
return StreamState(Closed, s); }
45 static StreamState getOpenFailed(
const Stmt *s) {
46 return StreamState(OpenFailed, s);
48 static StreamState getEscaped(
const Stmt *s) {
49 return StreamState(Escaped, s);
52 void Profile(llvm::FoldingSetNodeID &
ID)
const {
58 class StreamChecker :
public Checker<eval::Call,
59 check::DeadSymbols > {
60 mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
62 *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
63 *II_clearerr, *II_feof, *II_ferror, *II_fileno;
64 mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
65 BT_doubleclose, BT_ResourceLeak;
69 : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr),
70 II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr),
71 II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr),
72 II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr),
73 II_ferror(nullptr), II_fileno(nullptr) {}
75 bool evalCall(
const CallEvent &Call, CheckerContext &C)
const;
76 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C)
const;
79 void Fopen(CheckerContext &C,
const CallExpr *CE)
const;
80 void Tmpfile(CheckerContext &C,
const CallExpr *CE)
const;
81 void Fclose(CheckerContext &C,
const CallExpr *CE)
const;
82 void Fread(CheckerContext &C,
const CallExpr *CE)
const;
83 void Fwrite(CheckerContext &C,
const CallExpr *CE)
const;
84 void Fseek(CheckerContext &C,
const CallExpr *CE)
const;
85 void Ftell(CheckerContext &C,
const CallExpr *CE)
const;
86 void Rewind(CheckerContext &C,
const CallExpr *CE)
const;
87 void Fgetpos(CheckerContext &C,
const CallExpr *CE)
const;
88 void Fsetpos(CheckerContext &C,
const CallExpr *CE)
const;
89 void Clearerr(CheckerContext &C,
const CallExpr *CE)
const;
90 void Feof(CheckerContext &C,
const CallExpr *CE)
const;
91 void Ferror(CheckerContext &C,
const CallExpr *CE)
const;
92 void Fileno(CheckerContext &C,
const CallExpr *CE)
const;
94 void OpenFileAux(CheckerContext &C,
const CallExpr *CE)
const;
97 CheckerContext &C)
const;
99 CheckerContext &C)
const;
107 bool StreamChecker::evalCall(
const CallEvent &Call, CheckerContext &C)
const {
108 const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
112 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
138 II_clearerr = &Ctx.
Idents.
get(
"clearerr");
146 if (FD->getIdentifier() == II_fopen) {
150 if (FD->getIdentifier() == II_tmpfile) {
154 if (FD->getIdentifier() == II_fclose) {
158 if (FD->getIdentifier() == II_fread) {
162 if (FD->getIdentifier() == II_fwrite) {
166 if (FD->getIdentifier() == II_fseek) {
170 if (FD->getIdentifier() == II_ftell) {
174 if (FD->getIdentifier() == II_rewind) {
178 if (FD->getIdentifier() == II_fgetpos) {
182 if (FD->getIdentifier() == II_fsetpos) {
186 if (FD->getIdentifier() == II_clearerr) {
190 if (FD->getIdentifier() == II_feof) {
194 if (FD->getIdentifier() == II_ferror) {
198 if (FD->getIdentifier() == II_fileno) {
206 void StreamChecker::Fopen(CheckerContext &C,
const CallExpr *CE)
const {
210 void StreamChecker::Tmpfile(CheckerContext &C,
const CallExpr *CE)
const {
214 void StreamChecker::OpenFileAux(CheckerContext &C,
const CallExpr *CE)
const {
216 SValBuilder &svalBuilder = C.getSValBuilder();
217 const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
218 DefinedSVal RetVal = svalBuilder.conjureSymbolVal(
nullptr, CE, LCtx,
220 .castAs<DefinedSVal>();
221 state = state->BindExpr(CE, C.getLocationContext(), RetVal);
223 ConstraintManager &CM = C.getConstraintManager();
227 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
229 if (
SymbolRef Sym = RetVal.getAsSymbol()) {
232 stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE));
234 stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE));
236 C.addTransition(stateNotNull);
237 C.addTransition(stateNull);
241 void StreamChecker::Fclose(CheckerContext &C,
const CallExpr *CE)
const {
244 C.addTransition(state);
247 void StreamChecker::Fread(CheckerContext &C,
const CallExpr *CE)
const {
249 if (!CheckNullStream(C.getSVal(CE->
getArg(3)), state, C))
253 void StreamChecker::Fwrite(CheckerContext &C,
const CallExpr *CE)
const {
255 if (!CheckNullStream(C.getSVal(CE->
getArg(3)), state, C))
259 void StreamChecker::Fseek(CheckerContext &C,
const CallExpr *CE)
const {
261 if (!(state = CheckNullStream(C.getSVal(CE->
getArg(0)), state, C)))
264 SVal Whence = state->getSVal(CE->
getArg(2), C.getLocationContext());
270 int64_t x = CI->getValue().getSExtValue();
271 if (x >= 0 && x <= 2)
274 if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
275 if (!BT_illegalwhence)
276 BT_illegalwhence.reset(
277 new BuiltinBug(
this,
"Illegal whence argument",
278 "The whence argument to fseek() should be " 279 "SEEK_SET, SEEK_END, or SEEK_CUR."));
280 C.emitReport(llvm::make_unique<BugReport>(
281 *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
285 void StreamChecker::Ftell(CheckerContext &C,
const CallExpr *CE)
const {
287 if (!CheckNullStream(C.getSVal(CE->
getArg(0)), state, C))
291 void StreamChecker::Rewind(CheckerContext &C,
const CallExpr *CE)
const {
293 if (!CheckNullStream(C.getSVal(CE->
getArg(0)), state, C))
297 void StreamChecker::Fgetpos(CheckerContext &C,
const CallExpr *CE)
const {
299 if (!CheckNullStream(C.getSVal(CE->
getArg(0)), state, C))
303 void StreamChecker::Fsetpos(CheckerContext &C,
const CallExpr *CE)
const {
305 if (!CheckNullStream(C.getSVal(CE->
getArg(0)), state, C))
309 void StreamChecker::Clearerr(CheckerContext &C,
const CallExpr *CE)
const {
311 if (!CheckNullStream(C.getSVal(CE->
getArg(0)), state, C))
315 void StreamChecker::Feof(CheckerContext &C,
const CallExpr *CE)
const {
317 if (!CheckNullStream(C.getSVal(CE->
getArg(0)), state, C))
321 void StreamChecker::Ferror(CheckerContext &C,
const CallExpr *CE)
const {
323 if (!CheckNullStream(C.getSVal(CE->
getArg(0)), state, C))
327 void StreamChecker::Fileno(CheckerContext &C,
const CallExpr *CE)
const {
329 if (!CheckNullStream(C.getSVal(CE->
getArg(0)), state, C))
334 CheckerContext &C)
const {
339 ConstraintManager &CM = C.getConstraintManager();
341 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
343 if (!stateNotNull && stateNull) {
344 if (ExplodedNode *N = C.generateErrorNode(stateNull)) {
346 BT_nullfp.reset(
new BuiltinBug(
this,
"NULL stream pointer",
347 "Stream pointer might be NULL."));
348 C.emitReport(llvm::make_unique<BugReport>(
349 *BT_nullfp, BT_nullfp->getDescription(), N));
358 CheckerContext &C)
const {
363 const StreamState *SS = state->get<StreamMap>(Sym);
371 if (SS->isClosed()) {
372 ExplodedNode *N = C.generateErrorNode();
375 BT_doubleclose.reset(
new BuiltinBug(
376 this,
"Double fclose",
"Try to close a file Descriptor already" 377 " closed. Cause undefined behaviour."));
378 C.emitReport(llvm::make_unique<BugReport>(
379 *BT_doubleclose, BT_doubleclose->getDescription(), N));
385 return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
388 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
389 CheckerContext &C)
const {
393 const StreamMapTy &Map = state->get<StreamMap>();
394 for (
const auto &I: Map) {
396 const StreamState &SS = I.second;
397 if (!SymReaper.isDead(Sym) || !SS.isOpened())
400 ExplodedNode *N = C.generateErrorNode();
404 if (!BT_ResourceLeak)
405 BT_ResourceLeak.reset(
406 new BuiltinBug(
this,
"Resource Leak",
407 "Opened File never closed. Potential Resource leak."));
408 C.emitReport(llvm::make_unique<BugReport>(
409 *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
413 void ento::registerStreamChecker(CheckerManager &mgr) {
414 mgr.registerChecker<StreamChecker>();
417 bool ento::shouldRegisterStreamChecker(
const LangOptions &LO) {
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
bool operator==(CanQual< T > x, CanQual< U > y)
const SymExpr * SymbolRef
Stmt - This represents one statement.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
constexpr XRayInstrMask Function
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
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
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
#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 ...
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).