2010-06-16 13:38:05 +08:00
|
|
|
//===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2010-06-16 13:38:05 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file defines checkers that model and check stream handling functions.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
[analyzer][NFC] Move CheckerRegistry from the Core directory to Frontend
ClangCheckerRegistry is a very non-obvious, poorly documented, weird concept.
It derives from CheckerRegistry, and is placed in lib/StaticAnalyzer/Frontend,
whereas it's base is located in lib/StaticAnalyzer/Core. It was, from what I can
imagine, used to circumvent the problem that the registry functions of the
checkers are located in the clangStaticAnalyzerCheckers library, but that
library depends on clangStaticAnalyzerCore. However, clangStaticAnalyzerFrontend
depends on both of those libraries.
One can make the observation however, that CheckerRegistry has no place in Core,
it isn't used there at all! The only place where it is used is Frontend, which
is where it ultimately belongs.
This move implies that since
include/clang/StaticAnalyzer/Checkers/ClangCheckers.h only contained a single function:
class CheckerRegistry;
void registerBuiltinCheckers(CheckerRegistry ®istry);
it had to re purposed, as CheckerRegistry is no longer available to
clangStaticAnalyzerCheckers. It was renamed to BuiltinCheckerRegistration.h,
which actually describes it a lot better -- it does not contain the registration
functions for checkers, but only those generated by the tblgen files.
Differential Revision: https://reviews.llvm.org/D54436
llvm-svn: 349275
2018-12-16 00:23:51 +08:00
|
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
2011-03-01 09:16:21 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
2011-02-18 05:39:17 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
2011-02-24 09:05:33 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2011-08-16 06:09:50 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
|
2011-02-10 09:03:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
|
2010-06-16 13:38:05 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2010-06-16 13:38:05 +08:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2010-07-19 09:52:29 +08:00
|
|
|
struct StreamState {
|
2010-07-23 22:14:59 +08:00
|
|
|
enum Kind { Opened, Closed, OpenFailed, Escaped } K;
|
2010-07-19 09:52:29 +08:00
|
|
|
const Stmt *S;
|
|
|
|
|
|
|
|
StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
|
|
|
|
|
|
|
|
bool isOpened() const { return K == Opened; }
|
|
|
|
bool isClosed() const { return K == Closed; }
|
2010-09-03 12:34:38 +08:00
|
|
|
//bool isOpenFailed() const { return K == OpenFailed; }
|
|
|
|
//bool isEscaped() const { return K == Escaped; }
|
2010-07-19 09:52:29 +08:00
|
|
|
|
|
|
|
bool operator==(const StreamState &X) const {
|
|
|
|
return K == X.K && S == X.S;
|
|
|
|
}
|
|
|
|
|
|
|
|
static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
|
|
|
|
static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
|
2015-09-08 11:50:52 +08:00
|
|
|
static StreamState getOpenFailed(const Stmt *s) {
|
|
|
|
return StreamState(OpenFailed, s);
|
2010-07-22 22:01:01 +08:00
|
|
|
}
|
2010-07-23 22:14:59 +08:00
|
|
|
static StreamState getEscaped(const Stmt *s) {
|
|
|
|
return StreamState(Escaped, s);
|
|
|
|
}
|
2010-07-19 09:52:29 +08:00
|
|
|
|
|
|
|
void Profile(llvm::FoldingSetNodeID &ID) const {
|
|
|
|
ID.AddInteger(K);
|
|
|
|
ID.AddPointer(S);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-03-01 09:16:21 +08:00
|
|
|
class StreamChecker : public Checker<eval::Call,
|
2012-11-16 03:11:38 +08:00
|
|
|
check::DeadSymbols > {
|
2011-02-24 09:05:33 +08:00
|
|
|
mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
|
2015-09-08 11:50:52 +08:00
|
|
|
*II_fwrite,
|
|
|
|
*II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
|
2010-06-22 15:50:21 +08:00
|
|
|
*II_clearerr, *II_feof, *II_ferror, *II_fileno;
|
2014-03-08 04:03:18 +08:00
|
|
|
mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
|
|
|
|
BT_doubleclose, BT_ResourceLeak;
|
2010-06-16 13:38:05 +08:00
|
|
|
|
|
|
|
public:
|
2015-09-08 11:50:52 +08:00
|
|
|
StreamChecker()
|
2014-05-27 10:45:47 +08:00
|
|
|
: II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr),
|
|
|
|
II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr),
|
|
|
|
II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr),
|
|
|
|
II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr),
|
|
|
|
II_ferror(nullptr), II_fileno(nullptr) {}
|
2010-06-16 13:38:05 +08:00
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
|
|
|
|
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
|
2010-06-16 13:38:05 +08:00
|
|
|
|
|
|
|
private:
|
2011-02-24 09:05:33 +08:00
|
|
|
void Fopen(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Tmpfile(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Fclose(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Fread(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Fwrite(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Fseek(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Ftell(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Rewind(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Fgetpos(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Fsetpos(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Clearerr(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Feof(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Ferror(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
void Fileno(CheckerContext &C, const CallExpr *CE) const;
|
|
|
|
|
|
|
|
void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
|
2015-09-08 11:50:52 +08:00
|
|
|
|
|
|
|
ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state,
|
2011-02-24 09:05:33 +08:00
|
|
|
CheckerContext &C) const;
|
2015-09-08 11:50:52 +08:00
|
|
|
ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state,
|
2011-02-24 09:05:33 +08:00
|
|
|
CheckerContext &C) const;
|
2010-06-16 13:38:05 +08:00
|
|
|
};
|
|
|
|
|
2010-06-18 10:47:46 +08:00
|
|
|
} // end anonymous namespace
|
2010-06-16 13:38:05 +08:00
|
|
|
|
2012-11-02 09:54:42 +08:00
|
|
|
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
|
|
|
|
|
2010-07-19 09:52:29 +08:00
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
|
2011-12-01 13:57:37 +08:00
|
|
|
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
2012-07-11 07:13:01 +08:00
|
|
|
if (!FD || FD->getKind() != Decl::Function)
|
2010-06-16 13:38:05 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
ASTContext &Ctx = C.getASTContext();
|
|
|
|
if (!II_fopen)
|
|
|
|
II_fopen = &Ctx.Idents.get("fopen");
|
2010-07-22 22:01:01 +08:00
|
|
|
if (!II_tmpfile)
|
|
|
|
II_tmpfile = &Ctx.Idents.get("tmpfile");
|
2010-07-19 09:52:29 +08:00
|
|
|
if (!II_fclose)
|
|
|
|
II_fclose = &Ctx.Idents.get("fclose");
|
2010-06-16 13:38:05 +08:00
|
|
|
if (!II_fread)
|
|
|
|
II_fread = &Ctx.Idents.get("fread");
|
2010-06-22 15:50:21 +08:00
|
|
|
if (!II_fwrite)
|
|
|
|
II_fwrite = &Ctx.Idents.get("fwrite");
|
2010-06-18 10:47:46 +08:00
|
|
|
if (!II_fseek)
|
|
|
|
II_fseek = &Ctx.Idents.get("fseek");
|
|
|
|
if (!II_ftell)
|
|
|
|
II_ftell = &Ctx.Idents.get("ftell");
|
|
|
|
if (!II_rewind)
|
|
|
|
II_rewind = &Ctx.Idents.get("rewind");
|
2010-06-22 15:50:21 +08:00
|
|
|
if (!II_fgetpos)
|
|
|
|
II_fgetpos = &Ctx.Idents.get("fgetpos");
|
|
|
|
if (!II_fsetpos)
|
|
|
|
II_fsetpos = &Ctx.Idents.get("fsetpos");
|
|
|
|
if (!II_clearerr)
|
|
|
|
II_clearerr = &Ctx.Idents.get("clearerr");
|
|
|
|
if (!II_feof)
|
|
|
|
II_feof = &Ctx.Idents.get("feof");
|
|
|
|
if (!II_ferror)
|
|
|
|
II_ferror = &Ctx.Idents.get("ferror");
|
|
|
|
if (!II_fileno)
|
|
|
|
II_fileno = &Ctx.Idents.get("fileno");
|
2010-06-18 10:47:46 +08:00
|
|
|
|
2010-06-16 13:38:05 +08:00
|
|
|
if (FD->getIdentifier() == II_fopen) {
|
2010-06-22 15:50:21 +08:00
|
|
|
Fopen(C, CE);
|
2010-06-16 13:38:05 +08:00
|
|
|
return true;
|
|
|
|
}
|
2010-07-22 22:01:01 +08:00
|
|
|
if (FD->getIdentifier() == II_tmpfile) {
|
|
|
|
Tmpfile(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
2010-07-19 09:52:29 +08:00
|
|
|
if (FD->getIdentifier() == II_fclose) {
|
|
|
|
Fclose(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
2010-06-16 13:38:05 +08:00
|
|
|
if (FD->getIdentifier() == II_fread) {
|
2010-06-22 15:50:21 +08:00
|
|
|
Fread(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (FD->getIdentifier() == II_fwrite) {
|
|
|
|
Fwrite(C, CE);
|
2010-06-16 13:38:05 +08:00
|
|
|
return true;
|
|
|
|
}
|
2010-06-18 10:47:46 +08:00
|
|
|
if (FD->getIdentifier() == II_fseek) {
|
2010-06-22 15:50:21 +08:00
|
|
|
Fseek(C, CE);
|
2010-06-18 10:47:46 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (FD->getIdentifier() == II_ftell) {
|
2010-06-22 15:50:21 +08:00
|
|
|
Ftell(C, CE);
|
2010-06-18 10:47:46 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (FD->getIdentifier() == II_rewind) {
|
|
|
|
Rewind(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
2010-06-22 15:50:21 +08:00
|
|
|
if (FD->getIdentifier() == II_fgetpos) {
|
|
|
|
Fgetpos(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (FD->getIdentifier() == II_fsetpos) {
|
|
|
|
Fsetpos(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (FD->getIdentifier() == II_clearerr) {
|
|
|
|
Clearerr(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (FD->getIdentifier() == II_feof) {
|
|
|
|
Feof(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (FD->getIdentifier() == II_ferror) {
|
|
|
|
Ferror(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (FD->getIdentifier() == II_fileno) {
|
|
|
|
Fileno(C, CE);
|
|
|
|
return true;
|
|
|
|
}
|
2010-06-18 10:47:46 +08:00
|
|
|
|
2010-06-16 13:38:05 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const {
|
2010-07-22 22:01:01 +08:00
|
|
|
OpenFileAux(C, CE);
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const {
|
2010-07-22 22:01:01 +08:00
|
|
|
OpenFileAux(C, CE);
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2010-12-02 15:49:45 +08:00
|
|
|
SValBuilder &svalBuilder = C.getSValBuilder();
|
2012-02-18 07:13:45 +08:00
|
|
|
const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
|
2014-05-27 10:45:47 +08:00
|
|
|
DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
|
|
|
|
C.blockCount())
|
2013-02-20 13:52:05 +08:00
|
|
|
.castAs<DefinedSVal>();
|
2012-01-07 06:09:28 +08:00
|
|
|
state = state->BindExpr(CE, C.getLocationContext(), RetVal);
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2010-06-16 13:38:05 +08:00
|
|
|
ConstraintManager &CM = C.getConstraintManager();
|
|
|
|
// Bifurcate the state into two: one with a valid FILE* pointer, the other
|
|
|
|
// with a NULL.
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef stateNotNull, stateNull;
|
2014-03-02 21:01:17 +08:00
|
|
|
std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2010-09-03 09:07:04 +08:00
|
|
|
if (SymbolRef Sym = RetVal.getAsSymbol()) {
|
|
|
|
// if RetVal is not NULL, set the symbol's state to Opened.
|
|
|
|
stateNotNull =
|
2012-11-02 09:54:42 +08:00
|
|
|
stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE));
|
2010-09-03 09:07:04 +08:00
|
|
|
stateNull =
|
2012-11-02 09:54:42 +08:00
|
|
|
stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE));
|
2010-09-03 09:07:04 +08:00
|
|
|
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(stateNotNull);
|
|
|
|
C.addTransition(stateNull);
|
2010-09-03 09:07:04 +08:00
|
|
|
}
|
2010-06-16 13:38:05 +08:00
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C);
|
2010-07-19 09:52:29 +08:00
|
|
|
if (state)
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(state);
|
2010-07-19 09:52:29 +08:00
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
|
2010-06-18 10:47:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
|
2010-06-22 15:50:21 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!(state = CheckNullStream(C.getSVal(CE->getArg(0)), state, C)))
|
2010-06-24 21:36:41 +08:00
|
|
|
return;
|
|
|
|
// Check the legality of the 'whence' argument of 'fseek'.
|
2012-01-07 06:09:28 +08:00
|
|
|
SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
|
2013-02-21 06:23:23 +08:00
|
|
|
Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>();
|
2010-09-08 04:45:26 +08:00
|
|
|
|
2010-06-24 21:36:41 +08:00
|
|
|
if (!CI)
|
2010-09-08 04:45:26 +08:00
|
|
|
return;
|
2010-06-24 21:36:41 +08:00
|
|
|
|
|
|
|
int64_t x = CI->getValue().getSExtValue();
|
2010-09-08 04:45:26 +08:00
|
|
|
if (x >= 0 && x <= 2)
|
2010-06-18 10:47:46 +08:00
|
|
|
return;
|
2010-06-24 21:36:41 +08:00
|
|
|
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
|
2010-09-08 04:45:26 +08:00
|
|
|
if (!BT_illegalwhence)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_illegalwhence.reset(
|
|
|
|
new BuiltinBug(this, "Illegal whence argument",
|
|
|
|
"The whence argument to fseek() should be "
|
|
|
|
"SEEK_SET, SEEK_END, or SEEK_CUR."));
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(llvm::make_unique<BugReport>(
|
|
|
|
*BT_illegalwhence, BT_illegalwhence->getDescription(), N));
|
2010-09-08 04:45:26 +08:00
|
|
|
}
|
2010-06-18 10:47:46 +08:00
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
|
2010-06-18 10:47:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
|
2010-06-18 10:47:46 +08:00
|
|
|
return;
|
|
|
|
}
|
2010-06-16 13:38:05 +08:00
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
|
2010-06-22 15:50:21 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
|
2010-06-22 15:50:21 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
|
2010-06-22 15:50:21 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
|
2010-06-22 15:50:21 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
|
2010-06-22 15:50:21 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2018-01-18 04:27:29 +08:00
|
|
|
if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
|
2010-06-22 15:50:21 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
|
2011-02-24 09:05:33 +08:00
|
|
|
CheckerContext &C) const {
|
2013-02-21 06:23:23 +08:00
|
|
|
Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>();
|
2010-06-18 10:47:46 +08:00
|
|
|
if (!DV)
|
2014-05-27 10:45:47 +08:00
|
|
|
return nullptr;
|
2010-06-16 13:56:39 +08:00
|
|
|
|
2010-06-18 10:47:46 +08:00
|
|
|
ConstraintManager &CM = C.getConstraintManager();
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef stateNotNull, stateNull;
|
2014-03-02 21:01:17 +08:00
|
|
|
std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
|
2010-06-16 13:56:39 +08:00
|
|
|
|
2010-06-18 10:47:46 +08:00
|
|
|
if (!stateNotNull && stateNull) {
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateErrorNode(stateNull)) {
|
2010-06-18 10:47:46 +08:00
|
|
|
if (!BT_nullfp)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
|
|
|
|
"Stream pointer might be NULL."));
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(llvm::make_unique<BugReport>(
|
|
|
|
*BT_nullfp, BT_nullfp->getDescription(), N));
|
2010-06-16 13:38:05 +08:00
|
|
|
}
|
2014-05-27 10:45:47 +08:00
|
|
|
return nullptr;
|
2010-06-16 13:38:05 +08:00
|
|
|
}
|
2010-06-24 21:09:02 +08:00
|
|
|
return stateNotNull;
|
2010-06-16 13:38:05 +08:00
|
|
|
}
|
2010-07-19 09:52:29 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
|
|
|
|
ProgramStateRef state,
|
2011-02-24 09:05:33 +08:00
|
|
|
CheckerContext &C) const {
|
2018-01-18 04:27:29 +08:00
|
|
|
SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
|
2010-09-03 09:07:04 +08:00
|
|
|
if (!Sym)
|
|
|
|
return state;
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2012-11-02 09:54:42 +08:00
|
|
|
const StreamState *SS = state->get<StreamMap>(Sym);
|
2010-08-06 07:24:13 +08:00
|
|
|
|
|
|
|
// If the file stream is not tracked, return.
|
|
|
|
if (!SS)
|
|
|
|
return state;
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2010-07-19 09:52:29 +08:00
|
|
|
// Check: Double close a File Descriptor could cause undefined behaviour.
|
|
|
|
// Conforming to man-pages
|
|
|
|
if (SS->isClosed()) {
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2010-07-19 09:52:29 +08:00
|
|
|
if (N) {
|
|
|
|
if (!BT_doubleclose)
|
2014-02-12 05:49:21 +08:00
|
|
|
BT_doubleclose.reset(new BuiltinBug(
|
|
|
|
this, "Double fclose", "Try to close a file Descriptor already"
|
|
|
|
" closed. Cause undefined behaviour."));
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(llvm::make_unique<BugReport>(
|
|
|
|
*BT_doubleclose, BT_doubleclose->getDescription(), N));
|
2010-07-19 09:52:29 +08:00
|
|
|
}
|
2014-05-27 10:45:47 +08:00
|
|
|
return nullptr;
|
2010-07-19 09:52:29 +08:00
|
|
|
}
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2010-07-19 09:52:29 +08:00
|
|
|
// Close the File Descriptor.
|
2012-11-02 09:54:42 +08:00
|
|
|
return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
|
2010-07-19 09:52:29 +08:00
|
|
|
}
|
2010-07-23 22:14:59 +08:00
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
|
|
|
CheckerContext &C) const {
|
2018-11-30 11:27:50 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
|
|
|
|
2012-10-30 06:51:44 +08:00
|
|
|
// TODO: Clean up the state.
|
2018-11-30 11:27:50 +08:00
|
|
|
const StreamMapTy &Map = state->get<StreamMap>();
|
|
|
|
for (const auto &I: Map) {
|
|
|
|
SymbolRef Sym = I.first;
|
|
|
|
const StreamState &SS = I.second;
|
|
|
|
if (!SymReaper.isDead(Sym) || !SS.isOpened())
|
2013-03-16 07:34:31 +08:00
|
|
|
continue;
|
2010-07-23 22:14:59 +08:00
|
|
|
|
2018-11-30 11:27:50 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!BT_ResourceLeak)
|
|
|
|
BT_ResourceLeak.reset(
|
|
|
|
new BuiltinBug(this, "Resource Leak",
|
|
|
|
"Opened File never closed. Potential Resource leak."));
|
|
|
|
C.emitReport(llvm::make_unique<BugReport>(
|
|
|
|
*BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
|
2010-07-23 22:14:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:05:33 +08:00
|
|
|
void ento::registerStreamChecker(CheckerManager &mgr) {
|
|
|
|
mgr.registerChecker<StreamChecker>();
|
|
|
|
}
|
2019-01-26 22:23:08 +08:00
|
|
|
|
|
|
|
bool ento::shouldRegisterStreamChecker(const LangOptions &LO) {
|
|
|
|
return true;
|
|
|
|
}
|