forked from OSchip/llvm-project
[analyzer] Apply whitespace cleanups by Honggyu Kim.
llvm-svn: 246978
This commit is contained in:
parent
21dfbfb426
commit
3a0678e33c
|
@ -101,7 +101,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
|
||||||
else if (isa<BlockDecl>(D)) {
|
else if (isa<BlockDecl>(D)) {
|
||||||
output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
|
output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
NumBlocksUnreachable += unreachable;
|
NumBlocksUnreachable += unreachable;
|
||||||
NumBlocks += total;
|
NumBlocks += total;
|
||||||
std::string NameOfRootFunction = output.str();
|
std::string NameOfRootFunction = output.str();
|
||||||
|
|
|
@ -23,7 +23,7 @@ using namespace clang;
|
||||||
using namespace ento;
|
using namespace ento;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class ArrayBoundChecker :
|
class ArrayBoundChecker :
|
||||||
public Checker<check::Location> {
|
public Checker<check::Location> {
|
||||||
mutable std::unique_ptr<BuiltinBug> BT;
|
mutable std::unique_ptr<BuiltinBug> BT;
|
||||||
|
|
||||||
|
@ -55,8 +55,8 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
|
|
||||||
// Get the size of the array.
|
// Get the size of the array.
|
||||||
DefinedOrUnknownSVal NumElements
|
DefinedOrUnknownSVal NumElements
|
||||||
= C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
|
= C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
|
||||||
ER->getValueType());
|
ER->getValueType());
|
||||||
|
|
||||||
ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
|
ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
|
||||||
|
@ -65,7 +65,7 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
|
||||||
ExplodedNode *N = C.generateSink(StOutBound);
|
ExplodedNode *N = C.generateSink(StOutBound);
|
||||||
if (!N)
|
if (!N)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!BT)
|
if (!BT)
|
||||||
BT.reset(new BuiltinBug(
|
BT.reset(new BuiltinBug(
|
||||||
this, "Out-of-bound array access",
|
this, "Out-of-bound array access",
|
||||||
|
@ -82,7 +82,7 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
|
||||||
C.emitReport(std::move(report));
|
C.emitReport(std::move(report));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array bound check succeeded. From this point forward the array bound
|
// Array bound check succeeded. From this point forward the array bound
|
||||||
// should always succeed.
|
// should always succeed.
|
||||||
C.addTransition(StInBound);
|
C.addTransition(StInBound);
|
||||||
|
|
|
@ -26,15 +26,15 @@ using namespace clang;
|
||||||
using namespace ento;
|
using namespace ento;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class ArrayBoundCheckerV2 :
|
class ArrayBoundCheckerV2 :
|
||||||
public Checker<check::Location> {
|
public Checker<check::Location> {
|
||||||
mutable std::unique_ptr<BuiltinBug> BT;
|
mutable std::unique_ptr<BuiltinBug> BT;
|
||||||
|
|
||||||
enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
|
enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
|
||||||
|
|
||||||
void reportOOB(CheckerContext &C, ProgramStateRef errorState,
|
void reportOOB(CheckerContext &C, ProgramStateRef errorState,
|
||||||
OOB_Kind kind) const;
|
OOB_Kind kind) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void checkLocation(SVal l, bool isLoad, const Stmt*S,
|
void checkLocation(SVal l, bool isLoad, const Stmt*S,
|
||||||
CheckerContext &C) const;
|
CheckerContext &C) const;
|
||||||
|
@ -55,7 +55,7 @@ public:
|
||||||
|
|
||||||
NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); }
|
NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); }
|
||||||
const SubRegion *getRegion() const { return baseRegion; }
|
const SubRegion *getRegion() const { return baseRegion; }
|
||||||
|
|
||||||
static RegionRawOffsetV2 computeOffset(ProgramStateRef state,
|
static RegionRawOffsetV2 computeOffset(ProgramStateRef state,
|
||||||
SValBuilder &svalBuilder,
|
SValBuilder &svalBuilder,
|
||||||
SVal location);
|
SVal location);
|
||||||
|
@ -65,12 +65,12 @@ public:
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static SVal computeExtentBegin(SValBuilder &svalBuilder,
|
static SVal computeExtentBegin(SValBuilder &svalBuilder,
|
||||||
const MemRegion *region) {
|
const MemRegion *region) {
|
||||||
while (true)
|
while (true)
|
||||||
switch (region->getKind()) {
|
switch (region->getKind()) {
|
||||||
default:
|
default:
|
||||||
return svalBuilder.makeZeroArrayIndex();
|
return svalBuilder.makeZeroArrayIndex();
|
||||||
case MemRegion::SymbolicRegionKind:
|
case MemRegion::SymbolicRegionKind:
|
||||||
// FIXME: improve this later by tracking symbolic lower bounds
|
// FIXME: improve this later by tracking symbolic lower bounds
|
||||||
// for symbolic regions.
|
// for symbolic regions.
|
||||||
|
@ -94,22 +94,22 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
|
||||||
// memory access is within the extent of the base region. Since we
|
// memory access is within the extent of the base region. Since we
|
||||||
// have some flexibility in defining the base region, we can achieve
|
// have some flexibility in defining the base region, we can achieve
|
||||||
// various levels of conservatism in our buffer overflow checking.
|
// various levels of conservatism in our buffer overflow checking.
|
||||||
ProgramStateRef state = checkerContext.getState();
|
ProgramStateRef state = checkerContext.getState();
|
||||||
ProgramStateRef originalState = state;
|
ProgramStateRef originalState = state;
|
||||||
|
|
||||||
SValBuilder &svalBuilder = checkerContext.getSValBuilder();
|
SValBuilder &svalBuilder = checkerContext.getSValBuilder();
|
||||||
const RegionRawOffsetV2 &rawOffset =
|
const RegionRawOffsetV2 &rawOffset =
|
||||||
RegionRawOffsetV2::computeOffset(state, svalBuilder, location);
|
RegionRawOffsetV2::computeOffset(state, svalBuilder, location);
|
||||||
|
|
||||||
if (!rawOffset.getRegion())
|
if (!rawOffset.getRegion())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// CHECK LOWER BOUND: Is byteOffset < extent begin?
|
// CHECK LOWER BOUND: Is byteOffset < extent begin?
|
||||||
// If so, we are doing a load/store
|
// If so, we are doing a load/store
|
||||||
// before the first valid offset in the memory region.
|
// before the first valid offset in the memory region.
|
||||||
|
|
||||||
SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
|
SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
|
||||||
|
|
||||||
if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
|
if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
|
||||||
SVal lowerBound =
|
SVal lowerBound =
|
||||||
svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), *NV,
|
svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), *NV,
|
||||||
|
@ -118,7 +118,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
|
||||||
Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
|
Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
|
||||||
if (!lowerBoundToCheck)
|
if (!lowerBoundToCheck)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
|
ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
|
||||||
std::tie(state_precedesLowerBound, state_withinLowerBound) =
|
std::tie(state_precedesLowerBound, state_withinLowerBound) =
|
||||||
state->assume(*lowerBoundToCheck);
|
state->assume(*lowerBoundToCheck);
|
||||||
|
@ -128,12 +128,12 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
|
||||||
reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
|
reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, assume the constraint of the lower bound.
|
// Otherwise, assume the constraint of the lower bound.
|
||||||
assert(state_withinLowerBound);
|
assert(state_withinLowerBound);
|
||||||
state = state_withinLowerBound;
|
state = state_withinLowerBound;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so,
|
// CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so,
|
||||||
// we are doing a load/store after the last valid offset.
|
// we are doing a load/store after the last valid offset.
|
||||||
|
@ -146,11 +146,11 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
|
||||||
= svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(),
|
= svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(),
|
||||||
extentVal.castAs<NonLoc>(),
|
extentVal.castAs<NonLoc>(),
|
||||||
svalBuilder.getConditionType());
|
svalBuilder.getConditionType());
|
||||||
|
|
||||||
Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
|
Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
|
||||||
if (!upperboundToCheck)
|
if (!upperboundToCheck)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
|
ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
|
||||||
std::tie(state_exceedsUpperBound, state_withinUpperBound) =
|
std::tie(state_exceedsUpperBound, state_withinUpperBound) =
|
||||||
state->assume(*upperboundToCheck);
|
state->assume(*upperboundToCheck);
|
||||||
|
@ -161,19 +161,19 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
|
||||||
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted);
|
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are constrained enough to definitely exceed the upper bound, report.
|
// If we are constrained enough to definitely exceed the upper bound, report.
|
||||||
if (state_exceedsUpperBound) {
|
if (state_exceedsUpperBound) {
|
||||||
assert(!state_withinUpperBound);
|
assert(!state_withinUpperBound);
|
||||||
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
|
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(state_withinUpperBound);
|
assert(state_withinUpperBound);
|
||||||
state = state_withinUpperBound;
|
state = state_withinUpperBound;
|
||||||
}
|
}
|
||||||
while (false);
|
while (false);
|
||||||
|
|
||||||
if (state != originalState)
|
if (state != originalState)
|
||||||
checkerContext.addTransition(state);
|
checkerContext.addTransition(state);
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
|
||||||
void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
|
void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
|
||||||
ProgramStateRef errorState,
|
ProgramStateRef errorState,
|
||||||
OOB_Kind kind) const {
|
OOB_Kind kind) const {
|
||||||
|
|
||||||
ExplodedNode *errorNode = checkerContext.generateSink(errorState);
|
ExplodedNode *errorNode = checkerContext.generateSink(errorState);
|
||||||
if (!errorNode)
|
if (!errorNode)
|
||||||
return;
|
return;
|
||||||
|
@ -259,7 +259,7 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
|
||||||
{
|
{
|
||||||
const MemRegion *region = location.getAsRegion();
|
const MemRegion *region = location.getAsRegion();
|
||||||
SVal offset = UndefinedVal();
|
SVal offset = UndefinedVal();
|
||||||
|
|
||||||
while (region) {
|
while (region) {
|
||||||
switch (region->getKind()) {
|
switch (region->getKind()) {
|
||||||
default: {
|
default: {
|
||||||
|
@ -280,7 +280,7 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
|
||||||
ASTContext &astContext = svalBuilder.getContext();
|
ASTContext &astContext = svalBuilder.getContext();
|
||||||
if (elemType->isIncompleteType())
|
if (elemType->isIncompleteType())
|
||||||
return RegionRawOffsetV2();
|
return RegionRawOffsetV2();
|
||||||
|
|
||||||
// Update the offset.
|
// Update the offset.
|
||||||
offset = addValue(state,
|
offset = addValue(state,
|
||||||
getValue(offset, svalBuilder),
|
getValue(offset, svalBuilder),
|
||||||
|
|
|
@ -143,7 +143,7 @@ void NilArgChecker::warnIfNilExpr(const Expr *E,
|
||||||
if (ExplodedNode *N = C.generateSink()) {
|
if (ExplodedNode *N = C.generateSink()) {
|
||||||
generateBugReport(N, Msg, E->getSourceRange(), E, C);
|
generateBugReport(N, Msg, E->getSourceRange(), E, C);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ void NilArgChecker::warnIfNilArg(CheckerContext &C,
|
||||||
ProgramStateRef State = C.getState();
|
ProgramStateRef State = C.getState();
|
||||||
if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
|
if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ExplodedNode *N = C.generateSink()) {
|
if (ExplodedNode *N = C.generateSink()) {
|
||||||
SmallString<128> sbuf;
|
SmallString<128> sbuf;
|
||||||
llvm::raw_svector_ostream os(sbuf);
|
llvm::raw_svector_ostream os(sbuf);
|
||||||
|
@ -193,7 +193,7 @@ void NilArgChecker::warnIfNilArg(CheckerContext &C,
|
||||||
os << "' cannot be nil";
|
os << "' cannot be nil";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
|
generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
|
||||||
msg.getArgExpr(Arg), C);
|
msg.getArgExpr(Arg), C);
|
||||||
}
|
}
|
||||||
|
@ -224,7 +224,7 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
static const unsigned InvalidArgIndex = UINT_MAX;
|
static const unsigned InvalidArgIndex = UINT_MAX;
|
||||||
unsigned Arg = InvalidArgIndex;
|
unsigned Arg = InvalidArgIndex;
|
||||||
bool CanBeSubscript = false;
|
bool CanBeSubscript = false;
|
||||||
|
|
||||||
if (Class == FC_NSString) {
|
if (Class == FC_NSString) {
|
||||||
Selector S = msg.getSelector();
|
Selector S = msg.getSelector();
|
||||||
|
|
||||||
|
@ -433,7 +433,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
|
||||||
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
||||||
if (!FD)
|
if (!FD)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ASTContext &Ctx = C.getASTContext();
|
ASTContext &Ctx = C.getASTContext();
|
||||||
if (!II)
|
if (!II)
|
||||||
II = &Ctx.Idents.get("CFNumberCreate");
|
II = &Ctx.Idents.get("CFNumberCreate");
|
||||||
|
@ -495,17 +495,17 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
|
||||||
// FIXME: We can actually create an abstract "CFNumber" object that has
|
// FIXME: We can actually create an abstract "CFNumber" object that has
|
||||||
// the bits initialized to the provided values.
|
// the bits initialized to the provided values.
|
||||||
//
|
//
|
||||||
if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
|
if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
|
||||||
: C.addTransition()) {
|
: C.addTransition()) {
|
||||||
SmallString<128> sbuf;
|
SmallString<128> sbuf;
|
||||||
llvm::raw_svector_ostream os(sbuf);
|
llvm::raw_svector_ostream os(sbuf);
|
||||||
|
|
||||||
os << (SourceSize == 8 ? "An " : "A ")
|
os << (SourceSize == 8 ? "An " : "A ")
|
||||||
<< SourceSize << " bit integer is used to initialize a CFNumber "
|
<< SourceSize << " bit integer is used to initialize a CFNumber "
|
||||||
"object that represents "
|
"object that represents "
|
||||||
<< (TargetSize == 8 ? "an " : "a ")
|
<< (TargetSize == 8 ? "an " : "a ")
|
||||||
<< TargetSize << " bit integer. ";
|
<< TargetSize << " bit integer. ";
|
||||||
|
|
||||||
if (SourceSize < TargetSize)
|
if (SourceSize < TargetSize)
|
||||||
os << (TargetSize - SourceSize)
|
os << (TargetSize - SourceSize)
|
||||||
<< " bits of the CFNumber value will be garbage." ;
|
<< " bits of the CFNumber value will be garbage." ;
|
||||||
|
@ -549,7 +549,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
|
||||||
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
||||||
if (!FD)
|
if (!FD)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!BT) {
|
if (!BT) {
|
||||||
ASTContext &Ctx = C.getASTContext();
|
ASTContext &Ctx = C.getASTContext();
|
||||||
Retain = &Ctx.Idents.get("CFRetain");
|
Retain = &Ctx.Idents.get("CFRetain");
|
||||||
|
@ -635,7 +635,7 @@ public:
|
||||||
|
|
||||||
void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
|
|
||||||
if (!BT) {
|
if (!BT) {
|
||||||
BT.reset(new APIMisuse(
|
BT.reset(new APIMisuse(
|
||||||
this, "message incorrectly sent to class instead of class instance"));
|
this, "message incorrectly sent to class instead of class instance"));
|
||||||
|
@ -646,7 +646,7 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
autoreleaseS = GetNullarySelector("autorelease", Ctx);
|
autoreleaseS = GetNullarySelector("autorelease", Ctx);
|
||||||
drainS = GetNullarySelector("drain", Ctx);
|
drainS = GetNullarySelector("drain", Ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.isInstanceMessage())
|
if (msg.isInstanceMessage())
|
||||||
return;
|
return;
|
||||||
const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
|
const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
|
||||||
|
@ -655,7 +655,7 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
Selector S = msg.getSelector();
|
Selector S = msg.getSelector();
|
||||||
if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
|
if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ExplodedNode *N = C.addTransition()) {
|
if (ExplodedNode *N = C.addTransition()) {
|
||||||
SmallString<200> buf;
|
SmallString<200> buf;
|
||||||
llvm::raw_svector_ostream os(buf);
|
llvm::raw_svector_ostream os(buf);
|
||||||
|
@ -665,7 +665,7 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
os << "' message should be sent to instances "
|
os << "' message should be sent to instances "
|
||||||
"of class '" << Class->getName()
|
"of class '" << Class->getName()
|
||||||
<< "' and not the class directly";
|
<< "' and not the class directly";
|
||||||
|
|
||||||
auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
|
auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
|
||||||
report->addRange(msg.getSourceRange());
|
report->addRange(msg.getSourceRange());
|
||||||
C.emitReport(std::move(report));
|
C.emitReport(std::move(report));
|
||||||
|
@ -699,12 +699,12 @@ public:
|
||||||
bool
|
bool
|
||||||
VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
|
VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
|
||||||
const ObjCMethodDecl *MD = msg.getDecl();
|
const ObjCMethodDecl *MD = msg.getDecl();
|
||||||
|
|
||||||
if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
|
if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Selector S = msg.getSelector();
|
Selector S = msg.getSelector();
|
||||||
|
|
||||||
if (msg.isInstanceMessage()) {
|
if (msg.isInstanceMessage()) {
|
||||||
// FIXME: Ideally we'd look at the receiver interface here, but that's not
|
// FIXME: Ideally we'd look at the receiver interface here, but that's not
|
||||||
// useful for init, because alloc returns 'id'. In theory, this could lead
|
// useful for init, because alloc returns 'id'. In theory, this could lead
|
||||||
|
@ -751,7 +751,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
|
|
||||||
ASTContext &Ctx = C.getASTContext();
|
ASTContext &Ctx = C.getASTContext();
|
||||||
arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
|
arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
|
||||||
dictionaryWithObjectsAndKeysS =
|
dictionaryWithObjectsAndKeysS =
|
||||||
GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
|
GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
|
||||||
setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
|
setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
|
||||||
orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
|
orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
|
||||||
|
@ -789,11 +789,11 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
// Ignore pointer constants.
|
// Ignore pointer constants.
|
||||||
if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
|
if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Ignore pointer types annotated with 'NSObject' attribute.
|
// Ignore pointer types annotated with 'NSObject' attribute.
|
||||||
if (C.getASTContext().isObjCNSObjectType(ArgTy))
|
if (C.getASTContext().isObjCNSObjectType(ArgTy))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Ignore CF references, which can be toll-free bridged.
|
// Ignore CF references, which can be toll-free bridged.
|
||||||
if (coreFoundation::isCFObjectRef(ArgTy))
|
if (coreFoundation::isCFObjectRef(ArgTy))
|
||||||
continue;
|
continue;
|
||||||
|
@ -861,7 +861,7 @@ static bool isKnownNonNilCollectionType(QualType T) {
|
||||||
const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
|
const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
|
||||||
if (!PT)
|
if (!PT)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
|
const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
|
||||||
if (!ID)
|
if (!ID)
|
||||||
return false;
|
return false;
|
||||||
|
@ -1023,7 +1023,7 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
|
||||||
State = checkElementNonNil(C, State, FCS);
|
State = checkElementNonNil(C, State, FCS);
|
||||||
State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
|
State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!State)
|
if (!State)
|
||||||
C.generateSink();
|
C.generateSink();
|
||||||
else if (State != C.getState())
|
else if (State != C.getState())
|
||||||
|
@ -1041,7 +1041,7 @@ bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M,
|
||||||
if (S.isUnarySelector() &&
|
if (S.isUnarySelector() &&
|
||||||
(S.getIdentifierInfoForSlot(0) == CountSelectorII))
|
(S.getIdentifierInfoForSlot(0) == CountSelectorII))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1069,7 +1069,7 @@ void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
|
||||||
// a call to "count" and add it to the map.
|
// a call to "count" and add it to the map.
|
||||||
if (!isCollectionCountMethod(M, C))
|
if (!isCollectionCountMethod(M, C))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const Expr *MsgExpr = M.getOriginExpr();
|
const Expr *MsgExpr = M.getOriginExpr();
|
||||||
SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
|
SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
|
||||||
if (CountS) {
|
if (CountS) {
|
||||||
|
|
|
@ -42,45 +42,45 @@ void BoolAssignmentChecker::emitReport(ProgramStateRef state,
|
||||||
static bool isBooleanType(QualType Ty) {
|
static bool isBooleanType(QualType Ty) {
|
||||||
if (Ty->isBooleanType()) // C++ or C99
|
if (Ty->isBooleanType()) // C++ or C99
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (const TypedefType *TT = Ty->getAs<TypedefType>())
|
if (const TypedefType *TT = Ty->getAs<TypedefType>())
|
||||||
return TT->getDecl()->getName() == "BOOL" || // Objective-C
|
return TT->getDecl()->getName() == "BOOL" || // Objective-C
|
||||||
TT->getDecl()->getName() == "_Bool" || // stdbool.h < C99
|
TT->getDecl()->getName() == "_Bool" || // stdbool.h < C99
|
||||||
TT->getDecl()->getName() == "Boolean"; // MacTypes.h
|
TT->getDecl()->getName() == "Boolean"; // MacTypes.h
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
|
void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
|
|
||||||
// We are only interested in stores into Booleans.
|
// We are only interested in stores into Booleans.
|
||||||
const TypedValueRegion *TR =
|
const TypedValueRegion *TR =
|
||||||
dyn_cast_or_null<TypedValueRegion>(loc.getAsRegion());
|
dyn_cast_or_null<TypedValueRegion>(loc.getAsRegion());
|
||||||
|
|
||||||
if (!TR)
|
if (!TR)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QualType valTy = TR->getValueType();
|
QualType valTy = TR->getValueType();
|
||||||
|
|
||||||
if (!isBooleanType(valTy))
|
if (!isBooleanType(valTy))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get the value of the right-hand side. We only care about values
|
// Get the value of the right-hand side. We only care about values
|
||||||
// that are defined (UnknownVals and UndefinedVals are handled by other
|
// that are defined (UnknownVals and UndefinedVals are handled by other
|
||||||
// checkers).
|
// checkers).
|
||||||
Optional<DefinedSVal> DV = val.getAs<DefinedSVal>();
|
Optional<DefinedSVal> DV = val.getAs<DefinedSVal>();
|
||||||
if (!DV)
|
if (!DV)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Check if the assigned value meets our criteria for correctness. It must
|
// Check if the assigned value meets our criteria for correctness. It must
|
||||||
// be a value that is either 0 or 1. One way to check this is to see if
|
// be a value that is either 0 or 1. One way to check this is to see if
|
||||||
// the value is possibly < 0 (for a negative value) or greater than 1.
|
// the value is possibly < 0 (for a negative value) or greater than 1.
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
SValBuilder &svalBuilder = C.getSValBuilder();
|
SValBuilder &svalBuilder = C.getSValBuilder();
|
||||||
ConstraintManager &CM = C.getConstraintManager();
|
ConstraintManager &CM = C.getConstraintManager();
|
||||||
|
|
||||||
// First, ensure that the value is >= 0.
|
// First, ensure that the value is >= 0.
|
||||||
DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy);
|
DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy);
|
||||||
SVal greaterThanOrEqualToZeroVal =
|
SVal greaterThanOrEqualToZeroVal =
|
||||||
svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal,
|
svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal,
|
||||||
|
@ -91,13 +91,13 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
|
||||||
|
|
||||||
if (!greaterThanEqualToZero) {
|
if (!greaterThanEqualToZero) {
|
||||||
// The SValBuilder cannot construct a valid SVal for this condition.
|
// The SValBuilder cannot construct a valid SVal for this condition.
|
||||||
// This means we cannot properly reason about it.
|
// This means we cannot properly reason about it.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef stateLT, stateGE;
|
ProgramStateRef stateLT, stateGE;
|
||||||
std::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero);
|
std::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero);
|
||||||
|
|
||||||
// Is it possible for the value to be less than zero?
|
// Is it possible for the value to be less than zero?
|
||||||
if (stateLT) {
|
if (stateLT) {
|
||||||
// It is possible for the value to be less than zero. We only
|
// It is possible for the value to be less than zero. We only
|
||||||
|
@ -106,15 +106,15 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
|
||||||
// value is underconstrained and there is nothing left to be done.
|
// value is underconstrained and there is nothing left to be done.
|
||||||
if (!stateGE)
|
if (!stateGE)
|
||||||
emitReport(stateLT, C);
|
emitReport(stateLT, C);
|
||||||
|
|
||||||
// In either case, we are done.
|
// In either case, we are done.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we reach here, it must be the case that the value is constrained
|
// If we reach here, it must be the case that the value is constrained
|
||||||
// to only be >= 0.
|
// to only be >= 0.
|
||||||
assert(stateGE == state);
|
assert(stateGE == state);
|
||||||
|
|
||||||
// At this point we know that the value is >= 0.
|
// At this point we know that the value is >= 0.
|
||||||
// Now check to ensure that the value is <= 1.
|
// Now check to ensure that the value is <= 1.
|
||||||
DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy);
|
DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy);
|
||||||
|
@ -127,13 +127,13 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
|
||||||
|
|
||||||
if (!lessThanEqToOne) {
|
if (!lessThanEqToOne) {
|
||||||
// The SValBuilder cannot construct a valid SVal for this condition.
|
// The SValBuilder cannot construct a valid SVal for this condition.
|
||||||
// This means we cannot properly reason about it.
|
// This means we cannot properly reason about it.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef stateGT, stateLE;
|
ProgramStateRef stateGT, stateLE;
|
||||||
std::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne);
|
std::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne);
|
||||||
|
|
||||||
// Is it possible for the value to be greater than one?
|
// Is it possible for the value to be greater than one?
|
||||||
if (stateGT) {
|
if (stateGT) {
|
||||||
// It is possible for the value to be greater than one. We only
|
// It is possible for the value to be greater than one. We only
|
||||||
|
@ -142,11 +142,11 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
|
||||||
// value is underconstrained and there is nothing left to be done.
|
// value is underconstrained and there is nothing left to be done.
|
||||||
if (!stateLE)
|
if (!stateLE)
|
||||||
emitReport(stateGT, C);
|
emitReport(stateGT, C);
|
||||||
|
|
||||||
// In either case, we are done.
|
// In either case, we are done.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we reach here, it must be the case that the value is constrained
|
// If we reach here, it must be the case that the value is constrained
|
||||||
// to only be <= 1.
|
// to only be <= 1.
|
||||||
assert(stateLE == state);
|
assert(stateLE == state);
|
||||||
|
|
|
@ -65,7 +65,7 @@ public:
|
||||||
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
|
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
|
||||||
bool wantsRegionChangeUpdate(ProgramStateRef state) const;
|
bool wantsRegionChangeUpdate(ProgramStateRef state) const;
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
checkRegionChanges(ProgramStateRef state,
|
checkRegionChanges(ProgramStateRef state,
|
||||||
const InvalidatedSymbols *,
|
const InvalidatedSymbols *,
|
||||||
ArrayRef<const MemRegion *> ExplicitRegions,
|
ArrayRef<const MemRegion *> ExplicitRegions,
|
||||||
|
@ -92,7 +92,7 @@ public:
|
||||||
void evalstrLength(CheckerContext &C, const CallExpr *CE) const;
|
void evalstrLength(CheckerContext &C, const CallExpr *CE) const;
|
||||||
void evalstrnLength(CheckerContext &C, const CallExpr *CE) const;
|
void evalstrnLength(CheckerContext &C, const CallExpr *CE) const;
|
||||||
void evalstrLengthCommon(CheckerContext &C,
|
void evalstrLengthCommon(CheckerContext &C,
|
||||||
const CallExpr *CE,
|
const CallExpr *CE,
|
||||||
bool IsStrnlen = false) const;
|
bool IsStrnlen = false) const;
|
||||||
|
|
||||||
void evalStrcpy(CheckerContext &C, const CallExpr *CE) const;
|
void evalStrcpy(CheckerContext &C, const CallExpr *CE) const;
|
||||||
|
@ -137,9 +137,9 @@ public:
|
||||||
SVal Buf,
|
SVal Buf,
|
||||||
bool hypothetical = false) const;
|
bool hypothetical = false) const;
|
||||||
|
|
||||||
const StringLiteral *getCStringLiteral(CheckerContext &C,
|
const StringLiteral *getCStringLiteral(CheckerContext &C,
|
||||||
ProgramStateRef &state,
|
ProgramStateRef &state,
|
||||||
const Expr *expr,
|
const Expr *expr,
|
||||||
SVal val) const;
|
SVal val) const;
|
||||||
|
|
||||||
static ProgramStateRef InvalidateBuffer(CheckerContext &C,
|
static ProgramStateRef InvalidateBuffer(CheckerContext &C,
|
||||||
|
@ -282,7 +282,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
|
||||||
// Get the size of the array.
|
// Get the size of the array.
|
||||||
const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
|
const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
|
||||||
SValBuilder &svalBuilder = C.getSValBuilder();
|
SValBuilder &svalBuilder = C.getSValBuilder();
|
||||||
SVal Extent =
|
SVal Extent =
|
||||||
svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder));
|
svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder));
|
||||||
DefinedOrUnknownSVal Size = Extent.castAs<DefinedOrUnknownSVal>();
|
DefinedOrUnknownSVal Size = Extent.castAs<DefinedOrUnknownSVal>();
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
|
||||||
C.emitReport(std::move(report));
|
C.emitReport(std::move(report));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array bound check succeeded. From this point forward the array bound
|
// Array bound check succeeded. From this point forward the array bound
|
||||||
// should always succeed.
|
// should always succeed.
|
||||||
return StInBound;
|
return StInBound;
|
||||||
|
@ -442,7 +442,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
// Are the two values the same?
|
// Are the two values the same?
|
||||||
SValBuilder &svalBuilder = C.getSValBuilder();
|
SValBuilder &svalBuilder = C.getSValBuilder();
|
||||||
std::tie(stateTrue, stateFalse) =
|
std::tie(stateTrue, stateFalse) =
|
||||||
state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc));
|
state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc));
|
||||||
|
|
||||||
|
@ -489,7 +489,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
|
||||||
// Bail out if the cast fails.
|
// Bail out if the cast fails.
|
||||||
ASTContext &Ctx = svalBuilder.getContext();
|
ASTContext &Ctx = svalBuilder.getContext();
|
||||||
QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
|
QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
|
||||||
SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy,
|
SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy,
|
||||||
First->getType());
|
First->getType());
|
||||||
Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
|
Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
|
||||||
if (!FirstStartLoc)
|
if (!FirstStartLoc)
|
||||||
|
@ -568,7 +568,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
|
||||||
} else {
|
} else {
|
||||||
// Try switching the operands. (The order of these two assignments is
|
// Try switching the operands. (The order of these two assignments is
|
||||||
// important!)
|
// important!)
|
||||||
maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left,
|
maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left,
|
||||||
sizeTy);
|
sizeTy);
|
||||||
left = right;
|
left = right;
|
||||||
}
|
}
|
||||||
|
@ -723,7 +723,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
|
||||||
auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N);
|
auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N);
|
||||||
|
|
||||||
report->addRange(Ex->getSourceRange());
|
report->addRange(Ex->getSourceRange());
|
||||||
C.emitReport(std::move(report));
|
C.emitReport(std::move(report));
|
||||||
}
|
}
|
||||||
return UndefinedVal();
|
return UndefinedVal();
|
||||||
|
|
||||||
|
@ -787,7 +787,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
|
||||||
auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N);
|
auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N);
|
||||||
|
|
||||||
report->addRange(Ex->getSourceRange());
|
report->addRange(Ex->getSourceRange());
|
||||||
C.emitReport(std::move(report));
|
C.emitReport(std::move(report));
|
||||||
}
|
}
|
||||||
|
|
||||||
return UndefinedVal();
|
return UndefinedVal();
|
||||||
|
@ -843,13 +843,13 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
|
||||||
// Invalidate and escape only indirect regions accessible through the source
|
// Invalidate and escape only indirect regions accessible through the source
|
||||||
// buffer.
|
// buffer.
|
||||||
if (IsSourceBuffer) {
|
if (IsSourceBuffer) {
|
||||||
ITraits.setTrait(R,
|
ITraits.setTrait(R,
|
||||||
RegionAndSymbolInvalidationTraits::TK_PreserveContents);
|
RegionAndSymbolInvalidationTraits::TK_PreserveContents);
|
||||||
ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
|
ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
|
||||||
CausesPointerEscape = true;
|
CausesPointerEscape = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return state->invalidateRegions(R, E, C.blockCount(), LCtx,
|
return state->invalidateRegions(R, E, C.blockCount(), LCtx,
|
||||||
CausesPointerEscape, nullptr, nullptr,
|
CausesPointerEscape, nullptr, nullptr,
|
||||||
&ITraits);
|
&ITraits);
|
||||||
}
|
}
|
||||||
|
@ -901,7 +901,7 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
|
||||||
// evaluation of individual function calls.
|
// evaluation of individual function calls.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
void CStringChecker::evalCopyCommon(CheckerContext &C,
|
void CStringChecker::evalCopyCommon(CheckerContext &C,
|
||||||
const CallExpr *CE,
|
const CallExpr *CE,
|
||||||
ProgramStateRef state,
|
ProgramStateRef state,
|
||||||
const Expr *Size, const Expr *Dest,
|
const Expr *Size, const Expr *Dest,
|
||||||
|
@ -941,7 +941,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
|
||||||
|
|
||||||
// Get the value of the Src.
|
// Get the value of the Src.
|
||||||
SVal srcVal = state->getSVal(Source, LCtx);
|
SVal srcVal = state->getSVal(Source, LCtx);
|
||||||
|
|
||||||
// Ensure the source is not null. If it is NULL there will be a
|
// Ensure the source is not null. If it is NULL there will be a
|
||||||
// NULL pointer dereference.
|
// NULL pointer dereference.
|
||||||
state = checkNonNull(C, state, Source, srcVal);
|
state = checkNonNull(C, state, Source, srcVal);
|
||||||
|
@ -959,11 +959,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
|
||||||
if (!state)
|
if (!state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If this is mempcpy, get the byte after the last byte copied and
|
// If this is mempcpy, get the byte after the last byte copied and
|
||||||
// bind the expr.
|
// bind the expr.
|
||||||
if (IsMempcpy) {
|
if (IsMempcpy) {
|
||||||
loc::MemRegionVal destRegVal = destVal.castAs<loc::MemRegionVal>();
|
loc::MemRegionVal destRegVal = destVal.castAs<loc::MemRegionVal>();
|
||||||
|
|
||||||
// Get the length to copy.
|
// Get the length to copy.
|
||||||
if (Optional<NonLoc> lenValNonLoc = sizeVal.getAs<NonLoc>()) {
|
if (Optional<NonLoc> lenValNonLoc = sizeVal.getAs<NonLoc>()) {
|
||||||
// Get the byte after the last byte copied.
|
// Get the byte after the last byte copied.
|
||||||
|
@ -972,11 +972,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
|
||||||
QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
|
QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
|
||||||
loc::MemRegionVal DestRegCharVal = SvalBuilder.evalCast(destRegVal,
|
loc::MemRegionVal DestRegCharVal = SvalBuilder.evalCast(destRegVal,
|
||||||
CharPtrTy, Dest->getType()).castAs<loc::MemRegionVal>();
|
CharPtrTy, Dest->getType()).castAs<loc::MemRegionVal>();
|
||||||
SVal lastElement = C.getSValBuilder().evalBinOpLN(state, BO_Add,
|
SVal lastElement = C.getSValBuilder().evalBinOpLN(state, BO_Add,
|
||||||
DestRegCharVal,
|
DestRegCharVal,
|
||||||
*lenValNonLoc,
|
*lenValNonLoc,
|
||||||
Dest->getType());
|
Dest->getType());
|
||||||
|
|
||||||
// The byte after the last byte copied is the return value.
|
// The byte after the last byte copied is the return value.
|
||||||
state = state->BindExpr(CE, LCtx, lastElement);
|
state = state->BindExpr(CE, LCtx, lastElement);
|
||||||
} else {
|
} else {
|
||||||
|
@ -999,12 +999,12 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
|
||||||
// can use LazyCompoundVals to copy the source values into the destination.
|
// can use LazyCompoundVals to copy the source values into the destination.
|
||||||
// This would probably remove any existing bindings past the end of the
|
// This would probably remove any existing bindings past the end of the
|
||||||
// copied region, but that's still an improvement over blank invalidation.
|
// copied region, but that's still an improvement over blank invalidation.
|
||||||
state = InvalidateBuffer(C, state, Dest, C.getSVal(Dest),
|
state = InvalidateBuffer(C, state, Dest, C.getSVal(Dest),
|
||||||
/*IsSourceBuffer*/false);
|
/*IsSourceBuffer*/false);
|
||||||
|
|
||||||
// Invalidate the source (const-invalidation without const-pointer-escaping
|
// Invalidate the source (const-invalidation without const-pointer-escaping
|
||||||
// the address of the top-level region).
|
// the address of the top-level region).
|
||||||
state = InvalidateBuffer(C, state, Source, C.getSVal(Source),
|
state = InvalidateBuffer(C, state, Source, C.getSVal(Source),
|
||||||
/*IsSourceBuffer*/true);
|
/*IsSourceBuffer*/true);
|
||||||
|
|
||||||
C.addTransition(state);
|
C.addTransition(state);
|
||||||
|
@ -1032,7 +1032,7 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
|
||||||
// The return value is a pointer to the byte following the last written byte.
|
// The return value is a pointer to the byte following the last written byte.
|
||||||
const Expr *Dest = CE->getArg(0);
|
const Expr *Dest = CE->getArg(0);
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
|
|
||||||
evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true);
|
evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1053,7 +1053,7 @@ void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// void bcopy(const void *src, void *dst, size_t n);
|
// void bcopy(const void *src, void *dst, size_t n);
|
||||||
evalCopyCommon(C, CE, C.getState(),
|
evalCopyCommon(C, CE, C.getState(),
|
||||||
CE->getArg(2), CE->getArg(1), CE->getArg(0));
|
CE->getArg(2), CE->getArg(1), CE->getArg(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1244,7 +1244,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
|
||||||
state, BO_LE, resultNL, *strLengthNL, cmpTy)
|
state, BO_LE, resultNL, *strLengthNL, cmpTy)
|
||||||
.castAs<DefinedOrUnknownSVal>(), true);
|
.castAs<DefinedOrUnknownSVal>(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxlenValNL) {
|
if (maxlenValNL) {
|
||||||
state = state->assume(C.getSValBuilder().evalBinOpNN(
|
state = state->assume(C.getSValBuilder().evalBinOpNN(
|
||||||
state, BO_LE, resultNL, *maxlenValNL, cmpTy)
|
state, BO_LE, resultNL, *maxlenValNL, cmpTy)
|
||||||
|
@ -1275,8 +1275,8 @@ void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// char *strcpy(char *restrict dst, const char *restrict src);
|
// char *strcpy(char *restrict dst, const char *restrict src);
|
||||||
evalStrcpyCommon(C, CE,
|
evalStrcpyCommon(C, CE,
|
||||||
/* returnEnd = */ false,
|
/* returnEnd = */ false,
|
||||||
/* isBounded = */ false,
|
/* isBounded = */ false,
|
||||||
/* isAppending = */ false);
|
/* isAppending = */ false);
|
||||||
}
|
}
|
||||||
|
@ -1286,8 +1286,8 @@ void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// char *strncpy(char *restrict dst, const char *restrict src, size_t n);
|
// char *strncpy(char *restrict dst, const char *restrict src, size_t n);
|
||||||
evalStrcpyCommon(C, CE,
|
evalStrcpyCommon(C, CE,
|
||||||
/* returnEnd = */ false,
|
/* returnEnd = */ false,
|
||||||
/* isBounded = */ true,
|
/* isBounded = */ true,
|
||||||
/* isAppending = */ false);
|
/* isAppending = */ false);
|
||||||
}
|
}
|
||||||
|
@ -1297,8 +1297,8 @@ void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// char *stpcpy(char *restrict dst, const char *restrict src);
|
// char *stpcpy(char *restrict dst, const char *restrict src);
|
||||||
evalStrcpyCommon(C, CE,
|
evalStrcpyCommon(C, CE,
|
||||||
/* returnEnd = */ true,
|
/* returnEnd = */ true,
|
||||||
/* isBounded = */ false,
|
/* isBounded = */ false,
|
||||||
/* isAppending = */ false);
|
/* isAppending = */ false);
|
||||||
}
|
}
|
||||||
|
@ -1308,8 +1308,8 @@ void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//char *strcat(char *restrict s1, const char *restrict s2);
|
//char *strcat(char *restrict s1, const char *restrict s2);
|
||||||
evalStrcpyCommon(C, CE,
|
evalStrcpyCommon(C, CE,
|
||||||
/* returnEnd = */ false,
|
/* returnEnd = */ false,
|
||||||
/* isBounded = */ false,
|
/* isBounded = */ false,
|
||||||
/* isAppending = */ true);
|
/* isAppending = */ true);
|
||||||
}
|
}
|
||||||
|
@ -1319,8 +1319,8 @@ void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//char *strncat(char *restrict s1, const char *restrict s2, size_t n);
|
//char *strncat(char *restrict s1, const char *restrict s2, size_t n);
|
||||||
evalStrcpyCommon(C, CE,
|
evalStrcpyCommon(C, CE,
|
||||||
/* returnEnd = */ false,
|
/* returnEnd = */ false,
|
||||||
/* isBounded = */ true,
|
/* isBounded = */ true,
|
||||||
/* isAppending = */ true);
|
/* isAppending = */ true);
|
||||||
}
|
}
|
||||||
|
@ -1515,7 +1515,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
|
||||||
|
|
||||||
Optional<NonLoc> srcStrLengthNL = amountCopied.getAs<NonLoc>();
|
Optional<NonLoc> srcStrLengthNL = amountCopied.getAs<NonLoc>();
|
||||||
Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
|
Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
|
||||||
|
|
||||||
// If we know both string lengths, we might know the final string length.
|
// If we know both string lengths, we might know the final string length.
|
||||||
if (srcStrLengthNL && dstStrLengthNL) {
|
if (srcStrLengthNL && dstStrLengthNL) {
|
||||||
// Make sure the two lengths together don't overflow a size_t.
|
// Make sure the two lengths together don't overflow a size_t.
|
||||||
|
@ -1523,7 +1523,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
|
||||||
if (!state)
|
if (!state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *srcStrLengthNL,
|
finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *srcStrLengthNL,
|
||||||
*dstStrLengthNL, sizeTy);
|
*dstStrLengthNL, sizeTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1586,7 +1586,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
|
||||||
if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
|
if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
|
||||||
SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
|
SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
|
||||||
*maxLastNL, ptrTy);
|
*maxLastNL, ptrTy);
|
||||||
state = CheckLocation(C, state, CE->getArg(2), maxLastElement,
|
state = CheckLocation(C, state, CE->getArg(2), maxLastElement,
|
||||||
boundWarning);
|
boundWarning);
|
||||||
if (!state)
|
if (!state)
|
||||||
return;
|
return;
|
||||||
|
@ -1667,7 +1667,7 @@ void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const {
|
||||||
evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false);
|
evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStringChecker::evalStrcasecmp(CheckerContext &C,
|
void CStringChecker::evalStrcasecmp(CheckerContext &C,
|
||||||
const CallExpr *CE) const {
|
const CallExpr *CE) const {
|
||||||
if (CE->getNumArgs() < 2)
|
if (CE->getNumArgs() < 2)
|
||||||
return;
|
return;
|
||||||
|
@ -1676,7 +1676,7 @@ void CStringChecker::evalStrcasecmp(CheckerContext &C,
|
||||||
evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true);
|
evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStringChecker::evalStrncasecmp(CheckerContext &C,
|
void CStringChecker::evalStrncasecmp(CheckerContext &C,
|
||||||
const CallExpr *CE) const {
|
const CallExpr *CE) const {
|
||||||
if (CE->getNumArgs() < 3)
|
if (CE->getNumArgs() < 3)
|
||||||
return;
|
return;
|
||||||
|
@ -1915,7 +1915,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
|
||||||
evalFunction = &CStringChecker::evalBcopy;
|
evalFunction = &CStringChecker::evalBcopy;
|
||||||
else if (C.isCLibraryFunction(FDecl, "bcmp"))
|
else if (C.isCLibraryFunction(FDecl, "bcmp"))
|
||||||
evalFunction = &CStringChecker::evalMemcmp;
|
evalFunction = &CStringChecker::evalMemcmp;
|
||||||
|
|
||||||
// If the callee isn't a string function, let another checker handle it.
|
// If the callee isn't a string function, let another checker handle it.
|
||||||
if (!evalFunction)
|
if (!evalFunction)
|
||||||
return false;
|
return false;
|
||||||
|
@ -1975,7 +1975,7 @@ bool CStringChecker::wantsRegionChangeUpdate(ProgramStateRef state) const {
|
||||||
return !Entries.isEmpty();
|
return !Entries.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
CStringChecker::checkRegionChanges(ProgramStateRef state,
|
CStringChecker::checkRegionChanges(ProgramStateRef state,
|
||||||
const InvalidatedSymbols *,
|
const InvalidatedSymbols *,
|
||||||
ArrayRef<const MemRegion *> ExplicitRegions,
|
ArrayRef<const MemRegion *> ExplicitRegions,
|
||||||
|
|
|
@ -51,7 +51,7 @@ static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID,
|
||||||
if (E->getDecl()->getIdentifier() == SelfII)
|
if (E->getDecl()->getIdentifier() == SelfII)
|
||||||
if (ME->getMethodDecl() == PD->getSetterMethodDecl() &&
|
if (ME->getMethodDecl() == PD->getSetterMethodDecl() &&
|
||||||
ME->getNumArgs() == 1 &&
|
ME->getNumArgs() == 1 &&
|
||||||
ME->getArg(0)->isNullPointerConstant(Ctx,
|
ME->getArg(0)->isNullPointerConstant(Ctx,
|
||||||
Expr::NPC_ValueDependentIsNull))
|
Expr::NPC_ValueDependentIsNull))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID,
|
||||||
if (ObjCPropertyRefExpr *PRE =
|
if (ObjCPropertyRefExpr *PRE =
|
||||||
dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts()))
|
dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts()))
|
||||||
if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD)
|
if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD)
|
||||||
if (BO->getRHS()->isNullPointerConstant(Ctx,
|
if (BO->getRHS()->isNullPointerConstant(Ctx,
|
||||||
Expr::NPC_ValueDependentIsNull)) {
|
Expr::NPC_ValueDependentIsNull)) {
|
||||||
// This is only a 'release' if the property kind is not
|
// This is only a 'release' if the property kind is not
|
||||||
// 'assign'.
|
// 'assign'.
|
||||||
|
|
|
@ -115,7 +115,7 @@ void WalkAST::VisitChildren(Stmt *S) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalkAST::VisitCallExpr(CallExpr *CE) {
|
void WalkAST::VisitCallExpr(CallExpr *CE) {
|
||||||
// Get the callee.
|
// Get the callee.
|
||||||
const FunctionDecl *FD = CE->getDirectCallee();
|
const FunctionDecl *FD = CE->getDirectCallee();
|
||||||
|
|
||||||
if (!FD)
|
if (!FD)
|
||||||
|
@ -307,7 +307,7 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
|
||||||
void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
|
void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
|
||||||
if (!filter.check_gets)
|
if (!filter.check_gets)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
|
const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
|
||||||
if (!FPT)
|
if (!FPT)
|
||||||
return;
|
return;
|
||||||
|
@ -434,18 +434,18 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
|
||||||
.Case("mkdtemp", std::make_pair(0,-1))
|
.Case("mkdtemp", std::make_pair(0,-1))
|
||||||
.Case("mkstemps", std::make_pair(0,1))
|
.Case("mkstemps", std::make_pair(0,1))
|
||||||
.Default(std::make_pair(-1, -1));
|
.Default(std::make_pair(-1, -1));
|
||||||
|
|
||||||
assert(ArgSuffix.first >= 0 && "Unsupported function");
|
assert(ArgSuffix.first >= 0 && "Unsupported function");
|
||||||
|
|
||||||
// Check if the number of arguments is consistent with out expectations.
|
// Check if the number of arguments is consistent with out expectations.
|
||||||
unsigned numArgs = CE->getNumArgs();
|
unsigned numArgs = CE->getNumArgs();
|
||||||
if ((signed) numArgs <= ArgSuffix.first)
|
if ((signed) numArgs <= ArgSuffix.first)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const StringLiteral *strArg =
|
const StringLiteral *strArg =
|
||||||
dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
|
dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
|
||||||
->IgnoreParenImpCasts());
|
->IgnoreParenImpCasts());
|
||||||
|
|
||||||
// Currently we only handle string literals. It is possible to do better,
|
// Currently we only handle string literals. It is possible to do better,
|
||||||
// either by looking at references to const variables, or by doing real
|
// either by looking at references to const variables, or by doing real
|
||||||
// flow analysis.
|
// flow analysis.
|
||||||
|
@ -470,13 +470,13 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
|
||||||
suffix = (unsigned) Result.getZExtValue();
|
suffix = (unsigned) Result.getZExtValue();
|
||||||
n = (n > suffix) ? n - suffix : 0;
|
n = (n > suffix) ? n - suffix : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned i = 0; i < n; ++i)
|
for (unsigned i = 0; i < n; ++i)
|
||||||
if (str[i] == 'X') ++numX;
|
if (str[i] == 'X') ++numX;
|
||||||
|
|
||||||
if (numX >= 6)
|
if (numX >= 6)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Issue a warning.
|
// Issue a warning.
|
||||||
PathDiagnosticLocation CELoc =
|
PathDiagnosticLocation CELoc =
|
||||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||||
|
@ -502,13 +502,13 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Check: Any use of 'strcpy' is insecure.
|
// Check: Any use of 'strcpy' is insecure.
|
||||||
//
|
//
|
||||||
// CWE-119: Improper Restriction of Operations within
|
// CWE-119: Improper Restriction of Operations within
|
||||||
// the Bounds of a Memory Buffer
|
// the Bounds of a Memory Buffer
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
|
void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
|
||||||
if (!filter.check_strcpy)
|
if (!filter.check_strcpy)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!checkCall_strCommon(CE, FD))
|
if (!checkCall_strCommon(CE, FD))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -529,8 +529,8 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Check: Any use of 'strcat' is insecure.
|
// Check: Any use of 'strcat' is insecure.
|
||||||
//
|
//
|
||||||
// CWE-119: Improper Restriction of Operations within
|
// CWE-119: Improper Restriction of Operations within
|
||||||
// the Bounds of a Memory Buffer
|
// the Bounds of a Memory Buffer
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
|
void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
|
||||||
if (!filter.check_strcpy)
|
if (!filter.check_strcpy)
|
||||||
|
@ -684,7 +684,7 @@ void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
|
||||||
void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
|
void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
|
||||||
if (!filter.check_UncheckedReturn)
|
if (!filter.check_UncheckedReturn)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const FunctionDecl *FD = CE->getDirectCallee();
|
const FunctionDecl *FD = CE->getDirectCallee();
|
||||||
if (!FD)
|
if (!FD)
|
||||||
return;
|
return;
|
||||||
|
@ -749,7 +749,7 @@ namespace {
|
||||||
class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
|
class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
|
||||||
public:
|
public:
|
||||||
ChecksFilter filter;
|
ChecksFilter filter;
|
||||||
|
|
||||||
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
|
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
|
||||||
BugReporter &BR) const {
|
BugReporter &BR) const {
|
||||||
WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
|
WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
|
||||||
|
|
|
@ -55,8 +55,8 @@ void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
|
||||||
QualType T = E->getTypeOfArgument();
|
QualType T = E->getTypeOfArgument();
|
||||||
if (T->isPointerType()) {
|
if (T->isPointerType()) {
|
||||||
|
|
||||||
// Many false positives have the form 'sizeof *p'. This is reasonable
|
// Many false positives have the form 'sizeof *p'. This is reasonable
|
||||||
// because people know what they are doing when they intentionally
|
// because people know what they are doing when they intentionally
|
||||||
// dereference the pointer.
|
// dereference the pointer.
|
||||||
Expr *ArgEx = E->getArgumentExpr();
|
Expr *ArgEx = E->getArgumentExpr();
|
||||||
if (!isa<DeclRefExpr>(ArgEx->IgnoreParens()))
|
if (!isa<DeclRefExpr>(ArgEx->IgnoreParens()))
|
||||||
|
|
|
@ -222,7 +222,7 @@ public:
|
||||||
/// changed, this allows the analyzer core to skip the more expensive
|
/// changed, this allows the analyzer core to skip the more expensive
|
||||||
/// #checkRegionChanges when no checkers are tracking any state.
|
/// #checkRegionChanges when no checkers are tracking any state.
|
||||||
bool wantsRegionChangeUpdate(ProgramStateRef St) const { return true; }
|
bool wantsRegionChangeUpdate(ProgramStateRef St) const { return true; }
|
||||||
|
|
||||||
/// \brief Called when the contents of one or more regions change.
|
/// \brief Called when the contents of one or more regions change.
|
||||||
///
|
///
|
||||||
/// This can occur in many different ways: an explicit bind, a blanket
|
/// This can occur in many different ways: an explicit bind, a blanket
|
||||||
|
@ -246,7 +246,7 @@ public:
|
||||||
/// #wantsRegionChangeUpdate returns \c true.
|
/// #wantsRegionChangeUpdate returns \c true.
|
||||||
///
|
///
|
||||||
/// check::RegionChanges
|
/// check::RegionChanges
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
checkRegionChanges(ProgramStateRef State,
|
checkRegionChanges(ProgramStateRef State,
|
||||||
const InvalidatedSymbols *Invalidated,
|
const InvalidatedSymbols *Invalidated,
|
||||||
ArrayRef<const MemRegion *> ExplicitRegions,
|
ArrayRef<const MemRegion *> ExplicitRegions,
|
||||||
|
@ -259,12 +259,12 @@ public:
|
||||||
///
|
///
|
||||||
/// This notifies the checkers about pointer escape, which occurs whenever
|
/// This notifies the checkers about pointer escape, which occurs whenever
|
||||||
/// the analyzer cannot track the symbol any more. For example, as a
|
/// the analyzer cannot track the symbol any more. For example, as a
|
||||||
/// result of assigning a pointer into a global or when it's passed to a
|
/// result of assigning a pointer into a global or when it's passed to a
|
||||||
/// function call the analyzer cannot model.
|
/// function call the analyzer cannot model.
|
||||||
///
|
///
|
||||||
/// \param State The state at the point of escape.
|
/// \param State The state at the point of escape.
|
||||||
/// \param Escaped The list of escaped symbols.
|
/// \param Escaped The list of escaped symbols.
|
||||||
/// \param Call The corresponding CallEvent, if the symbols escape as
|
/// \param Call The corresponding CallEvent, if the symbols escape as
|
||||||
/// parameters to the given call.
|
/// parameters to the given call.
|
||||||
/// \param Kind How the symbols have escaped.
|
/// \param Kind How the symbols have escaped.
|
||||||
/// \returns Checkers can modify the state by returning a new state.
|
/// \returns Checkers can modify the state by returning a new state.
|
||||||
|
@ -285,7 +285,7 @@ public:
|
||||||
PointerEscapeKind Kind) const {
|
PointerEscapeKind Kind) const {
|
||||||
return State;
|
return State;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// check::Event<ImplicitNullDerefEvent>
|
/// check::Event<ImplicitNullDerefEvent>
|
||||||
void checkEvent(ImplicitNullDerefEvent Event) const {}
|
void checkEvent(ImplicitNullDerefEvent Event) const {}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ def DeadCodeAlpha : Package<"deadcode">, InPackage<Alpha>, Hidden;
|
||||||
def Security : Package <"security">;
|
def Security : Package <"security">;
|
||||||
def InsecureAPI : Package<"insecureAPI">, InPackage<Security>;
|
def InsecureAPI : Package<"insecureAPI">, InPackage<Security>;
|
||||||
def SecurityAlpha : Package<"security">, InPackage<Alpha>, Hidden;
|
def SecurityAlpha : Package<"security">, InPackage<Alpha>, Hidden;
|
||||||
def Taint : Package<"taint">, InPackage<SecurityAlpha>, Hidden;
|
def Taint : Package<"taint">, InPackage<SecurityAlpha>, Hidden;
|
||||||
|
|
||||||
def Unix : Package<"unix">;
|
def Unix : Package<"unix">;
|
||||||
def UnixAlpha : Package<"unix">, InPackage<Alpha>, Hidden;
|
def UnixAlpha : Package<"unix">, InPackage<Alpha>, Hidden;
|
||||||
|
@ -192,7 +192,7 @@ def UndefBranchChecker : Checker<"Branch">,
|
||||||
def UndefCapturedBlockVarChecker : Checker<"CapturedBlockVariable">,
|
def UndefCapturedBlockVarChecker : Checker<"CapturedBlockVariable">,
|
||||||
HelpText<"Check for blocks that capture uninitialized values">,
|
HelpText<"Check for blocks that capture uninitialized values">,
|
||||||
DescFile<"UndefCapturedBlockVarChecker.cpp">;
|
DescFile<"UndefCapturedBlockVarChecker.cpp">;
|
||||||
|
|
||||||
def ReturnUndefChecker : Checker<"UndefReturn">,
|
def ReturnUndefChecker : Checker<"UndefReturn">,
|
||||||
HelpText<"Check for uninitialized values being returned to the caller">,
|
HelpText<"Check for uninitialized values being returned to the caller">,
|
||||||
DescFile<"ReturnUndefChecker.cpp">;
|
DescFile<"ReturnUndefChecker.cpp">;
|
||||||
|
@ -206,11 +206,11 @@ def ReturnUndefChecker : Checker<"UndefReturn">,
|
||||||
let ParentPackage = Cplusplus in {
|
let ParentPackage = Cplusplus in {
|
||||||
|
|
||||||
def NewDeleteChecker : Checker<"NewDelete">,
|
def NewDeleteChecker : Checker<"NewDelete">,
|
||||||
HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">,
|
HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">,
|
||||||
DescFile<"MallocChecker.cpp">;
|
DescFile<"MallocChecker.cpp">;
|
||||||
|
|
||||||
def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">,
|
def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">,
|
||||||
HelpText<"Check for memory leaks. Traces memory managed by new/delete.">,
|
HelpText<"Check for memory leaks. Traces memory managed by new/delete.">,
|
||||||
DescFile<"MallocChecker.cpp">;
|
DescFile<"MallocChecker.cpp">;
|
||||||
|
|
||||||
} // end: "cplusplus"
|
} // end: "cplusplus"
|
||||||
|
@ -218,7 +218,7 @@ def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">,
|
||||||
let ParentPackage = CplusplusAlpha in {
|
let ParentPackage = CplusplusAlpha in {
|
||||||
|
|
||||||
def VirtualCallChecker : Checker<"VirtualCall">,
|
def VirtualCallChecker : Checker<"VirtualCall">,
|
||||||
HelpText<"Check virtual function calls during construction or destruction">,
|
HelpText<"Check virtual function calls during construction or destruction">,
|
||||||
DescFile<"VirtualCallChecker.cpp">;
|
DescFile<"VirtualCallChecker.cpp">;
|
||||||
|
|
||||||
} // end: "alpha.cplusplus"
|
} // end: "alpha.cplusplus"
|
||||||
|
@ -282,7 +282,7 @@ let ParentPackage = SecurityAlpha in {
|
||||||
|
|
||||||
def ArrayBoundChecker : Checker<"ArrayBound">,
|
def ArrayBoundChecker : Checker<"ArrayBound">,
|
||||||
HelpText<"Warn about buffer overflows (older checker)">,
|
HelpText<"Warn about buffer overflows (older checker)">,
|
||||||
DescFile<"ArrayBoundChecker.cpp">;
|
DescFile<"ArrayBoundChecker.cpp">;
|
||||||
|
|
||||||
def ArrayBoundCheckerV2 : Checker<"ArrayBoundV2">,
|
def ArrayBoundCheckerV2 : Checker<"ArrayBoundV2">,
|
||||||
HelpText<"Warn about buffer overflows (newer checker)">,
|
HelpText<"Warn about buffer overflows (newer checker)">,
|
||||||
|
@ -323,7 +323,7 @@ def UnixAPIChecker : Checker<"API">,
|
||||||
def MallocChecker: Checker<"Malloc">,
|
def MallocChecker: Checker<"Malloc">,
|
||||||
HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free().">,
|
HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free().">,
|
||||||
DescFile<"MallocChecker.cpp">;
|
DescFile<"MallocChecker.cpp">;
|
||||||
|
|
||||||
def MallocSizeofChecker : Checker<"MallocSizeof">,
|
def MallocSizeofChecker : Checker<"MallocSizeof">,
|
||||||
HelpText<"Check for dubious malloc arguments involving sizeof">,
|
HelpText<"Check for dubious malloc arguments involving sizeof">,
|
||||||
DescFile<"MallocSizeofChecker.cpp">;
|
DescFile<"MallocSizeofChecker.cpp">;
|
||||||
|
@ -331,7 +331,7 @@ def MallocSizeofChecker : Checker<"MallocSizeof">,
|
||||||
def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">,
|
def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">,
|
||||||
HelpText<"Check for mismatched deallocators.">,
|
HelpText<"Check for mismatched deallocators.">,
|
||||||
DescFile<"MallocChecker.cpp">;
|
DescFile<"MallocChecker.cpp">;
|
||||||
|
|
||||||
} // end "unix"
|
} // end "unix"
|
||||||
|
|
||||||
let ParentPackage = UnixAlpha in {
|
let ParentPackage = UnixAlpha in {
|
||||||
|
@ -362,7 +362,7 @@ def CStringNullArg : Checker<"NullArg">,
|
||||||
|
|
||||||
def CStringSyntaxChecker : Checker<"BadSizeArg">,
|
def CStringSyntaxChecker : Checker<"BadSizeArg">,
|
||||||
HelpText<"Check the size argument passed into C string functions for common erroneous patterns">,
|
HelpText<"Check the size argument passed into C string functions for common erroneous patterns">,
|
||||||
DescFile<"CStringSyntaxChecker.cpp">;
|
DescFile<"CStringSyntaxChecker.cpp">;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ParentPackage = CStringAlpha in {
|
let ParentPackage = CStringAlpha in {
|
||||||
|
@ -514,7 +514,7 @@ def ObjCContainersASTChecker : Checker<"PointerSizedValues">,
|
||||||
def ObjCContainersChecker : Checker<"OutOfBounds">,
|
def ObjCContainersChecker : Checker<"OutOfBounds">,
|
||||||
HelpText<"Checks for index out-of-bounds when using 'CFArray' API">,
|
HelpText<"Checks for index out-of-bounds when using 'CFArray' API">,
|
||||||
DescFile<"ObjCContainersChecker.cpp">;
|
DescFile<"ObjCContainersChecker.cpp">;
|
||||||
|
|
||||||
}
|
}
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Checkers for LLVM development.
|
// Checkers for LLVM development.
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace {
|
||||||
|
|
||||||
// enum value that represent the jail state
|
// enum value that represent the jail state
|
||||||
enum Kind { NO_CHROOT, ROOT_CHANGED, JAIL_ENTERED };
|
enum Kind { NO_CHROOT, ROOT_CHANGED, JAIL_ENTERED };
|
||||||
|
|
||||||
bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; }
|
bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; }
|
||||||
//bool isJailEntered(intptr_t k) { return k == JAIL_ENTERED; }
|
//bool isJailEntered(intptr_t k) { return k == JAIL_ENTERED; }
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ public:
|
||||||
static int x;
|
static int x;
|
||||||
return &x;
|
return &x;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
|
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
|
||||||
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
|
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
|
||||||
|
|
||||||
|
@ -87,8 +87,8 @@ bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
|
||||||
void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
|
void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
ProgramStateManager &Mgr = state->getStateManager();
|
ProgramStateManager &Mgr = state->getStateManager();
|
||||||
|
|
||||||
// Once encouter a chroot(), set the enum value ROOT_CHANGED directly in
|
// Once encouter a chroot(), set the enum value ROOT_CHANGED directly in
|
||||||
// the GDM.
|
// the GDM.
|
||||||
state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED);
|
state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED);
|
||||||
C.addTransition(state);
|
C.addTransition(state);
|
||||||
|
@ -106,7 +106,7 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
|
||||||
// After chdir("/"), enter the jail, set the enum value JAIL_ENTERED.
|
// After chdir("/"), enter the jail, set the enum value JAIL_ENTERED.
|
||||||
const Expr *ArgExpr = CE->getArg(0);
|
const Expr *ArgExpr = CE->getArg(0);
|
||||||
SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext());
|
SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext());
|
||||||
|
|
||||||
if (const MemRegion *R = ArgVal.getAsRegion()) {
|
if (const MemRegion *R = ArgVal.getAsRegion()) {
|
||||||
R = R->StripCasts();
|
R = R->StripCasts();
|
||||||
if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) {
|
if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) {
|
||||||
|
@ -135,7 +135,7 @@ void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
|
||||||
// Ingnore chroot and chdir.
|
// Ingnore chroot and chdir.
|
||||||
if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir)
|
if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If jail state is ROOT_CHANGED, generate BugReport.
|
// If jail state is ROOT_CHANGED, generate BugReport.
|
||||||
void *const* k = C.getState()->FindGDM(ChrootChecker::getTag());
|
void *const* k = C.getState()->FindGDM(ChrootChecker::getTag());
|
||||||
if (k)
|
if (k)
|
||||||
|
|
|
@ -28,36 +28,36 @@
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
using namespace ento;
|
using namespace ento;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// A simple visitor to record what VarDecls occur in EH-handling code.
|
/// A simple visitor to record what VarDecls occur in EH-handling code.
|
||||||
class EHCodeVisitor : public RecursiveASTVisitor<EHCodeVisitor> {
|
class EHCodeVisitor : public RecursiveASTVisitor<EHCodeVisitor> {
|
||||||
public:
|
public:
|
||||||
bool inEH;
|
bool inEH;
|
||||||
llvm::DenseSet<const VarDecl *> &S;
|
llvm::DenseSet<const VarDecl *> &S;
|
||||||
|
|
||||||
bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
|
bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
|
||||||
SaveAndRestore<bool> inFinally(inEH, true);
|
SaveAndRestore<bool> inFinally(inEH, true);
|
||||||
return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
|
return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) {
|
bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) {
|
||||||
SaveAndRestore<bool> inCatch(inEH, true);
|
SaveAndRestore<bool> inCatch(inEH, true);
|
||||||
return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
|
return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TraverseCXXCatchStmt(CXXCatchStmt *S) {
|
bool TraverseCXXCatchStmt(CXXCatchStmt *S) {
|
||||||
SaveAndRestore<bool> inCatch(inEH, true);
|
SaveAndRestore<bool> inCatch(inEH, true);
|
||||||
return TraverseStmt(S->getHandlerBlock());
|
return TraverseStmt(S->getHandlerBlock());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VisitDeclRefExpr(DeclRefExpr *DR) {
|
bool VisitDeclRefExpr(DeclRefExpr *DR) {
|
||||||
if (inEH)
|
if (inEH)
|
||||||
if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl()))
|
if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl()))
|
||||||
S.insert(D);
|
S.insert(D);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) :
|
EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) :
|
||||||
inEH(false), S(S) {}
|
inEH(false), S(S) {}
|
||||||
};
|
};
|
||||||
|
@ -70,9 +70,9 @@ class ReachableCode {
|
||||||
public:
|
public:
|
||||||
ReachableCode(const CFG &cfg)
|
ReachableCode(const CFG &cfg)
|
||||||
: cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {}
|
: cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {}
|
||||||
|
|
||||||
void computeReachableBlocks();
|
void computeReachableBlocks();
|
||||||
|
|
||||||
bool isReachable(const CFGBlock *block) const {
|
bool isReachable(const CFGBlock *block) const {
|
||||||
return reachable[block->getBlockID()];
|
return reachable[block->getBlockID()];
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ public:
|
||||||
void ReachableCode::computeReachableBlocks() {
|
void ReachableCode::computeReachableBlocks() {
|
||||||
if (!cfg.getNumBlockIDs())
|
if (!cfg.getNumBlockIDs())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SmallVector<const CFGBlock*, 10> worklist;
|
SmallVector<const CFGBlock*, 10> worklist;
|
||||||
worklist.push_back(&cfg.getEntry());
|
worklist.push_back(&cfg.getEntry());
|
||||||
|
|
||||||
|
@ -160,19 +160,19 @@ public:
|
||||||
// to analyze that yet.
|
// to analyze that yet.
|
||||||
return InEH->count(D);
|
return InEH->count(D);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Report(const VarDecl *V, DeadStoreKind dsk,
|
void Report(const VarDecl *V, DeadStoreKind dsk,
|
||||||
PathDiagnosticLocation L, SourceRange R) {
|
PathDiagnosticLocation L, SourceRange R) {
|
||||||
if (Escaped.count(V))
|
if (Escaped.count(V))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Compute reachable blocks within the CFG for trivial cases
|
// Compute reachable blocks within the CFG for trivial cases
|
||||||
// where a bogus dead store can be reported because itself is unreachable.
|
// where a bogus dead store can be reported because itself is unreachable.
|
||||||
if (!reachableCode.get()) {
|
if (!reachableCode.get()) {
|
||||||
reachableCode.reset(new ReachableCode(cfg));
|
reachableCode.reset(new ReachableCode(cfg));
|
||||||
reachableCode->computeReachableBlocks();
|
reachableCode->computeReachableBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reachableCode->isReachable(currentBlock))
|
if (!reachableCode->isReachable(currentBlock))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ public:
|
||||||
|
|
||||||
case Enclosing:
|
case Enclosing:
|
||||||
// Don't report issues in this case, e.g.: "if (x = foo())",
|
// Don't report issues in this case, e.g.: "if (x = foo())",
|
||||||
// where 'x' is unused later. We have yet to see a case where
|
// where 'x' is unused later. We have yet to see a case where
|
||||||
// this is a real bug.
|
// this is a real bug.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,7 @@ public:
|
||||||
const LiveVariables::LivenessValues &Live) override {
|
const LiveVariables::LivenessValues &Live) override {
|
||||||
|
|
||||||
currentBlock = block;
|
currentBlock = block;
|
||||||
|
|
||||||
// Skip statements in macros.
|
// Skip statements in macros.
|
||||||
if (S->getLocStart().isMacroID())
|
if (S->getLocStart().isMacroID())
|
||||||
return;
|
return;
|
||||||
|
@ -276,7 +276,7 @@ public:
|
||||||
const Expr *RHS =
|
const Expr *RHS =
|
||||||
LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
|
LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
|
||||||
RHS = RHS->IgnoreParenCasts();
|
RHS = RHS->IgnoreParenCasts();
|
||||||
|
|
||||||
QualType T = VD->getType();
|
QualType T = VD->getType();
|
||||||
if (T->isPointerType() || T->isObjCObjectPointerType()) {
|
if (T->isPointerType() || T->isObjCObjectPointerType()) {
|
||||||
if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull))
|
if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull))
|
||||||
|
@ -318,27 +318,27 @@ public:
|
||||||
|
|
||||||
if (!V)
|
if (!V)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (V->hasLocalStorage()) {
|
if (V->hasLocalStorage()) {
|
||||||
// Reference types confuse the dead stores checker. Skip them
|
// Reference types confuse the dead stores checker. Skip them
|
||||||
// for now.
|
// for now.
|
||||||
if (V->getType()->getAs<ReferenceType>())
|
if (V->getType()->getAs<ReferenceType>())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (const Expr *E = V->getInit()) {
|
if (const Expr *E = V->getInit()) {
|
||||||
while (const ExprWithCleanups *exprClean =
|
while (const ExprWithCleanups *exprClean =
|
||||||
dyn_cast<ExprWithCleanups>(E))
|
dyn_cast<ExprWithCleanups>(E))
|
||||||
E = exprClean->getSubExpr();
|
E = exprClean->getSubExpr();
|
||||||
|
|
||||||
// Look through transitive assignments, e.g.:
|
// Look through transitive assignments, e.g.:
|
||||||
// int x = y = 0;
|
// int x = y = 0;
|
||||||
E = LookThroughTransitiveAssignmentsAndCommaOperators(E);
|
E = LookThroughTransitiveAssignmentsAndCommaOperators(E);
|
||||||
|
|
||||||
// Don't warn on C++ objects (yet) until we can show that their
|
// Don't warn on C++ objects (yet) until we can show that their
|
||||||
// constructors/destructors don't have side effects.
|
// constructors/destructors don't have side effects.
|
||||||
if (isa<CXXConstructExpr>(E))
|
if (isa<CXXConstructExpr>(E))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// A dead initialization is a variable that is dead after it
|
// A dead initialization is a variable that is dead after it
|
||||||
// is initialized. We don't flag warnings for those variables
|
// is initialized. We don't flag warnings for those variables
|
||||||
// marked 'unused' or 'objc_precise_lifetime'.
|
// marked 'unused' or 'objc_precise_lifetime'.
|
||||||
|
|
|
@ -84,7 +84,7 @@ DereferenceChecker::AddDerefSource(raw_ostream &os,
|
||||||
SourceLocation L = IV->getLocation();
|
SourceLocation L = IV->getLocation();
|
||||||
Ranges.push_back(SourceRange(L, L));
|
Ranges.push_back(SourceRange(L, L));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > {
|
||||||
CheckerContext &C) const ;
|
CheckerContext &C) const ;
|
||||||
public:
|
public:
|
||||||
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
|
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
void DivZeroChecker::reportBug(const char *Msg,
|
void DivZeroChecker::reportBug(const char *Msg,
|
||||||
|
|
|
@ -113,7 +113,7 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
|
||||||
|
|
||||||
ProgramStateRef State = C.getState();
|
ProgramStateRef State = C.getState();
|
||||||
const ObjCMethodDecl *D = Msg->getDecl();
|
const ObjCMethodDecl *D = Msg->getDecl();
|
||||||
|
|
||||||
if (D && D->hasRelatedResultType()) {
|
if (D && D->hasRelatedResultType()) {
|
||||||
switch (Msg->getMethodFamily()) {
|
switch (Msg->getMethodFamily()) {
|
||||||
default:
|
default:
|
||||||
|
@ -201,7 +201,7 @@ void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE,
|
||||||
const MemRegion *MR = C.getSVal(NewE).getAsRegion();
|
const MemRegion *MR = C.getSVal(NewE).getAsRegion();
|
||||||
if (!MR)
|
if (!MR)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
C.addTransition(C.getState()->setDynamicTypeInfo(MR, NewE->getType(),
|
C.addTransition(C.getState()->setDynamicTypeInfo(MR, NewE->getType(),
|
||||||
/*CanBeSubclass=*/false));
|
/*CanBeSubclass=*/false));
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ using namespace clang;
|
||||||
using namespace ento;
|
using namespace ento;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class FixedAddressChecker
|
class FixedAddressChecker
|
||||||
: public Checker< check::PreStmt<BinaryOperator> > {
|
: public Checker< check::PreStmt<BinaryOperator> > {
|
||||||
mutable std::unique_ptr<BuiltinBug> BT;
|
mutable std::unique_ptr<BuiltinBug> BT;
|
||||||
|
|
||||||
|
|
|
@ -100,8 +100,8 @@ private:
|
||||||
/// Generate a report if the expression is tainted or points to tainted data.
|
/// Generate a report if the expression is tainted or points to tainted data.
|
||||||
bool generateReportIfTainted(const Expr *E, const char Msg[],
|
bool generateReportIfTainted(const Expr *E, const char Msg[],
|
||||||
CheckerContext &C) const;
|
CheckerContext &C) const;
|
||||||
|
|
||||||
|
|
||||||
typedef SmallVector<unsigned, 2> ArgVector;
|
typedef SmallVector<unsigned, 2> ArgVector;
|
||||||
|
|
||||||
/// \brief A struct used to specify taint propagation rules for a function.
|
/// \brief A struct used to specify taint propagation rules for a function.
|
||||||
|
@ -441,7 +441,7 @@ SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C,
|
||||||
return Val.getAsSymbol();
|
return Val.getAsSymbol();
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
|
GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
ProgramStateRef State = C.getState();
|
ProgramStateRef State = C.getState();
|
||||||
|
@ -667,8 +667,8 @@ bool GenericTaintChecker::checkUncontrolledFormatString(const CallExpr *CE,
|
||||||
bool GenericTaintChecker::checkSystemCall(const CallExpr *CE,
|
bool GenericTaintChecker::checkSystemCall(const CallExpr *CE,
|
||||||
StringRef Name,
|
StringRef Name,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
// TODO: It might make sense to run this check on demand. In some cases,
|
// TODO: It might make sense to run this check on demand. In some cases,
|
||||||
// we should check if the environment has been cleansed here. We also might
|
// we should check if the environment has been cleansed here. We also might
|
||||||
// need to know if the user was reset before these calls(seteuid).
|
// need to know if the user was reset before these calls(seteuid).
|
||||||
unsigned ArgNum = llvm::StringSwitch<unsigned>(Name)
|
unsigned ArgNum = llvm::StringSwitch<unsigned>(Name)
|
||||||
.Case("system", 0)
|
.Case("system", 0)
|
||||||
|
|
|
@ -96,7 +96,7 @@ void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B,
|
||||||
}
|
}
|
||||||
LHS = B2->getLHS();
|
LHS = B2->getLHS();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) {
|
if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) {
|
||||||
Sr[0] = RHS->getSourceRange();
|
Sr[0] = RHS->getSourceRange();
|
||||||
Sr[1] = LHS->getSourceRange();
|
Sr[1] = LHS->getSourceRange();
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
// been called on them. An invalidation method should either invalidate all
|
// been called on them. An invalidation method should either invalidate all
|
||||||
// the ivars or call another invalidation method (on self).
|
// the ivars or call another invalidation method (on self).
|
||||||
//
|
//
|
||||||
// Partial invalidor annotation allows to addess cases when ivars are
|
// Partial invalidor annotation allows to addess cases when ivars are
|
||||||
// invalidated by other methods, which might or might not be called from
|
// invalidated by other methods, which might or might not be called from
|
||||||
// the invalidation method. The checker checks that each invalidation
|
// the invalidation method. The checker checks that each invalidation
|
||||||
// method and all the partial methods cumulatively invalidate all ivars.
|
// method and all the partial methods cumulatively invalidate all ivars.
|
||||||
// __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
// __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
||||||
|
@ -310,7 +310,7 @@ const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
|
||||||
|
|
||||||
// Lookup for the synthesized case.
|
// Lookup for the synthesized case.
|
||||||
IvarD = Prop->getPropertyIvarDecl();
|
IvarD = Prop->getPropertyIvarDecl();
|
||||||
// We only track the ivars/properties that are defined in the current
|
// We only track the ivars/properties that are defined in the current
|
||||||
// class (not the parent).
|
// class (not the parent).
|
||||||
if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
|
if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
|
||||||
if (TrackedIvars.count(IvarD)) {
|
if (TrackedIvars.count(IvarD)) {
|
||||||
|
|
|
@ -118,7 +118,7 @@ private:
|
||||||
SValBuilder &Builder) const {
|
SValBuilder &Builder) const {
|
||||||
return definitelyReturnedError(RetSym, State, Builder, true);
|
return definitelyReturnedError(RetSym, State, Builder, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark an AllocationPair interesting for diagnostic reporting.
|
/// Mark an AllocationPair interesting for diagnostic reporting.
|
||||||
void markInteresting(BugReport *R, const AllocationPair &AP) const {
|
void markInteresting(BugReport *R, const AllocationPair &AP) const {
|
||||||
R->markInteresting(AP.first);
|
R->markInteresting(AP.first);
|
||||||
|
@ -282,7 +282,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
|
||||||
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
||||||
if (!FD || FD->getKind() != Decl::Function)
|
if (!FD || FD->getKind() != Decl::Function)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
StringRef funName = C.getCalleeName(FD);
|
StringRef funName = C.getCalleeName(FD);
|
||||||
if (funName.empty())
|
if (funName.empty())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -79,7 +79,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
|
||||||
if (TrimmedFName != FName)
|
if (TrimmedFName != FName)
|
||||||
FName = TrimmedFName;
|
FName = TrimmedFName;
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallString<256> S;
|
SmallString<256> S;
|
||||||
llvm::raw_svector_ostream os(S);
|
llvm::raw_svector_ostream os(S);
|
||||||
os << "Call to '" << FName << "' uses";
|
os << "Call to '" << FName << "' uses";
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
##===- clang/lib/Checker/Makefile --------------------------*- Makefile -*-===##
|
##===- clang/lib/Checker/Makefile --------------------------*- Makefile -*-===##
|
||||||
#
|
#
|
||||||
# The LLVM Compiler Infrastructure
|
# The LLVM Compiler Infrastructure
|
||||||
#
|
#
|
||||||
# This file is distributed under the University of Illinois Open Source
|
# This file is distributed under the University of Illinois Open Source
|
||||||
# License. See LICENSE.TXT for details.
|
# License. See LICENSE.TXT for details.
|
||||||
#
|
#
|
||||||
##===----------------------------------------------------------------------===##
|
##===----------------------------------------------------------------------===##
|
||||||
#
|
#
|
||||||
# This implements analyses built on top of source-level CFGs.
|
# This implements analyses built on top of source-level CFGs.
|
||||||
#
|
#
|
||||||
##===----------------------------------------------------------------------===##
|
##===----------------------------------------------------------------------===##
|
||||||
|
|
||||||
|
|
|
@ -65,10 +65,10 @@ class RefState {
|
||||||
|
|
||||||
const Stmt *S;
|
const Stmt *S;
|
||||||
unsigned K : 3; // Kind enum, but stored as a bitfield.
|
unsigned K : 3; // Kind enum, but stored as a bitfield.
|
||||||
unsigned Family : 29; // Rest of 32-bit word, currently just an allocation
|
unsigned Family : 29; // Rest of 32-bit word, currently just an allocation
|
||||||
// family.
|
// family.
|
||||||
|
|
||||||
RefState(Kind k, const Stmt *s, unsigned family)
|
RefState(Kind k, const Stmt *s, unsigned family)
|
||||||
: S(s), K(k), Family(family) {
|
: S(s), K(k), Family(family) {
|
||||||
assert(family != AF_None);
|
assert(family != AF_None);
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ public:
|
||||||
return RefState(AllocatedOfSizeZero, RS->getStmt(),
|
return RefState(AllocatedOfSizeZero, RS->getStmt(),
|
||||||
RS->getAllocationFamily());
|
RS->getAllocationFamily());
|
||||||
}
|
}
|
||||||
static RefState getReleased(unsigned family, const Stmt *s) {
|
static RefState getReleased(unsigned family, const Stmt *s) {
|
||||||
return RefState(Released, s, family);
|
return RefState(Released, s, family);
|
||||||
}
|
}
|
||||||
static RefState getRelinquished(unsigned family, const Stmt *s) {
|
static RefState getRelinquished(unsigned family, const Stmt *s) {
|
||||||
|
@ -169,9 +169,9 @@ class MallocChecker : public Checker<check::DeadSymbols,
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MallocChecker()
|
MallocChecker()
|
||||||
: II_alloca(nullptr), II_malloc(nullptr), II_free(nullptr),
|
: II_alloca(nullptr), II_malloc(nullptr), II_free(nullptr),
|
||||||
II_realloc(nullptr), II_calloc(nullptr), II_valloc(nullptr),
|
II_realloc(nullptr), II_calloc(nullptr), II_valloc(nullptr),
|
||||||
II_reallocf(nullptr), II_strndup(nullptr), II_strdup(nullptr),
|
II_reallocf(nullptr), II_strndup(nullptr), II_strdup(nullptr),
|
||||||
II_kmalloc(nullptr), II_if_nameindex(nullptr),
|
II_kmalloc(nullptr), II_if_nameindex(nullptr),
|
||||||
II_if_freenameindex(nullptr) {}
|
II_if_freenameindex(nullptr) {}
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ public:
|
||||||
CK_NumCheckKinds
|
CK_NumCheckKinds
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class MemoryOperationKind {
|
enum class MemoryOperationKind {
|
||||||
MOK_Allocate,
|
MOK_Allocate,
|
||||||
MOK_Free,
|
MOK_Free,
|
||||||
MOK_Any
|
MOK_Any
|
||||||
|
@ -245,19 +245,19 @@ private:
|
||||||
/// \brief Print names of allocators and deallocators.
|
/// \brief Print names of allocators and deallocators.
|
||||||
///
|
///
|
||||||
/// \returns true on success.
|
/// \returns true on success.
|
||||||
bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
|
bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
|
||||||
const Expr *E) const;
|
const Expr *E) const;
|
||||||
|
|
||||||
/// \brief Print expected name of an allocator based on the deallocator's
|
/// \brief Print expected name of an allocator based on the deallocator's
|
||||||
/// family derived from the DeallocExpr.
|
/// family derived from the DeallocExpr.
|
||||||
void printExpectedAllocName(raw_ostream &os, CheckerContext &C,
|
void printExpectedAllocName(raw_ostream &os, CheckerContext &C,
|
||||||
const Expr *DeallocExpr) const;
|
const Expr *DeallocExpr) const;
|
||||||
/// \brief Print expected name of a deallocator based on the allocator's
|
/// \brief Print expected name of a deallocator based on the allocator's
|
||||||
/// family.
|
/// family.
|
||||||
void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const;
|
void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const;
|
||||||
|
|
||||||
///@{
|
///@{
|
||||||
/// Check if this is one of the functions which can allocate/reallocate memory
|
/// Check if this is one of the functions which can allocate/reallocate memory
|
||||||
/// pointed to by one of its arguments.
|
/// pointed to by one of its arguments.
|
||||||
bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
|
bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
|
||||||
bool isCMemFunction(const FunctionDecl *FD,
|
bool isCMemFunction(const FunctionDecl *FD,
|
||||||
|
@ -292,7 +292,7 @@ private:
|
||||||
const ProgramStateRef &State) const;
|
const ProgramStateRef &State) const;
|
||||||
|
|
||||||
/// Update the RefState to reflect the new memory allocation.
|
/// Update the RefState to reflect the new memory allocation.
|
||||||
static ProgramStateRef
|
static ProgramStateRef
|
||||||
MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
|
MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
|
||||||
AllocationFamily Family = AF_Malloc);
|
AllocationFamily Family = AF_Malloc);
|
||||||
|
|
||||||
|
@ -312,17 +312,17 @@ private:
|
||||||
bool ReturnsNullOnFailure = false) const;
|
bool ReturnsNullOnFailure = false) const;
|
||||||
|
|
||||||
ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
|
ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
|
||||||
bool FreesMemOnFailure,
|
bool FreesMemOnFailure,
|
||||||
ProgramStateRef State) const;
|
ProgramStateRef State) const;
|
||||||
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
|
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
|
||||||
ProgramStateRef State);
|
ProgramStateRef State);
|
||||||
|
|
||||||
///\brief Check if the memory associated with this symbol was released.
|
///\brief Check if the memory associated with this symbol was released.
|
||||||
bool isReleased(SymbolRef Sym, CheckerContext &C) const;
|
bool isReleased(SymbolRef Sym, CheckerContext &C) const;
|
||||||
|
|
||||||
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
|
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
|
||||||
|
|
||||||
void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
|
void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
|
||||||
const Stmt *S) const;
|
const Stmt *S) const;
|
||||||
|
|
||||||
bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;
|
bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;
|
||||||
|
@ -330,7 +330,7 @@ private:
|
||||||
/// Check if the function is known free memory, or if it is
|
/// Check if the function is known free memory, or if it is
|
||||||
/// "interesting" and should be modeled explicitly.
|
/// "interesting" and should be modeled explicitly.
|
||||||
///
|
///
|
||||||
/// \param [out] EscapingSymbol A function might not free memory in general,
|
/// \param [out] EscapingSymbol A function might not free memory in general,
|
||||||
/// but could be known to free a particular symbol. In this case, false is
|
/// but could be known to free a particular symbol. In this case, false is
|
||||||
/// returned and the single escaping symbol is returned through the out
|
/// returned and the single escaping symbol is returned through the out
|
||||||
/// parameter.
|
/// parameter.
|
||||||
|
@ -357,20 +357,20 @@ private:
|
||||||
Optional<CheckKind> getCheckIfTracked(CheckerContext &C,
|
Optional<CheckKind> getCheckIfTracked(CheckerContext &C,
|
||||||
const Stmt *AllocDeallocStmt,
|
const Stmt *AllocDeallocStmt,
|
||||||
bool IsALeakCheck = false) const;
|
bool IsALeakCheck = false) const;
|
||||||
Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
|
Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
|
||||||
bool IsALeakCheck = false) const;
|
bool IsALeakCheck = false) const;
|
||||||
///@}
|
///@}
|
||||||
static bool SummarizeValue(raw_ostream &os, SVal V);
|
static bool SummarizeValue(raw_ostream &os, SVal V);
|
||||||
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
|
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
|
||||||
void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
|
void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
|
||||||
const Expr *DeallocExpr) const;
|
const Expr *DeallocExpr) const;
|
||||||
void ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
|
void ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
|
||||||
SourceRange Range) const;
|
SourceRange Range) const;
|
||||||
void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,
|
void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,
|
||||||
const Expr *DeallocExpr, const RefState *RS,
|
const Expr *DeallocExpr, const RefState *RS,
|
||||||
SymbolRef Sym, bool OwnershipTransferred) const;
|
SymbolRef Sym, bool OwnershipTransferred) const;
|
||||||
void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
|
void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
|
||||||
const Expr *DeallocExpr,
|
const Expr *DeallocExpr,
|
||||||
const Expr *AllocExpr = nullptr) const;
|
const Expr *AllocExpr = nullptr) const;
|
||||||
void ReportUseAfterFree(CheckerContext &C, SourceRange Range,
|
void ReportUseAfterFree(CheckerContext &C, SourceRange Range,
|
||||||
SymbolRef Sym) const;
|
SymbolRef Sym) const;
|
||||||
|
@ -425,8 +425,8 @@ private:
|
||||||
const Stmt *Stmt) {
|
const Stmt *Stmt) {
|
||||||
// Did not track -> allocated. Other state (released) -> allocated.
|
// Did not track -> allocated. Other state (released) -> allocated.
|
||||||
return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
|
return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
|
||||||
(S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
|
(S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
|
||||||
(!SPrev || !(SPrev->isAllocated() ||
|
(!SPrev || !(SPrev->isAllocated() ||
|
||||||
SPrev->isAllocatedOfSizeZero())));
|
SPrev->isAllocatedOfSizeZero())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,7 +509,7 @@ private:
|
||||||
REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)
|
REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)
|
||||||
REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
|
REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
|
||||||
|
|
||||||
// A map from the freed symbol to the symbol representing the return value of
|
// A map from the freed symbol to the symbol representing the return value of
|
||||||
// the free function.
|
// the free function.
|
||||||
REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef)
|
REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef)
|
||||||
|
|
||||||
|
@ -633,7 +633,7 @@ bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
OverloadedOperatorKind Kind = FD->getOverloadedOperator();
|
OverloadedOperatorKind Kind = FD->getOverloadedOperator();
|
||||||
if (Kind != OO_New && Kind != OO_Array_New &&
|
if (Kind != OO_New && Kind != OO_Array_New &&
|
||||||
Kind != OO_Delete && Kind != OO_Array_Delete)
|
Kind != OO_Delete && Kind != OO_Array_Delete)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -798,8 +798,8 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
|
||||||
State = ProcessZeroAllocation(C, CE, 0, State);
|
State = ProcessZeroAllocation(C, CE, 0, State);
|
||||||
} else if (isStandardNewDelete(FD, C.getASTContext())) {
|
} else if (isStandardNewDelete(FD, C.getASTContext())) {
|
||||||
// Process direct calls to operator new/new[]/delete/delete[] functions
|
// Process direct calls to operator new/new[]/delete/delete[] functions
|
||||||
// as distinct from new/new[]/delete/delete[] expressions that are
|
// as distinct from new/new[]/delete/delete[] expressions that are
|
||||||
// processed by the checkPostStmt callbacks for CXXNewExpr and
|
// processed by the checkPostStmt callbacks for CXXNewExpr and
|
||||||
// CXXDeleteExpr.
|
// CXXDeleteExpr.
|
||||||
OverloadedOperatorKind K = FD->getOverloadedOperator();
|
OverloadedOperatorKind K = FD->getOverloadedOperator();
|
||||||
if (K == OO_New) {
|
if (K == OO_New) {
|
||||||
|
@ -869,7 +869,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
|
||||||
|
|
||||||
assert(Arg);
|
assert(Arg);
|
||||||
|
|
||||||
Optional<DefinedSVal> DefArgVal =
|
Optional<DefinedSVal> DefArgVal =
|
||||||
State->getSVal(Arg, C.getLocationContext()).getAs<DefinedSVal>();
|
State->getSVal(Arg, C.getLocationContext()).getAs<DefinedSVal>();
|
||||||
|
|
||||||
if (!DefArgVal)
|
if (!DefArgVal)
|
||||||
|
@ -881,7 +881,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
|
||||||
DefinedSVal Zero =
|
DefinedSVal Zero =
|
||||||
SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
|
SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
|
||||||
|
|
||||||
std::tie(TrueState, FalseState) =
|
std::tie(TrueState, FalseState) =
|
||||||
State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));
|
State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));
|
||||||
|
|
||||||
if (TrueState && !FalseState) {
|
if (TrueState && !FalseState) {
|
||||||
|
@ -892,7 +892,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
|
||||||
|
|
||||||
const RefState *RS = State->get<RegionState>(Sym);
|
const RefState *RS = State->get<RegionState>(Sym);
|
||||||
if (!RS)
|
if (!RS)
|
||||||
return State; // TODO: change to assert(RS); after realloc() will
|
return State; // TODO: change to assert(RS); after realloc() will
|
||||||
// guarantee have a RegionState attached.
|
// guarantee have a RegionState attached.
|
||||||
|
|
||||||
if (!RS->isAllocated())
|
if (!RS->isAllocated())
|
||||||
|
@ -943,7 +943,7 @@ static bool treatUnusedNewEscaped(const CXXNewExpr *NE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
|
void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
|
|
||||||
if (NE->getNumPlacementArgs())
|
if (NE->getNumPlacementArgs())
|
||||||
|
@ -960,17 +960,17 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ProgramStateRef State = C.getState();
|
ProgramStateRef State = C.getState();
|
||||||
// The return value from operator new is bound to a specified initialization
|
// The return value from operator new is bound to a specified initialization
|
||||||
// value (if any) and we don't want to loose this value. So we call
|
// value (if any) and we don't want to loose this value. So we call
|
||||||
// MallocUpdateRefState() instead of MallocMemAux() which breakes the
|
// MallocUpdateRefState() instead of MallocMemAux() which breakes the
|
||||||
// existing binding.
|
// existing binding.
|
||||||
State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
|
State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
|
||||||
: AF_CXXNew);
|
: AF_CXXNew);
|
||||||
State = ProcessZeroAllocation(C, NE, 0, State);
|
State = ProcessZeroAllocation(C, NE, 0, State);
|
||||||
C.addTransition(State);
|
C.addTransition(State);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
|
void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
|
|
||||||
if (!ChecksEnabled[CK_NewDeleteChecker])
|
if (!ChecksEnabled[CK_NewDeleteChecker])
|
||||||
|
@ -1037,7 +1037,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
|
MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
|
||||||
const OwnershipAttr *Att,
|
const OwnershipAttr *Att,
|
||||||
ProgramStateRef State) const {
|
ProgramStateRef State) const {
|
||||||
if (!State)
|
if (!State)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1104,7 +1104,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
|
||||||
State = State->assume(extentMatchesSize, true);
|
State = State->assume(extentMatchesSize, true);
|
||||||
assert(State);
|
assert(State);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MallocUpdateRefState(C, CE, State, Family);
|
return MallocUpdateRefState(C, CE, State, Family);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1131,7 +1131,7 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
|
||||||
|
|
||||||
ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
|
ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
|
||||||
const CallExpr *CE,
|
const CallExpr *CE,
|
||||||
const OwnershipAttr *Att,
|
const OwnershipAttr *Att,
|
||||||
ProgramStateRef State) const {
|
ProgramStateRef State) const {
|
||||||
if (!State)
|
if (!State)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1183,7 +1183,7 @@ static bool didPreviousFreeFail(ProgramStateRef State,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
|
AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
|
||||||
const Stmt *S) const {
|
const Stmt *S) const {
|
||||||
if (!S)
|
if (!S)
|
||||||
return AF_None;
|
return AF_None;
|
||||||
|
@ -1228,14 +1228,14 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
|
||||||
return AF_None;
|
return AF_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C,
|
bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C,
|
||||||
const Expr *E) const {
|
const Expr *E) const {
|
||||||
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
|
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
|
||||||
// FIXME: This doesn't handle indirect calls.
|
// FIXME: This doesn't handle indirect calls.
|
||||||
const FunctionDecl *FD = CE->getDirectCallee();
|
const FunctionDecl *FD = CE->getDirectCallee();
|
||||||
if (!FD)
|
if (!FD)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
os << *FD;
|
os << *FD;
|
||||||
if (!FD->isOverloadedOperator())
|
if (!FD->isOverloadedOperator())
|
||||||
os << "()";
|
os << "()";
|
||||||
|
@ -1252,14 +1252,14 @@ bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
|
if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
|
||||||
os << "'"
|
os << "'"
|
||||||
<< getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator())
|
<< getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator())
|
||||||
<< "'";
|
<< "'";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) {
|
if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) {
|
||||||
os << "'"
|
os << "'"
|
||||||
<< getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator())
|
<< getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator())
|
||||||
<< "'";
|
<< "'";
|
||||||
return true;
|
return true;
|
||||||
|
@ -1282,7 +1282,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocChecker::printExpectedDeallocName(raw_ostream &os,
|
void MallocChecker::printExpectedDeallocName(raw_ostream &os,
|
||||||
AllocationFamily Family) const {
|
AllocationFamily Family) const {
|
||||||
switch(Family) {
|
switch(Family) {
|
||||||
case AF_Malloc: os << "free()"; return;
|
case AF_Malloc: os << "free()"; return;
|
||||||
|
@ -1326,25 +1326,25 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const MemRegion *R = ArgVal.getAsRegion();
|
const MemRegion *R = ArgVal.getAsRegion();
|
||||||
|
|
||||||
// Nonlocs can't be freed, of course.
|
// Nonlocs can't be freed, of course.
|
||||||
// Non-region locations (labels and fixed addresses) also shouldn't be freed.
|
// Non-region locations (labels and fixed addresses) also shouldn't be freed.
|
||||||
if (!R) {
|
if (!R) {
|
||||||
ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
|
ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
R = R->StripCasts();
|
R = R->StripCasts();
|
||||||
|
|
||||||
// Blocks might show up as heap data, but should not be free()d
|
// Blocks might show up as heap data, but should not be free()d
|
||||||
if (isa<BlockDataRegion>(R)) {
|
if (isa<BlockDataRegion>(R)) {
|
||||||
ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
|
ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemSpaceRegion *MS = R->getMemorySpace();
|
const MemSpaceRegion *MS = R->getMemorySpace();
|
||||||
|
|
||||||
// Parameters, locals, statics, globals, and memory returned by
|
// Parameters, locals, statics, globals, and memory returned by
|
||||||
// __builtin_alloca() shouldn't be freed.
|
// __builtin_alloca() shouldn't be freed.
|
||||||
if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {
|
if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {
|
||||||
// FIXME: at the time this code was written, malloc() regions were
|
// FIXME: at the time this code was written, malloc() regions were
|
||||||
|
@ -1390,7 +1390,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
|
||||||
|
|
||||||
// If the pointer is allocated or escaped, but we are now trying to free it,
|
// If the pointer is allocated or escaped, but we are now trying to free it,
|
||||||
// check that the call to free is proper.
|
// check that the call to free is proper.
|
||||||
} else if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() ||
|
} else if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() ||
|
||||||
RsBase->isEscaped()) {
|
RsBase->isEscaped()) {
|
||||||
|
|
||||||
// Check if an expected deallocation function matches the real one.
|
// Check if an expected deallocation function matches the real one.
|
||||||
|
@ -1409,20 +1409,20 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
|
||||||
!Offset.hasSymbolicOffset() &&
|
!Offset.hasSymbolicOffset() &&
|
||||||
Offset.getOffset() != 0) {
|
Offset.getOffset() != 0) {
|
||||||
const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
|
const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
|
||||||
ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
|
ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
|
||||||
AllocExpr);
|
AllocExpr);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() ||
|
ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() ||
|
||||||
RsBase->isAllocatedOfSizeZero());
|
RsBase->isAllocatedOfSizeZero());
|
||||||
|
|
||||||
// Clean out the info on previous call to free return info.
|
// Clean out the info on previous call to free return info.
|
||||||
State = State->remove<FreeReturnValue>(SymBase);
|
State = State->remove<FreeReturnValue>(SymBase);
|
||||||
|
|
||||||
// Keep track of the return value. If it is NULL, we will know that free
|
// Keep track of the return value. If it is NULL, we will know that free
|
||||||
// failed.
|
// failed.
|
||||||
if (ReturnsNullOnFailure) {
|
if (ReturnsNullOnFailure) {
|
||||||
SVal RetVal = C.getSVal(ParentExpr);
|
SVal RetVal = C.getSVal(ParentExpr);
|
||||||
|
@ -1462,7 +1462,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
|
||||||
if (IsALeakCheck) {
|
if (IsALeakCheck) {
|
||||||
if (ChecksEnabled[CK_NewDeleteLeaksChecker])
|
if (ChecksEnabled[CK_NewDeleteLeaksChecker])
|
||||||
return CK_NewDeleteLeaksChecker;
|
return CK_NewDeleteLeaksChecker;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (ChecksEnabled[CK_NewDeleteChecker])
|
if (ChecksEnabled[CK_NewDeleteChecker])
|
||||||
return CK_NewDeleteChecker;
|
return CK_NewDeleteChecker;
|
||||||
|
@ -1501,7 +1501,7 @@ bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
|
||||||
os << "the address of the label '" << Label->getLabel()->getName() << "'";
|
os << "the address of the label '" << Label->getLabel()->getName() << "'";
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1525,7 +1525,7 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os,
|
||||||
return true;
|
return true;
|
||||||
default: {
|
default: {
|
||||||
const MemSpaceRegion *MS = MR->getMemorySpace();
|
const MemSpaceRegion *MS = MR->getMemorySpace();
|
||||||
|
|
||||||
if (isa<StackLocalsSpaceRegion>(MS)) {
|
if (isa<StackLocalsSpaceRegion>(MS)) {
|
||||||
const VarRegion *VR = dyn_cast<VarRegion>(MR);
|
const VarRegion *VR = dyn_cast<VarRegion>(MR);
|
||||||
const VarDecl *VD;
|
const VarDecl *VD;
|
||||||
|
@ -1579,8 +1579,8 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
|
void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
|
||||||
SourceRange Range,
|
SourceRange Range,
|
||||||
const Expr *DeallocExpr) const {
|
const Expr *DeallocExpr) const {
|
||||||
|
|
||||||
if (!ChecksEnabled[CK_MallocChecker] &&
|
if (!ChecksEnabled[CK_MallocChecker] &&
|
||||||
|
@ -1609,7 +1609,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
|
||||||
os << "deallocator";
|
os << "deallocator";
|
||||||
|
|
||||||
os << " is ";
|
os << " is ";
|
||||||
bool Summarized = MR ? SummarizeRegion(os, MR)
|
bool Summarized = MR ? SummarizeRegion(os, MR)
|
||||||
: SummarizeValue(os, ArgVal);
|
: SummarizeValue(os, ArgVal);
|
||||||
if (Summarized)
|
if (Summarized)
|
||||||
os << ", which is not memory allocated by ";
|
os << ", which is not memory allocated by ";
|
||||||
|
@ -1625,7 +1625,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
|
void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
|
||||||
SourceRange Range) const {
|
SourceRange Range) const {
|
||||||
|
|
||||||
Optional<MallocChecker::CheckKind> CheckKind;
|
Optional<MallocChecker::CheckKind> CheckKind;
|
||||||
|
@ -1651,11 +1651,11 @@ void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
|
void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
|
||||||
SourceRange Range,
|
SourceRange Range,
|
||||||
const Expr *DeallocExpr,
|
const Expr *DeallocExpr,
|
||||||
const RefState *RS,
|
const RefState *RS,
|
||||||
SymbolRef Sym,
|
SymbolRef Sym,
|
||||||
bool OwnershipTransferred) const {
|
bool OwnershipTransferred) const {
|
||||||
|
|
||||||
if (!ChecksEnabled[CK_MismatchedDeallocatorChecker])
|
if (!ChecksEnabled[CK_MismatchedDeallocatorChecker])
|
||||||
|
@ -1679,7 +1679,7 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
|
||||||
if (OwnershipTransferred) {
|
if (OwnershipTransferred) {
|
||||||
if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
|
if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
|
||||||
os << DeallocOs.str() << " cannot";
|
os << DeallocOs.str() << " cannot";
|
||||||
else
|
else
|
||||||
os << "Cannot";
|
os << "Cannot";
|
||||||
|
|
||||||
os << " take ownership of memory";
|
os << " take ownership of memory";
|
||||||
|
@ -1790,7 +1790,7 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
|
void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
|
||||||
bool Released, SymbolRef Sym,
|
bool Released, SymbolRef Sym,
|
||||||
SymbolRef PrevSym) const {
|
SymbolRef PrevSym) const {
|
||||||
|
|
||||||
if (!ChecksEnabled[CK_MallocChecker] &&
|
if (!ChecksEnabled[CK_MallocChecker] &&
|
||||||
|
@ -1920,7 +1920,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
|
||||||
bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull;
|
bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull;
|
||||||
bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero;
|
bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero;
|
||||||
|
|
||||||
// If the ptr is NULL and the size is not 0, the call is equivalent to
|
// If the ptr is NULL and the size is not 0, the call is equivalent to
|
||||||
// malloc(size).
|
// malloc(size).
|
||||||
if ( PrtIsNull && !SizeIsZero) {
|
if ( PrtIsNull && !SizeIsZero) {
|
||||||
ProgramStateRef stateMalloc = MallocMemAux(C, CE, CE->getArg(1),
|
ProgramStateRef stateMalloc = MallocMemAux(C, CE, CE->getArg(1),
|
||||||
|
@ -1978,7 +1978,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
|
ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
|
||||||
ProgramStateRef State) {
|
ProgramStateRef State) {
|
||||||
if (!State)
|
if (!State)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1991,7 +1991,7 @@ ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
|
||||||
SVal count = State->getSVal(CE->getArg(0), LCtx);
|
SVal count = State->getSVal(CE->getArg(0), LCtx);
|
||||||
SVal elementSize = State->getSVal(CE->getArg(1), LCtx);
|
SVal elementSize = State->getSVal(CE->getArg(1), LCtx);
|
||||||
SVal TotalSize = svalBuilder.evalBinOp(State, BO_Mul, count, elementSize,
|
SVal TotalSize = svalBuilder.evalBinOp(State, BO_Mul, count, elementSize,
|
||||||
svalBuilder.getContext().getSizeType());
|
svalBuilder.getContext().getSizeType());
|
||||||
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
|
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
|
||||||
|
|
||||||
return MallocMemAux(C, CE, TotalSize, zeroVal, State);
|
return MallocMemAux(C, CE, TotalSize, zeroVal, State);
|
||||||
|
@ -2078,7 +2078,7 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
|
||||||
const ExplodedNode *AllocNode = nullptr;
|
const ExplodedNode *AllocNode = nullptr;
|
||||||
const MemRegion *Region = nullptr;
|
const MemRegion *Region = nullptr;
|
||||||
std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C);
|
std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C);
|
||||||
|
|
||||||
ProgramPoint P = AllocNode->getLocation();
|
ProgramPoint P = AllocNode->getLocation();
|
||||||
const Stmt *AllocationStmt = nullptr;
|
const Stmt *AllocationStmt = nullptr;
|
||||||
if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>())
|
if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>())
|
||||||
|
@ -2127,7 +2127,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup the Realloc Pairs Map.
|
// Cleanup the Realloc Pairs Map.
|
||||||
ReallocPairsTy RP = state->get<ReallocPairs>();
|
ReallocPairsTy RP = state->get<ReallocPairs>();
|
||||||
for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
|
for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
|
||||||
|
@ -2232,7 +2232,7 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Blocks should be either inlined or should call invalidate regions
|
// TODO: Blocks should be either inlined or should call invalidate regions
|
||||||
// upon invocation. After that's in place, special casing here will not be
|
// upon invocation. After that's in place, special casing here will not be
|
||||||
// needed.
|
// needed.
|
||||||
void MallocChecker::checkPostStmt(const BlockExpr *BE,
|
void MallocChecker::checkPostStmt(const BlockExpr *BE,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
|
@ -2446,7 +2446,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
|
||||||
StringRef FName = II->getName();
|
StringRef FName = II->getName();
|
||||||
|
|
||||||
// White list the 'XXXNoCopy' CoreFoundation functions.
|
// White list the 'XXXNoCopy' CoreFoundation functions.
|
||||||
// We specifically check these before
|
// We specifically check these before
|
||||||
if (FName.endswith("NoCopy")) {
|
if (FName.endswith("NoCopy")) {
|
||||||
// Look for the deallocator argument. We know that the memory ownership
|
// Look for the deallocator argument. We know that the memory ownership
|
||||||
// is not transferred only if the deallocator argument is
|
// is not transferred only if the deallocator argument is
|
||||||
|
@ -2555,7 +2555,7 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
|
||||||
|
|
||||||
if (EscapingSymbol && EscapingSymbol != sym)
|
if (EscapingSymbol && EscapingSymbol != sym)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (const RefState *RS = State->get<RegionState>(sym)) {
|
if (const RefState *RS = State->get<RegionState>(sym)) {
|
||||||
if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) &&
|
if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) &&
|
||||||
CheckRefState(RS)) {
|
CheckRefState(RS)) {
|
||||||
|
@ -2702,7 +2702,7 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
|
||||||
checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
|
checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
|
||||||
checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
|
checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
|
||||||
mgr.getCurrentCheckName();
|
mgr.getCurrentCheckName();
|
||||||
// We currently treat NewDeleteLeaks checker as a subchecker of NewDelete
|
// We currently treat NewDeleteLeaks checker as a subchecker of NewDelete
|
||||||
// checker.
|
// checker.
|
||||||
if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker])
|
if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker])
|
||||||
checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true;
|
checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true;
|
||||||
|
|
|
@ -33,7 +33,7 @@ struct MallocOverflowCheck {
|
||||||
const BinaryOperator *mulop;
|
const BinaryOperator *mulop;
|
||||||
const Expr *variable;
|
const Expr *variable;
|
||||||
|
|
||||||
MallocOverflowCheck (const BinaryOperator *m, const Expr *v)
|
MallocOverflowCheck (const BinaryOperator *m, const Expr *v)
|
||||||
: mulop(m), variable (v)
|
: mulop(m), variable (v)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -143,20 +143,20 @@ static bool typesCompatible(ASTContext &C, QualType A, QualType B) {
|
||||||
while (true) {
|
while (true) {
|
||||||
A = A.getCanonicalType();
|
A = A.getCanonicalType();
|
||||||
B = B.getCanonicalType();
|
B = B.getCanonicalType();
|
||||||
|
|
||||||
if (A.getTypePtr() == B.getTypePtr())
|
if (A.getTypePtr() == B.getTypePtr())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (const PointerType *ptrA = A->getAs<PointerType>())
|
if (const PointerType *ptrA = A->getAs<PointerType>())
|
||||||
if (const PointerType *ptrB = B->getAs<PointerType>()) {
|
if (const PointerType *ptrB = B->getAs<PointerType>()) {
|
||||||
A = ptrA->getPointeeType();
|
A = ptrA->getPointeeType();
|
||||||
B = ptrB->getPointeeType();
|
B = ptrB->getPointeeType();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
// about subpar uses of NSAutoreleasePool. Note that while the check itself
|
// about subpar uses of NSAutoreleasePool. Note that while the check itself
|
||||||
// (in its current form) could be written as a flow-insensitive check, in
|
// (in its current form) could be written as a flow-insensitive check, in
|
||||||
// can be potentially enhanced in the future with flow-sensitive information.
|
// can be potentially enhanced in the future with flow-sensitive information.
|
||||||
// It is also a good example of the CheckerVisitor interface.
|
// It is also a good example of the CheckerVisitor interface.
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
|
||||||
|
|
||||||
const ObjCInterfaceDecl *OD = msg.getReceiverInterface();
|
const ObjCInterfaceDecl *OD = msg.getReceiverInterface();
|
||||||
if (!OD)
|
if (!OD)
|
||||||
return;
|
return;
|
||||||
if (!OD->getIdentifier()->isStr("NSAutoreleasePool"))
|
if (!OD->getIdentifier()->isStr("NSAutoreleasePool"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!II)
|
if (!II)
|
||||||
II = &D->getASTContext().Idents.get("NSError");
|
II = &D->getASTContext().Idents.get("NSError");
|
||||||
|
|
||||||
bool hasNSError = false;
|
bool hasNSError = false;
|
||||||
for (const auto *I : D->params()) {
|
for (const auto *I : D->params()) {
|
||||||
|
@ -105,7 +105,7 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!II)
|
if (!II)
|
||||||
II = &D->getASTContext().Idents.get("CFErrorRef");
|
II = &D->getASTContext().Idents.get("CFErrorRef");
|
||||||
|
|
||||||
bool hasCFError = false;
|
bool hasCFError = false;
|
||||||
for (auto I : D->params()) {
|
for (auto I : D->params()) {
|
||||||
|
|
|
@ -133,7 +133,7 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
|
||||||
if (IdxVal.isUnknownOrUndef())
|
if (IdxVal.isUnknownOrUndef())
|
||||||
return;
|
return;
|
||||||
DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();
|
DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();
|
||||||
|
|
||||||
// Now, check if 'Idx in [0, Size-1]'.
|
// Now, check if 'Idx in [0, Size-1]'.
|
||||||
const QualType T = IdxExpr->getType();
|
const QualType T = IdxExpr->getType();
|
||||||
ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
|
ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
|
||||||
|
|
|
@ -49,7 +49,7 @@ public:
|
||||||
DoesCallSuper = true;
|
DoesCallSuper = true;
|
||||||
|
|
||||||
// Recurse if we didn't find the super call yet.
|
// Recurse if we didn't find the super call yet.
|
||||||
return !DoesCallSuper;
|
return !DoesCallSuper;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DoesCallSuper;
|
bool DoesCallSuper;
|
||||||
|
@ -59,7 +59,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// ObjCSuperCallChecker
|
// ObjCSuperCallChecker
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
class ObjCSuperCallChecker : public Checker<
|
class ObjCSuperCallChecker : public Checker<
|
||||||
|
@ -202,7 +202,7 @@ void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
|
||||||
SmallString<320> Buf;
|
SmallString<320> Buf;
|
||||||
llvm::raw_svector_ostream os(Buf);
|
llvm::raw_svector_ostream os(Buf);
|
||||||
|
|
||||||
os << "The '" << S.getAsString()
|
os << "The '" << S.getAsString()
|
||||||
<< "' instance method in " << SuperclassName.str() << " subclass '"
|
<< "' instance method in " << SuperclassName.str() << " subclass '"
|
||||||
<< *D << "' is missing a [super " << S.getAsString() << "] call";
|
<< *D << "' is missing a [super " << S.getAsString() << "] call";
|
||||||
|
|
||||||
|
|
|
@ -145,13 +145,13 @@ void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C,
|
||||||
const char *errorStr) const {
|
const char *errorStr) const {
|
||||||
if (!E)
|
if (!E)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!C.getState()->get<CalledInit>())
|
if (!C.getState()->get<CalledInit>())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!isInvalidSelf(E, C))
|
if (!isInvalidSelf(E, C))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Generate an error node.
|
// Generate an error node.
|
||||||
ExplodedNode *N = C.generateSink();
|
ExplodedNode *N = C.generateSink();
|
||||||
if (!N)
|
if (!N)
|
||||||
|
@ -177,12 +177,12 @@ void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
|
||||||
if (isInitMessage(Msg)) {
|
if (isInitMessage(Msg)) {
|
||||||
// Tag the return value as the result of an initializer.
|
// Tag the return value as the result of an initializer.
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
|
|
||||||
// FIXME this really should be context sensitive, where we record
|
// FIXME this really should be context sensitive, where we record
|
||||||
// the current stack frame (for IPA). Also, we need to clean this
|
// the current stack frame (for IPA). Also, we need to clean this
|
||||||
// value out when we return from this method.
|
// value out when we return from this method.
|
||||||
state = state->set<CalledInit>(true);
|
state = state->set<CalledInit>(true);
|
||||||
|
|
||||||
SVal V = state->getSVal(Msg.getOriginExpr(), C.getLocationContext());
|
SVal V = state->getSVal(Msg.getOriginExpr(), C.getLocationContext());
|
||||||
addSelfFlag(state, V, SelfFlag_InitRes, C);
|
addSelfFlag(state, V, SelfFlag_InitRes, C);
|
||||||
return;
|
return;
|
||||||
|
@ -318,7 +318,7 @@ void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
// Allow assignment of anything to self. Self is a local variable in the
|
// Allow assignment of anything to self. Self is a local variable in the
|
||||||
// initializer, so it is legal to assign anything to it, like results of
|
// initializer, so it is legal to assign anything to it, like results of
|
||||||
// static functions/method calls. After self is assigned something we cannot
|
// static functions/method calls. After self is assigned something we cannot
|
||||||
// reason about, stop enforcing the rules.
|
// reason about, stop enforcing the rules.
|
||||||
// (Only continue checking if the assigned value should be treated as self.)
|
// (Only continue checking if the assigned value should be treated as self.)
|
||||||
if ((isSelfVar(loc, C)) &&
|
if ((isSelfVar(loc, C)) &&
|
||||||
|
@ -412,7 +412,7 @@ static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) {
|
||||||
|
|
||||||
/// \brief Returns true if the location is 'self'.
|
/// \brief Returns true if the location is 'self'.
|
||||||
static bool isSelfVar(SVal location, CheckerContext &C) {
|
static bool isSelfVar(SVal location, CheckerContext &C) {
|
||||||
AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext();
|
AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext();
|
||||||
if (!analCtx->getSelfDecl())
|
if (!analCtx->getSelfDecl())
|
||||||
return false;
|
return false;
|
||||||
if (!location.getAs<loc::MemRegionVal>())
|
if (!location.getAs<loc::MemRegionVal>())
|
||||||
|
|
|
@ -22,7 +22,7 @@ using namespace clang;
|
||||||
using namespace ento;
|
using namespace ento;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class PointerArithChecker
|
class PointerArithChecker
|
||||||
: public Checker< check::PreStmt<BinaryOperator> > {
|
: public Checker< check::PreStmt<BinaryOperator> > {
|
||||||
mutable std::unique_ptr<BuiltinBug> BT;
|
mutable std::unique_ptr<BuiltinBug> BT;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *B,
|
||||||
|
|
||||||
// If pointer arithmetic is done on variables of non-array type, this often
|
// If pointer arithmetic is done on variables of non-array type, this often
|
||||||
// means behavior rely on memory organization, which is dangerous.
|
// means behavior rely on memory organization, which is dangerous.
|
||||||
if (isa<VarRegion>(LR) || isa<CodeTextRegion>(LR) ||
|
if (isa<VarRegion>(LR) || isa<CodeTextRegion>(LR) ||
|
||||||
isa<CompoundLiteralRegion>(LR)) {
|
isa<CompoundLiteralRegion>(LR)) {
|
||||||
|
|
||||||
if (ExplodedNode *N = C.addTransition()) {
|
if (ExplodedNode *N = C.addTransition()) {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This files defines PointerSubChecker, a builtin checker that checks for
|
// This files defines PointerSubChecker, a builtin checker that checks for
|
||||||
// pointer subtractions on two pointers pointing to different memory chunks.
|
// pointer subtractions on two pointers pointing to different memory chunks.
|
||||||
// This check corresponds to CWE-469.
|
// This check corresponds to CWE-469.
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -23,7 +23,7 @@ using namespace clang;
|
||||||
using namespace ento;
|
using namespace ento;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class PointerSubChecker
|
class PointerSubChecker
|
||||||
: public Checker< check::PreStmt<BinaryOperator> > {
|
: public Checker< check::PreStmt<BinaryOperator> > {
|
||||||
mutable std::unique_ptr<BuiltinBug> BT;
|
mutable std::unique_ptr<BuiltinBug> BT;
|
||||||
|
|
||||||
|
|
|
@ -62,10 +62,10 @@ class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
|
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
|
||||||
|
|
||||||
void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
|
void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
|
||||||
bool isTryLock, enum LockingSemantics semantics) const;
|
bool isTryLock, enum LockingSemantics semantics) const;
|
||||||
|
|
||||||
void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
|
void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
|
||||||
void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
|
void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
|
||||||
void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
|
void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
|
||||||
|
@ -96,7 +96,7 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
|
||||||
false, PthreadSemantics);
|
false, PthreadSemantics);
|
||||||
else if (FName == "lck_mtx_lock" ||
|
else if (FName == "lck_mtx_lock" ||
|
||||||
FName == "lck_rw_lock_exclusive" ||
|
FName == "lck_rw_lock_exclusive" ||
|
||||||
FName == "lck_rw_lock_shared")
|
FName == "lck_rw_lock_shared")
|
||||||
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
|
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
|
||||||
false, XNUSemantics);
|
false, XNUSemantics);
|
||||||
else if (FName == "pthread_mutex_trylock" ||
|
else if (FName == "pthread_mutex_trylock" ||
|
||||||
|
@ -124,17 +124,17 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
|
||||||
void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
|
void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
|
||||||
SVal lock, bool isTryLock,
|
SVal lock, bool isTryLock,
|
||||||
enum LockingSemantics semantics) const {
|
enum LockingSemantics semantics) const {
|
||||||
|
|
||||||
const MemRegion *lockR = lock.getAsRegion();
|
const MemRegion *lockR = lock.getAsRegion();
|
||||||
if (!lockR)
|
if (!lockR)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
|
|
||||||
SVal X = state->getSVal(CE, C.getLocationContext());
|
SVal X = state->getSVal(CE, C.getLocationContext());
|
||||||
if (X.isUnknownOrUndef())
|
if (X.isUnknownOrUndef())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DefinedSVal retVal = X.castAs<DefinedSVal>();
|
DefinedSVal retVal = X.castAs<DefinedSVal>();
|
||||||
|
|
||||||
if (const LockState *LState = state->get<LockMap>(lockR)) {
|
if (const LockState *LState = state->get<LockMap>(lockR)) {
|
||||||
|
@ -183,8 +183,8 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
|
||||||
assert((semantics == XNUSemantics) && "Unknown locking semantics");
|
assert((semantics == XNUSemantics) && "Unknown locking semantics");
|
||||||
lockSucc = state;
|
lockSucc = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record that the lock was acquired.
|
// Record that the lock was acquired.
|
||||||
lockSucc = lockSucc->add<LockSet>(lockR);
|
lockSucc = lockSucc->add<LockSet>(lockR);
|
||||||
lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
|
lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
|
||||||
C.addTransition(lockSucc);
|
C.addTransition(lockSucc);
|
||||||
|
@ -196,7 +196,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
|
||||||
const MemRegion *lockR = lock.getAsRegion();
|
const MemRegion *lockR = lock.getAsRegion();
|
||||||
if (!lockR)
|
if (!lockR)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
|
|
||||||
if (const LockState *LState = state->get<LockMap>(lockR)) {
|
if (const LockState *LState = state->get<LockMap>(lockR)) {
|
||||||
|
|
|
@ -250,7 +250,7 @@ public:
|
||||||
bool operator==(const RefVal& X) const {
|
bool operator==(const RefVal& X) const {
|
||||||
return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind();
|
return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profile(llvm::FoldingSetNodeID& ID) const {
|
void Profile(llvm::FoldingSetNodeID& ID) const {
|
||||||
ID.Add(T);
|
ID.Add(T);
|
||||||
ID.AddInteger(RawKind);
|
ID.AddInteger(RawKind);
|
||||||
|
@ -426,16 +426,16 @@ public:
|
||||||
/// setRetEffect - Set the effect of the return value of the call.
|
/// setRetEffect - Set the effect of the return value of the call.
|
||||||
void setRetEffect(RetEffect E) { Ret = E; }
|
void setRetEffect(RetEffect E) { Ret = E; }
|
||||||
|
|
||||||
|
|
||||||
/// Sets the effect on the receiver of the message.
|
/// Sets the effect on the receiver of the message.
|
||||||
void setReceiverEffect(ArgEffect e) { Receiver = e; }
|
void setReceiverEffect(ArgEffect e) { Receiver = e; }
|
||||||
|
|
||||||
/// getReceiverEffect - Returns the effect on the receiver of the call.
|
/// getReceiverEffect - Returns the effect on the receiver of the call.
|
||||||
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
|
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
|
||||||
ArgEffect getReceiverEffect() const { return Receiver; }
|
ArgEffect getReceiverEffect() const { return Receiver; }
|
||||||
|
|
||||||
/// Test if two retain summaries are identical. Note that merely equivalent
|
/// Test if two retain summaries are identical. Note that merely equivalent
|
||||||
/// summaries are not necessarily identical (for example, if an explicit
|
/// summaries are not necessarily identical (for example, if an explicit
|
||||||
/// argument effect matches the default effect).
|
/// argument effect matches the default effect).
|
||||||
bool operator==(const RetainSummary &Other) const {
|
bool operator==(const RetainSummary &Other) const {
|
||||||
return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect &&
|
return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect &&
|
||||||
|
@ -621,7 +621,7 @@ class RetainSummaryManager {
|
||||||
ArgEffects::Factory AF;
|
ArgEffects::Factory AF;
|
||||||
|
|
||||||
/// ScratchArgs - A holding buffer for construct ArgEffects.
|
/// ScratchArgs - A holding buffer for construct ArgEffects.
|
||||||
ArgEffects ScratchArgs;
|
ArgEffects ScratchArgs;
|
||||||
|
|
||||||
/// ObjCAllocRetE - Default return effect for methods returning Objective-C
|
/// ObjCAllocRetE - Default return effect for methods returning Objective-C
|
||||||
/// objects.
|
/// objects.
|
||||||
|
@ -644,7 +644,7 @@ class RetainSummaryManager {
|
||||||
ArgEffects getArgEffects();
|
ArgEffects getArgEffects();
|
||||||
|
|
||||||
enum UnaryFuncKind { cfretain, cfrelease, cfautorelease, cfmakecollectable };
|
enum UnaryFuncKind { cfretain, cfrelease, cfautorelease, cfmakecollectable };
|
||||||
|
|
||||||
const RetainSummary *getUnarySummary(const FunctionType* FT,
|
const RetainSummary *getUnarySummary(const FunctionType* FT,
|
||||||
UnaryFuncKind func);
|
UnaryFuncKind func);
|
||||||
|
|
||||||
|
@ -664,7 +664,7 @@ class RetainSummaryManager {
|
||||||
const RetainSummary *getDoNothingSummary() {
|
const RetainSummary *getDoNothingSummary() {
|
||||||
return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
||||||
}
|
}
|
||||||
|
|
||||||
const RetainSummary *getDefaultSummary() {
|
const RetainSummary *getDefaultSummary() {
|
||||||
return getPersistentSummary(RetEffect::MakeNoRet(),
|
return getPersistentSummary(RetEffect::MakeNoRet(),
|
||||||
DoNothing, MayEscape);
|
DoNothing, MayEscape);
|
||||||
|
@ -689,7 +689,7 @@ private:
|
||||||
void addClassMethSummary(const char* Cls, const char* name,
|
void addClassMethSummary(const char* Cls, const char* name,
|
||||||
const RetainSummary *Summ, bool isNullary = true) {
|
const RetainSummary *Summ, bool isNullary = true) {
|
||||||
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
|
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
|
||||||
Selector S = isNullary ? GetNullarySelector(name, Ctx)
|
Selector S = isNullary ? GetNullarySelector(name, Ctx)
|
||||||
: GetUnarySelector(name, Ctx);
|
: GetUnarySelector(name, Ctx);
|
||||||
ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
|
||||||
}
|
}
|
||||||
|
@ -739,7 +739,7 @@ public:
|
||||||
? RetEffect::MakeGCNotOwned()
|
? RetEffect::MakeGCNotOwned()
|
||||||
: (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
|
: (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
|
||||||
: RetEffect::MakeOwned(RetEffect::ObjC, true))),
|
: RetEffect::MakeOwned(RetEffect::ObjC, true))),
|
||||||
ObjCInitRetE(gcenabled
|
ObjCInitRetE(gcenabled
|
||||||
? RetEffect::MakeGCNotOwned()
|
? RetEffect::MakeGCNotOwned()
|
||||||
: (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
|
: (usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
|
||||||
: RetEffect::MakeOwnedWhenTrackedReceiver())) {
|
: RetEffect::MakeOwnedWhenTrackedReceiver())) {
|
||||||
|
@ -803,7 +803,7 @@ public:
|
||||||
bool isGCEnabled() const { return GCEnabled; }
|
bool isGCEnabled() const { return GCEnabled; }
|
||||||
|
|
||||||
bool isARCEnabled() const { return ARCEnabled; }
|
bool isARCEnabled() const { return ARCEnabled; }
|
||||||
|
|
||||||
bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
|
bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
|
||||||
|
|
||||||
RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
|
RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
|
||||||
|
@ -966,7 +966,7 @@ void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S,
|
||||||
// Additionally, our Self Init checker already warns about it. To avoid
|
// Additionally, our Self Init checker already warns about it. To avoid
|
||||||
// overwhelming the user with messages from both checkers, we model the case
|
// overwhelming the user with messages from both checkers, we model the case
|
||||||
// of '[super init]' in cases when it is not consumed by another expression
|
// of '[super init]' in cases when it is not consumed by another expression
|
||||||
// as if the call preserves the value of 'self'; essentially, assuming it can
|
// as if the call preserves the value of 'self'; essentially, assuming it can
|
||||||
// never fail and return 'nil'.
|
// never fail and return 'nil'.
|
||||||
// Note, we don't want to just stop tracking the value since we want the
|
// Note, we don't want to just stop tracking the value since we want the
|
||||||
// RetainCount checker to report leaks and use-after-free if SelfInit checker
|
// RetainCount checker to report leaks and use-after-free if SelfInit checker
|
||||||
|
@ -1150,7 +1150,7 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
|
||||||
if (S)
|
if (S)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (RetTy->isPointerType()) {
|
if (RetTy->isPointerType()) {
|
||||||
// For CoreFoundation ('CF') types.
|
// For CoreFoundation ('CF') types.
|
||||||
if (cocoa::isRefType(RetTy, "CF", FName)) {
|
if (cocoa::isRefType(RetTy, "CF", FName)) {
|
||||||
if (isRetain(FD, FName)) {
|
if (isRetain(FD, FName)) {
|
||||||
|
@ -1278,14 +1278,14 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT,
|
||||||
return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
|
||||||
}
|
}
|
||||||
|
|
||||||
const RetainSummary *
|
const RetainSummary *
|
||||||
RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
|
RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
|
||||||
assert (ScratchArgs.isEmpty());
|
assert (ScratchArgs.isEmpty());
|
||||||
|
|
||||||
return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
|
return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
const RetainSummary *
|
const RetainSummary *
|
||||||
RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
|
RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
|
||||||
assert (ScratchArgs.isEmpty());
|
assert (ScratchArgs.isEmpty());
|
||||||
return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF),
|
return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF),
|
||||||
|
@ -1331,7 +1331,7 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
|
||||||
|
|
||||||
// Effects on the parameters.
|
// Effects on the parameters.
|
||||||
unsigned parm_idx = 0;
|
unsigned parm_idx = 0;
|
||||||
for (FunctionDecl::param_const_iterator pi = FD->param_begin(),
|
for (FunctionDecl::param_const_iterator pi = FD->param_begin(),
|
||||||
pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) {
|
pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) {
|
||||||
const ParmVarDecl *pd = *pi;
|
const ParmVarDecl *pd = *pi;
|
||||||
if (pd->hasAttr<NSConsumedAttr>())
|
if (pd->hasAttr<NSConsumedAttr>())
|
||||||
|
@ -1367,8 +1367,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
|
||||||
|
|
||||||
// Effects on the receiver.
|
// Effects on the receiver.
|
||||||
if (MD->hasAttr<NSConsumesSelfAttr>())
|
if (MD->hasAttr<NSConsumesSelfAttr>())
|
||||||
Template->setReceiverEffect(DecRefMsg);
|
Template->setReceiverEffect(DecRefMsg);
|
||||||
|
|
||||||
// Effects on the parameters.
|
// Effects on the parameters.
|
||||||
unsigned parm_idx = 0;
|
unsigned parm_idx = 0;
|
||||||
for (ObjCMethodDecl::param_const_iterator
|
for (ObjCMethodDecl::param_const_iterator
|
||||||
|
@ -1376,9 +1376,9 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
|
||||||
pi != pe; ++pi, ++parm_idx) {
|
pi != pe; ++pi, ++parm_idx) {
|
||||||
const ParmVarDecl *pd = *pi;
|
const ParmVarDecl *pd = *pi;
|
||||||
if (pd->hasAttr<NSConsumedAttr>())
|
if (pd->hasAttr<NSConsumedAttr>())
|
||||||
Template->addArg(AF, parm_idx, DecRefMsg);
|
Template->addArg(AF, parm_idx, DecRefMsg);
|
||||||
else if (pd->hasAttr<CFConsumedAttr>()) {
|
else if (pd->hasAttr<CFConsumedAttr>()) {
|
||||||
Template->addArg(AF, parm_idx, DecRef);
|
Template->addArg(AF, parm_idx, DecRef);
|
||||||
} else if (pd->hasAttr<CFReturnsRetainedAttr>()) {
|
} else if (pd->hasAttr<CFReturnsRetainedAttr>()) {
|
||||||
QualType PointeeTy = pd->getType()->getPointeeType();
|
QualType PointeeTy = pd->getType()->getPointeeType();
|
||||||
if (!PointeeTy.isNull())
|
if (!PointeeTy.isNull())
|
||||||
|
@ -1415,7 +1415,7 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
|
||||||
if (cocoa::isCocoaObjectRef(RetTy))
|
if (cocoa::isCocoaObjectRef(RetTy))
|
||||||
ResultEff = RetEffect::MakeNotOwned(RetEffect::ObjC);
|
ResultEff = RetEffect::MakeNotOwned(RetEffect::ObjC);
|
||||||
else if (coreFoundation::isCFObjectRef(RetTy)) {
|
else if (coreFoundation::isCFObjectRef(RetTy)) {
|
||||||
// ObjCMethodDecl currently doesn't consider CF objects as valid return
|
// ObjCMethodDecl currently doesn't consider CF objects as valid return
|
||||||
// values for alloc, new, copy, or mutableCopy, so we have to
|
// values for alloc, new, copy, or mutableCopy, so we have to
|
||||||
// double-check with the selector. This is ugly, but there aren't that
|
// double-check with the selector. This is ugly, but there aren't that
|
||||||
// many Objective-C methods that return CF objects, right?
|
// many Objective-C methods that return CF objects, right?
|
||||||
|
@ -1428,11 +1428,11 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
|
||||||
ResultEff = RetEffect::MakeOwned(RetEffect::CF, true);
|
ResultEff = RetEffect::MakeOwned(RetEffect::CF, true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ResultEff = RetEffect::MakeNotOwned(RetEffect::CF);
|
ResultEff = RetEffect::MakeNotOwned(RetEffect::CF);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ResultEff = RetEffect::MakeNotOwned(RetEffect::CF);
|
ResultEff = RetEffect::MakeNotOwned(RetEffect::CF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1749,7 +1749,7 @@ namespace {
|
||||||
SymbolRef Sym;
|
SymbolRef Sym;
|
||||||
const SummaryLogTy &SummaryLog;
|
const SummaryLogTy &SummaryLog;
|
||||||
bool GCEnabled;
|
bool GCEnabled;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CFRefReportVisitor(SymbolRef sym, bool gcEnabled, const SummaryLogTy &log)
|
CFRefReportVisitor(SymbolRef sym, bool gcEnabled, const SummaryLogTy &log)
|
||||||
: Sym(sym), SummaryLog(log), GCEnabled(gcEnabled) {}
|
: Sym(sym), SummaryLog(log), GCEnabled(gcEnabled) {}
|
||||||
|
@ -1869,7 +1869,7 @@ void CFRefReport::addGCModeDescription(const LangOptions &LOpts,
|
||||||
|
|
||||||
static bool isNumericLiteralExpression(const Expr *E) {
|
static bool isNumericLiteralExpression(const Expr *E) {
|
||||||
// FIXME: This set of cases was copied from SemaExprObjC.
|
// FIXME: This set of cases was copied from SemaExprObjC.
|
||||||
return isa<IntegerLiteral>(E) ||
|
return isa<IntegerLiteral>(E) ||
|
||||||
isa<CharacterLiteral>(E) ||
|
isa<CharacterLiteral>(E) ||
|
||||||
isa<FloatingLiteral>(E) ||
|
isa<FloatingLiteral>(E) ||
|
||||||
isa<ObjCBoolLiteralExpr>(E) ||
|
isa<ObjCBoolLiteralExpr>(E) ||
|
||||||
|
@ -1948,7 +1948,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
|
||||||
else if (isa<ObjCIvarRefExpr>(S)) {
|
else if (isa<ObjCIvarRefExpr>(S)) {
|
||||||
os << "Object loaded from instance variable";
|
os << "Object loaded from instance variable";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
|
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
|
||||||
// Get the name of the callee (if it is available).
|
// Get the name of the callee (if it is available).
|
||||||
SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
|
SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
|
||||||
|
@ -2228,7 +2228,7 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
|
||||||
|
|
||||||
StoreManager::FindUniqueBinding FB(Sym);
|
StoreManager::FindUniqueBinding FB(Sym);
|
||||||
StateMgr.iterBindings(St, FB);
|
StateMgr.iterBindings(St, FB);
|
||||||
|
|
||||||
if (FB) {
|
if (FB) {
|
||||||
const MemRegion *R = FB.getRegion();
|
const MemRegion *R = FB.getRegion();
|
||||||
const VarRegion *VR = R->getBaseRegion()->getAs<VarRegion>();
|
const VarRegion *VR = R->getBaseRegion()->getAs<VarRegion>();
|
||||||
|
@ -2345,10 +2345,10 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
|
||||||
// objects. Only "copy", "alloc", "retain" and "new" transfer ownership
|
// objects. Only "copy", "alloc", "retain" and "new" transfer ownership
|
||||||
// to the caller for NS objects.
|
// to the caller for NS objects.
|
||||||
const Decl *D = &EndN->getCodeDecl();
|
const Decl *D = &EndN->getCodeDecl();
|
||||||
|
|
||||||
os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
|
os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
|
||||||
: " is returned from a function ");
|
: " is returned from a function ");
|
||||||
|
|
||||||
if (D->hasAttr<CFReturnsNotRetainedAttr>())
|
if (D->hasAttr<CFReturnsNotRetainedAttr>())
|
||||||
os << "that is annotated as CF_RETURNS_NOT_RETAINED";
|
os << "that is annotated as CF_RETURNS_NOT_RETAINED";
|
||||||
else if (D->hasAttr<NSReturnsNotRetainedAttr>())
|
else if (D->hasAttr<NSReturnsNotRetainedAttr>())
|
||||||
|
@ -2385,7 +2385,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
|
||||||
}
|
}
|
||||||
|
|
||||||
CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
|
CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
|
||||||
bool GCEnabled, const SummaryLogTy &Log,
|
bool GCEnabled, const SummaryLogTy &Log,
|
||||||
ExplodedNode *n, SymbolRef sym,
|
ExplodedNode *n, SymbolRef sym,
|
||||||
CheckerContext &Ctx,
|
CheckerContext &Ctx,
|
||||||
bool IncludeAllocationLine)
|
bool IncludeAllocationLine)
|
||||||
|
@ -2492,7 +2492,7 @@ class RetainCountChecker
|
||||||
/// the allocation line.
|
/// the allocation line.
|
||||||
mutable bool IncludeAllocationLine;
|
mutable bool IncludeAllocationLine;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RetainCountChecker(AnalyzerOptions &AO)
|
RetainCountChecker(AnalyzerOptions &AO)
|
||||||
: ShouldResetSummaryLog(false),
|
: ShouldResetSummaryLog(false),
|
||||||
IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {}
|
IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {}
|
||||||
|
@ -2617,7 +2617,7 @@ public:
|
||||||
void checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const;
|
void checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const;
|
||||||
|
|
||||||
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
|
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
|
||||||
|
|
||||||
void checkSummary(const RetainSummary &Summ, const CallEvent &Call,
|
void checkSummary(const RetainSummary &Summ, const CallEvent &Call,
|
||||||
CheckerContext &C) const;
|
CheckerContext &C) const;
|
||||||
|
|
||||||
|
@ -2630,13 +2630,13 @@ public:
|
||||||
ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
|
ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
|
||||||
bool Assumption) const;
|
bool Assumption) const;
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
checkRegionChanges(ProgramStateRef state,
|
checkRegionChanges(ProgramStateRef state,
|
||||||
const InvalidatedSymbols *invalidated,
|
const InvalidatedSymbols *invalidated,
|
||||||
ArrayRef<const MemRegion *> ExplicitRegions,
|
ArrayRef<const MemRegion *> ExplicitRegions,
|
||||||
ArrayRef<const MemRegion *> Regions,
|
ArrayRef<const MemRegion *> Regions,
|
||||||
const CallEvent *Call) const;
|
const CallEvent *Call) const;
|
||||||
|
|
||||||
bool wantsRegionChangeUpdate(ProgramStateRef state) const {
|
bool wantsRegionChangeUpdate(ProgramStateRef state) const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2645,7 +2645,7 @@ public:
|
||||||
void checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C,
|
void checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C,
|
||||||
ExplodedNode *Pred, RetEffect RE, RefVal X,
|
ExplodedNode *Pred, RetEffect RE, RefVal X,
|
||||||
SymbolRef Sym, ProgramStateRef state) const;
|
SymbolRef Sym, ProgramStateRef state) const;
|
||||||
|
|
||||||
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
|
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
|
||||||
void checkEndFunction(CheckerContext &C) const;
|
void checkEndFunction(CheckerContext &C) const;
|
||||||
|
|
||||||
|
@ -2656,7 +2656,7 @@ public:
|
||||||
void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange,
|
void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange,
|
||||||
RefVal::Kind ErrorKind, SymbolRef Sym,
|
RefVal::Kind ErrorKind, SymbolRef Sym,
|
||||||
CheckerContext &C) const;
|
CheckerContext &C) const;
|
||||||
|
|
||||||
void processObjCLiterals(CheckerContext &C, const Expr *Ex) const;
|
void processObjCLiterals(CheckerContext &C, const Expr *Ex) const;
|
||||||
|
|
||||||
const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const;
|
const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const;
|
||||||
|
@ -2740,21 +2740,21 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
|
||||||
const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
|
const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
|
||||||
if (!BE)
|
if (!BE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ArgEffect AE = IncRef;
|
ArgEffect AE = IncRef;
|
||||||
|
|
||||||
switch (BE->getBridgeKind()) {
|
switch (BE->getBridgeKind()) {
|
||||||
case clang::OBC_Bridge:
|
case clang::OBC_Bridge:
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
return;
|
return;
|
||||||
case clang::OBC_BridgeRetained:
|
case clang::OBC_BridgeRetained:
|
||||||
AE = IncRef;
|
AE = IncRef;
|
||||||
break;
|
break;
|
||||||
case clang::OBC_BridgeTransfer:
|
case clang::OBC_BridgeTransfer:
|
||||||
AE = DecRefBridgedTransferred;
|
AE = DecRefBridgedTransferred;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol();
|
SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol();
|
||||||
if (!Sym)
|
if (!Sym)
|
||||||
|
@ -2765,7 +2765,7 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
|
||||||
|
|
||||||
RefVal::Kind hasErr = (RefVal::Kind) 0;
|
RefVal::Kind hasErr = (RefVal::Kind) 0;
|
||||||
state = updateSymbol(state, Sym, *T, AE, hasErr, C);
|
state = updateSymbol(state, Sym, *T, AE, hasErr, C);
|
||||||
|
|
||||||
if (hasErr) {
|
if (hasErr) {
|
||||||
// FIXME: If we get an error during a bridge cast, should we report it?
|
// FIXME: If we get an error during a bridge cast, should we report it?
|
||||||
return;
|
return;
|
||||||
|
@ -2777,7 +2777,7 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
|
||||||
void RetainCountChecker::processObjCLiterals(CheckerContext &C,
|
void RetainCountChecker::processObjCLiterals(CheckerContext &C,
|
||||||
const Expr *Ex) const {
|
const Expr *Ex) const {
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
const ExplodedNode *pred = C.getPredecessor();
|
const ExplodedNode *pred = C.getPredecessor();
|
||||||
for (const Stmt *Child : Ex->children()) {
|
for (const Stmt *Child : Ex->children()) {
|
||||||
SVal V = state->getSVal(Child, pred->getLocationContext());
|
SVal V = state->getSVal(Child, pred->getLocationContext());
|
||||||
if (SymbolRef sym = V.getAsSymbol())
|
if (SymbolRef sym = V.getAsSymbol())
|
||||||
|
@ -2790,17 +2790,17 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the object as autoreleased.
|
// Return the object as autoreleased.
|
||||||
// RetEffect RE = RetEffect::MakeNotOwned(RetEffect::ObjC);
|
// RetEffect RE = RetEffect::MakeNotOwned(RetEffect::ObjC);
|
||||||
if (SymbolRef sym =
|
if (SymbolRef sym =
|
||||||
state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) {
|
state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) {
|
||||||
QualType ResultTy = Ex->getType();
|
QualType ResultTy = Ex->getType();
|
||||||
state = setRefBinding(state, sym,
|
state = setRefBinding(state, sym,
|
||||||
RefVal::makeNotOwned(RetEffect::ObjC, ResultTy));
|
RefVal::makeNotOwned(RetEffect::ObjC, ResultTy));
|
||||||
}
|
}
|
||||||
|
|
||||||
C.addTransition(state);
|
C.addTransition(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL,
|
void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL,
|
||||||
|
@ -2817,7 +2817,7 @@ void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
|
||||||
|
|
||||||
void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
|
void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
const ExplodedNode *Pred = C.getPredecessor();
|
const ExplodedNode *Pred = C.getPredecessor();
|
||||||
const LocationContext *LCtx = Pred->getLocationContext();
|
const LocationContext *LCtx = Pred->getLocationContext();
|
||||||
ProgramStateRef State = Pred->getState();
|
ProgramStateRef State = Pred->getState();
|
||||||
|
|
||||||
|
@ -2966,7 +2966,7 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ,
|
||||||
if (Sym)
|
if (Sym)
|
||||||
state = removeRefBinding(state, Sym);
|
state = removeRefBinding(state, Sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
C.addTransition(state);
|
C.addTransition(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3062,7 +3062,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
|
||||||
|
|
||||||
if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) {
|
if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) {
|
||||||
if (ReceiverIsTracked)
|
if (ReceiverIsTracked)
|
||||||
RE = getSummaryManager(C).getObjAllocRetEffect();
|
RE = getSummaryManager(C).getObjAllocRetEffect();
|
||||||
else
|
else
|
||||||
RE = RetEffect::MakeNoRet();
|
RE = RetEffect::MakeNoRet();
|
||||||
}
|
}
|
||||||
|
@ -3130,7 +3130,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
|
RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
|
||||||
RefVal V, ArgEffect E, RefVal::Kind &hasErr,
|
RefVal V, ArgEffect E, RefVal::Kind &hasErr,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
|
@ -3388,7 +3388,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
|
||||||
isMakeCollectable(FD, FName);
|
isMakeCollectable(FD, FName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canEval)
|
if (!canEval)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -3700,7 +3700,7 @@ ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RetainCountChecker::checkRegionChanges(ProgramStateRef state,
|
RetainCountChecker::checkRegionChanges(ProgramStateRef state,
|
||||||
const InvalidatedSymbols *invalidated,
|
const InvalidatedSymbols *invalidated,
|
||||||
ArrayRef<const MemRegion *> ExplicitRegions,
|
ArrayRef<const MemRegion *> ExplicitRegions,
|
||||||
|
@ -3810,7 +3810,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
|
RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
|
||||||
SymbolRef sid, RefVal V,
|
SymbolRef sid, RefVal V,
|
||||||
SmallVectorImpl<SymbolRef> &Leaked) const {
|
SmallVectorImpl<SymbolRef> &Leaked) const {
|
||||||
|
@ -3890,7 +3890,7 @@ void RetainCountChecker::checkEndFunction(CheckerContext &Ctx) const {
|
||||||
// and suggest annotations.
|
// and suggest annotations.
|
||||||
if (LCtx->getParent())
|
if (LCtx->getParent())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
B = state->get<RefBindings>();
|
B = state->get<RefBindings>();
|
||||||
SmallVector<SymbolRef, 10> Leaked;
|
SmallVector<SymbolRef, 10> Leaked;
|
||||||
|
|
||||||
|
@ -3910,7 +3910,7 @@ RetainCountChecker::getDeadSymbolTag(SymbolRef sym) const {
|
||||||
sym->dumpToStream(out);
|
sym->dumpToStream(out);
|
||||||
tag = new CheckerProgramPointTag(this, out.str());
|
tag = new CheckerProgramPointTag(this, out.str());
|
||||||
}
|
}
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
||||||
|
|
|
@ -23,7 +23,7 @@ using namespace clang;
|
||||||
using namespace ento;
|
using namespace ento;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class ReturnPointerRangeChecker :
|
class ReturnPointerRangeChecker :
|
||||||
public Checker< check::PreStmt<ReturnStmt> > {
|
public Checker< check::PreStmt<ReturnStmt> > {
|
||||||
mutable std::unique_ptr<BuiltinBug> BT;
|
mutable std::unique_ptr<BuiltinBug> BT;
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
|
||||||
const Expr *RetE = RS->getRetValue();
|
const Expr *RetE = RS->getRetValue();
|
||||||
if (!RetE)
|
if (!RetE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SVal V = state->getSVal(RetE, C.getLocationContext());
|
SVal V = state->getSVal(RetE, C.getLocationContext());
|
||||||
const MemRegion *R = V.getAsRegion();
|
const MemRegion *R = V.getAsRegion();
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
|
||||||
|
|
||||||
if (!N)
|
if (!N)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// FIXME: This bug correspond to CWE-466. Eventually we should have bug
|
// FIXME: This bug correspond to CWE-466. Eventually we should have bug
|
||||||
// types explicitly reference such exploit categories (when applicable).
|
// types explicitly reference such exploit categories (when applicable).
|
||||||
if (!BT)
|
if (!BT)
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This file defines stack address leak checker, which checks if an invalid
|
// This file defines stack address leak checker, which checks if an invalid
|
||||||
// stack address is stored into a global or heap location. See CERT DCL30-C.
|
// stack address is stored into a global or heap location. See CERT DCL30-C.
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -49,20 +49,20 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
|
||||||
SourceManager &SM = Ctx.getSourceManager();
|
SourceManager &SM = Ctx.getSourceManager();
|
||||||
SourceRange range;
|
SourceRange range;
|
||||||
os << "Address of ";
|
os << "Address of ";
|
||||||
|
|
||||||
// Check if the region is a compound literal.
|
// Check if the region is a compound literal.
|
||||||
if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) {
|
if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) {
|
||||||
const CompoundLiteralExpr *CL = CR->getLiteralExpr();
|
const CompoundLiteralExpr *CL = CR->getLiteralExpr();
|
||||||
os << "stack memory associated with a compound literal "
|
os << "stack memory associated with a compound literal "
|
||||||
"declared on line "
|
"declared on line "
|
||||||
<< SM.getExpansionLineNumber(CL->getLocStart())
|
<< SM.getExpansionLineNumber(CL->getLocStart())
|
||||||
<< " returned to caller";
|
<< " returned to caller";
|
||||||
range = CL->getSourceRange();
|
range = CL->getSourceRange();
|
||||||
}
|
}
|
||||||
else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) {
|
else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) {
|
||||||
const Expr *ARE = AR->getExpr();
|
const Expr *ARE = AR->getExpr();
|
||||||
SourceLocation L = ARE->getLocStart();
|
SourceLocation L = ARE->getLocStart();
|
||||||
range = ARE->getSourceRange();
|
range = ARE->getSourceRange();
|
||||||
os << "stack memory allocated by call to alloca() on line "
|
os << "stack memory allocated by call to alloca() on line "
|
||||||
<< SM.getExpansionLineNumber(L);
|
<< SM.getExpansionLineNumber(L);
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,8 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
|
llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *
|
||||||
|
|
||||||
void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
|
void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
|
|
||||||
const Expr *RetE = RS->getRetValue();
|
const Expr *RetE = RS->getRetValue();
|
||||||
if (!RetE)
|
if (!RetE)
|
||||||
return;
|
return;
|
||||||
|
@ -130,10 +130,10 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
|
||||||
|
|
||||||
if (!R)
|
if (!R)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const StackSpaceRegion *SS =
|
const StackSpaceRegion *SS =
|
||||||
dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace());
|
dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace());
|
||||||
|
|
||||||
if (!SS)
|
if (!SS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -175,35 +175,35 @@ void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const {
|
||||||
Ctx(CC),
|
Ctx(CC),
|
||||||
CurSFC(CC.getLocationContext()->getCurrentStackFrame())
|
CurSFC(CC.getLocationContext()->getCurrentStackFrame())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool HandleBinding(StoreManager &SMgr, Store store,
|
bool HandleBinding(StoreManager &SMgr, Store store,
|
||||||
const MemRegion *region, SVal val) override {
|
const MemRegion *region, SVal val) override {
|
||||||
|
|
||||||
if (!isa<GlobalsSpaceRegion>(region->getMemorySpace()))
|
if (!isa<GlobalsSpaceRegion>(region->getMemorySpace()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const MemRegion *vR = val.getAsRegion();
|
const MemRegion *vR = val.getAsRegion();
|
||||||
if (!vR)
|
if (!vR)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Under automated retain release, it is okay to assign a block
|
// Under automated retain release, it is okay to assign a block
|
||||||
// directly to a global variable.
|
// directly to a global variable.
|
||||||
if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount &&
|
if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount &&
|
||||||
isa<BlockDataRegion>(vR))
|
isa<BlockDataRegion>(vR))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (const StackSpaceRegion *SSR =
|
if (const StackSpaceRegion *SSR =
|
||||||
dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) {
|
dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) {
|
||||||
// If the global variable holds a location in the current stack frame,
|
// If the global variable holds a location in the current stack frame,
|
||||||
// record the binding to emit a warning.
|
// record the binding to emit a warning.
|
||||||
if (SSR->getStackFrame() == CurSFC)
|
if (SSR->getStackFrame() == CurSFC)
|
||||||
V.push_back(std::make_pair(region, vR));
|
V.push_back(std::make_pair(region, vR));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CallBack cb(Ctx);
|
CallBack cb(Ctx);
|
||||||
state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb);
|
state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb);
|
||||||
|
|
||||||
|
|
|
@ -43,8 +43,8 @@ struct StreamState {
|
||||||
|
|
||||||
static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
|
static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
|
||||||
static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
|
static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
|
||||||
static StreamState getOpenFailed(const Stmt *s) {
|
static StreamState getOpenFailed(const Stmt *s) {
|
||||||
return StreamState(OpenFailed, s);
|
return StreamState(OpenFailed, s);
|
||||||
}
|
}
|
||||||
static StreamState getEscaped(const Stmt *s) {
|
static StreamState getEscaped(const Stmt *s) {
|
||||||
return StreamState(Escaped, s);
|
return StreamState(Escaped, s);
|
||||||
|
@ -59,14 +59,14 @@ struct StreamState {
|
||||||
class StreamChecker : public Checker<eval::Call,
|
class StreamChecker : public Checker<eval::Call,
|
||||||
check::DeadSymbols > {
|
check::DeadSymbols > {
|
||||||
mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
|
mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
|
||||||
*II_fwrite,
|
*II_fwrite,
|
||||||
*II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
|
*II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
|
||||||
*II_clearerr, *II_feof, *II_ferror, *II_fileno;
|
*II_clearerr, *II_feof, *II_ferror, *II_fileno;
|
||||||
mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
|
mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
|
||||||
BT_doubleclose, BT_ResourceLeak;
|
BT_doubleclose, BT_ResourceLeak;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StreamChecker()
|
StreamChecker()
|
||||||
: II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr),
|
: II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr),
|
||||||
II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr),
|
II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr),
|
||||||
II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr),
|
II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr),
|
||||||
|
@ -93,10 +93,10 @@ private:
|
||||||
void Fileno(CheckerContext &C, const CallExpr *CE) const;
|
void Fileno(CheckerContext &C, const CallExpr *CE) const;
|
||||||
|
|
||||||
void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
|
void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
|
||||||
|
|
||||||
ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state,
|
ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state,
|
||||||
CheckerContext &C) const;
|
CheckerContext &C) const;
|
||||||
ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state,
|
ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state,
|
||||||
CheckerContext &C) const;
|
CheckerContext &C) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,13 +216,13 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
|
||||||
C.blockCount())
|
C.blockCount())
|
||||||
.castAs<DefinedSVal>();
|
.castAs<DefinedSVal>();
|
||||||
state = state->BindExpr(CE, C.getLocationContext(), RetVal);
|
state = state->BindExpr(CE, C.getLocationContext(), RetVal);
|
||||||
|
|
||||||
ConstraintManager &CM = C.getConstraintManager();
|
ConstraintManager &CM = C.getConstraintManager();
|
||||||
// Bifurcate the state into two: one with a valid FILE* pointer, the other
|
// Bifurcate the state into two: one with a valid FILE* pointer, the other
|
||||||
// with a NULL.
|
// with a NULL.
|
||||||
ProgramStateRef stateNotNull, stateNull;
|
ProgramStateRef stateNotNull, stateNull;
|
||||||
std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
|
std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
|
||||||
|
|
||||||
if (SymbolRef Sym = RetVal.getAsSymbol()) {
|
if (SymbolRef Sym = RetVal.getAsSymbol()) {
|
||||||
// if RetVal is not NULL, set the symbol's state to Opened.
|
// if RetVal is not NULL, set the symbol's state to Opened.
|
||||||
stateNotNull =
|
stateNotNull =
|
||||||
|
@ -368,13 +368,13 @@ ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
|
||||||
state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol();
|
state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol();
|
||||||
if (!Sym)
|
if (!Sym)
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
const StreamState *SS = state->get<StreamMap>(Sym);
|
const StreamState *SS = state->get<StreamMap>(Sym);
|
||||||
|
|
||||||
// If the file stream is not tracked, return.
|
// If the file stream is not tracked, return.
|
||||||
if (!SS)
|
if (!SS)
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
// Check: Double close a File Descriptor could cause undefined behaviour.
|
// Check: Double close a File Descriptor could cause undefined behaviour.
|
||||||
// Conforming to man-pages
|
// Conforming to man-pages
|
||||||
if (SS->isClosed()) {
|
if (SS->isClosed()) {
|
||||||
|
@ -389,7 +389,7 @@ ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the File Descriptor.
|
// Close the File Descriptor.
|
||||||
return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
|
return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ class UndefBranchChecker : public Checker<check::BranchCondition> {
|
||||||
ProgramStateRef St;
|
ProgramStateRef St;
|
||||||
const LocationContext *LCtx;
|
const LocationContext *LCtx;
|
||||||
|
|
||||||
FindUndefExpr(ProgramStateRef S, const LocationContext *L)
|
FindUndefExpr(ProgramStateRef S, const LocationContext *L)
|
||||||
: St(S), LCtx(L) {}
|
: St(S), LCtx(L) {}
|
||||||
|
|
||||||
const Expr *FindExpr(const Expr *Ex) {
|
const Expr *FindExpr(const Expr *Ex) {
|
||||||
|
@ -45,7 +45,7 @@ class UndefBranchChecker : public Checker<check::BranchCondition> {
|
||||||
return Ex;
|
return Ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MatchesCriteria(const Expr *Ex) {
|
bool MatchesCriteria(const Expr *Ex) {
|
||||||
return St->getSVal(Ex, LCtx).isUndef();
|
return St->getSVal(Ex, LCtx).isUndef();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -83,7 +83,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
|
||||||
SmallString<128> buf;
|
SmallString<128> buf;
|
||||||
llvm::raw_svector_ostream os(buf);
|
llvm::raw_svector_ostream os(buf);
|
||||||
|
|
||||||
os << "Variable '" << VD->getName()
|
os << "Variable '" << VD->getName()
|
||||||
<< "' is uninitialized when captured by block";
|
<< "' is uninitialized when captured by block";
|
||||||
|
|
||||||
auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
|
auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This defines UndefResultChecker, a builtin check in ExprEngine that
|
// This defines UndefResultChecker, a builtin check in ExprEngine that
|
||||||
// performs checks for undefined results of non-assignment binary operators.
|
// performs checks for undefined results of non-assignment binary operators.
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -25,7 +25,7 @@ using namespace clang;
|
||||||
using namespace ento;
|
using namespace ento;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class UndefResultChecker
|
class UndefResultChecker
|
||||||
: public Checker< check::PostStmt<BinaryOperator> > {
|
: public Checker< check::PostStmt<BinaryOperator> > {
|
||||||
|
|
||||||
mutable std::unique_ptr<BugType> BT;
|
mutable std::unique_ptr<BugType> BT;
|
||||||
|
@ -53,7 +53,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
|
||||||
ExplodedNode *N = C.generateSink();
|
ExplodedNode *N = C.generateSink();
|
||||||
if (!N)
|
if (!N)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!BT)
|
if (!BT)
|
||||||
BT.reset(
|
BT.reset(
|
||||||
new BuiltinBug(this, "Result of operation is garbage or undefined"));
|
new BuiltinBug(this, "Result of operation is garbage or undefined"));
|
||||||
|
@ -62,7 +62,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
|
||||||
llvm::raw_svector_ostream OS(sbuf);
|
llvm::raw_svector_ostream OS(sbuf);
|
||||||
const Expr *Ex = nullptr;
|
const Expr *Ex = nullptr;
|
||||||
bool isLeft = true;
|
bool isLeft = true;
|
||||||
|
|
||||||
if (state->getSVal(B->getLHS(), LCtx).isUndef()) {
|
if (state->getSVal(B->getLHS(), LCtx).isUndef()) {
|
||||||
Ex = B->getLHS()->IgnoreParenCasts();
|
Ex = B->getLHS()->IgnoreParenCasts();
|
||||||
isLeft = true;
|
isLeft = true;
|
||||||
|
@ -71,13 +71,13 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
|
||||||
Ex = B->getRHS()->IgnoreParenCasts();
|
Ex = B->getRHS()->IgnoreParenCasts();
|
||||||
isLeft = false;
|
isLeft = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Ex) {
|
if (Ex) {
|
||||||
OS << "The " << (isLeft ? "left" : "right")
|
OS << "The " << (isLeft ? "left" : "right")
|
||||||
<< " operand of '"
|
<< " operand of '"
|
||||||
<< BinaryOperator::getOpcodeStr(B->getOpcode())
|
<< BinaryOperator::getOpcodeStr(B->getOpcode())
|
||||||
<< "' is a garbage value";
|
<< "' is a garbage value";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Neither operand was undefined, but the result is undefined.
|
// Neither operand was undefined, but the result is undefined.
|
||||||
OS << "The result of the '"
|
OS << "The result of the '"
|
||||||
|
@ -91,7 +91,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
bugreporter::trackNullOrUndefValue(N, B, *report);
|
bugreporter::trackNullOrUndefValue(N, B, *report);
|
||||||
|
|
||||||
C.emitReport(std::move(report));
|
C.emitReport(std::move(report));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public:
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
void
|
void
|
||||||
UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
|
UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
const Expr *Index = A->getIdx();
|
const Expr *Index = A->getIdx();
|
||||||
|
|
|
@ -114,7 +114,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
|
||||||
// The definition of O_CREAT is platform specific. We need a better way
|
// The definition of O_CREAT is platform specific. We need a better way
|
||||||
// of querying this information from the checking environment.
|
// of querying this information from the checking environment.
|
||||||
if (!Val_O_CREAT.hasValue()) {
|
if (!Val_O_CREAT.hasValue()) {
|
||||||
if (C.getASTContext().getTargetInfo().getTriple().getVendor()
|
if (C.getASTContext().getTargetInfo().getTriple().getVendor()
|
||||||
== llvm::Triple::Apple)
|
== llvm::Triple::Apple)
|
||||||
Val_O_CREAT = 0x0200;
|
Val_O_CREAT = 0x0200;
|
||||||
else {
|
else {
|
||||||
|
@ -220,7 +220,7 @@ static bool IsZeroByteAllocation(ProgramStateRef state,
|
||||||
ProgramStateRef *falseState) {
|
ProgramStateRef *falseState) {
|
||||||
std::tie(*trueState, *falseState) =
|
std::tie(*trueState, *falseState) =
|
||||||
state->assume(argVal.castAs<DefinedSVal>());
|
state->assume(argVal.castAs<DefinedSVal>());
|
||||||
|
|
||||||
return (*falseState && !*trueState);
|
return (*falseState && !*trueState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
|
||||||
"Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
|
"Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
|
||||||
|
|
||||||
SmallString<256> S;
|
SmallString<256> S;
|
||||||
llvm::raw_svector_ostream os(S);
|
llvm::raw_svector_ostream os(S);
|
||||||
os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
|
os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
|
||||||
auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N);
|
auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N);
|
||||||
|
|
||||||
|
@ -272,13 +272,13 @@ void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
|
||||||
|
|
||||||
// Is the value perfectly constrained to zero?
|
// Is the value perfectly constrained to zero?
|
||||||
if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
|
if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
|
||||||
(void) ReportZeroByteAllocation(C, falseState, arg, fn);
|
(void) ReportZeroByteAllocation(C, falseState, arg, fn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Assume the value is non-zero going forward.
|
// Assume the value is non-zero going forward.
|
||||||
assert(trueState);
|
assert(trueState);
|
||||||
if (trueState != state)
|
if (trueState != state)
|
||||||
C.addTransition(trueState);
|
C.addTransition(trueState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
|
void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
|
||||||
|
|
|
@ -54,7 +54,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
|
||||||
BugReporter &B,
|
BugReporter &B,
|
||||||
ExprEngine &Eng) const {
|
ExprEngine &Eng) const {
|
||||||
CFGBlocksSet reachable, visited;
|
CFGBlocksSet reachable, visited;
|
||||||
|
|
||||||
if (Eng.hasWorkRemaining())
|
if (Eng.hasWorkRemaining())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
|
||||||
// Bail out if we didn't get the CFG or the ParentMap.
|
// Bail out if we didn't get the CFG or the ParentMap.
|
||||||
if (!D || !C || !PM)
|
if (!D || !C || !PM)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Don't do anything for template instantiations. Proving that code
|
// Don't do anything for template instantiations. Proving that code
|
||||||
// in a template instantiation is unreachable means proving that it is
|
// in a template instantiation is unreachable means proving that it is
|
||||||
// unreachable in all instantiations.
|
// unreachable in all instantiations.
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This defines VLASizeChecker, a builtin check in ExprEngine that
|
// This defines VLASizeChecker, a builtin check in ExprEngine that
|
||||||
// performs checks for declaration of VLA of undefined or zero size.
|
// performs checks for declaration of VLA of undefined or zero size.
|
||||||
// In addition, VLASizeChecker is responsible for defining the extent
|
// In addition, VLASizeChecker is responsible for defining the extent
|
||||||
// of the MemRegion that represents a VLA.
|
// of the MemRegion that represents a VLA.
|
||||||
|
@ -82,7 +82,7 @@ void VLASizeChecker::reportBug(VLASize_Kind Kind,
|
||||||
void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
|
void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
|
||||||
if (!DS->isSingleDecl())
|
if (!DS->isSingleDecl())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
|
const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
|
||||||
if (!VD)
|
if (!VD)
|
||||||
return;
|
return;
|
||||||
|
@ -106,7 +106,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
|
||||||
// warned about that already.
|
// warned about that already.
|
||||||
if (sizeV.isUnknown())
|
if (sizeV.isUnknown())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Check if the size is tainted.
|
// Check if the size is tainted.
|
||||||
if (state->isTainted(sizeV)) {
|
if (state->isTainted(sizeV)) {
|
||||||
reportBug(VLA_Tainted, SE, nullptr, C);
|
reportBug(VLA_Tainted, SE, nullptr, C);
|
||||||
|
@ -123,7 +123,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
|
||||||
reportBug(VLA_Zero, SE, stateZero, C);
|
reportBug(VLA_Zero, SE, stateZero, C);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// From this point on, assume that the size is not zero.
|
// From this point on, assume that the size is not zero.
|
||||||
state = stateNotZero;
|
state = stateNotZero;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This file defines a checker that checks virtual function calls during
|
// This file defines a checker that checks virtual function calls during
|
||||||
// construction or destruction of C++ objects.
|
// construction or destruction of C++ objects.
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -37,13 +37,13 @@ class WalkAST : public StmtVisitor<WalkAST> {
|
||||||
|
|
||||||
/// A vector representing the worklist which has a chain of CallExprs.
|
/// A vector representing the worklist which has a chain of CallExprs.
|
||||||
DFSWorkList WList;
|
DFSWorkList WList;
|
||||||
|
|
||||||
// PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
|
// PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
|
||||||
// body has not been visited yet.
|
// body has not been visited yet.
|
||||||
// PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
|
// PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
|
||||||
// body has been visited.
|
// body has been visited.
|
||||||
enum Kind { NotVisited,
|
enum Kind { NotVisited,
|
||||||
PreVisited, /**< A CallExpr to this FunctionDecl is in the
|
PreVisited, /**< A CallExpr to this FunctionDecl is in the
|
||||||
worklist, but the body has not yet been
|
worklist, but the body has not yet been
|
||||||
visited. */
|
visited. */
|
||||||
PostVisited /**< A CallExpr to this FunctionDecl is in the
|
PostVisited /**< A CallExpr to this FunctionDecl is in the
|
||||||
|
@ -57,7 +57,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
|
||||||
/// generating bug reports. This is null while visiting the body of a
|
/// generating bug reports. This is null while visiting the body of a
|
||||||
/// constructor or destructor.
|
/// constructor or destructor.
|
||||||
const CallExpr *visitingCallExpr;
|
const CallExpr *visitingCallExpr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WalkAST(const CheckerBase *checker, BugReporter &br,
|
WalkAST(const CheckerBase *checker, BugReporter &br,
|
||||||
AnalysisDeclContext *ac)
|
AnalysisDeclContext *ac)
|
||||||
|
@ -70,7 +70,7 @@ public:
|
||||||
void Enqueue(WorkListUnit WLUnit) {
|
void Enqueue(WorkListUnit WLUnit) {
|
||||||
const FunctionDecl *FD = WLUnit->getDirectCallee();
|
const FunctionDecl *FD = WLUnit->getDirectCallee();
|
||||||
if (!FD || !FD->getBody())
|
if (!FD || !FD->getBody())
|
||||||
return;
|
return;
|
||||||
Kind &K = VisitedFunctions[FD];
|
Kind &K = VisitedFunctions[FD];
|
||||||
if (K != NotVisited)
|
if (K != NotVisited)
|
||||||
return;
|
return;
|
||||||
|
@ -81,9 +81,9 @@ public:
|
||||||
/// This method returns an item from the worklist without removing it.
|
/// This method returns an item from the worklist without removing it.
|
||||||
WorkListUnit Dequeue() {
|
WorkListUnit Dequeue() {
|
||||||
assert(!WList.empty());
|
assert(!WList.empty());
|
||||||
return WList.back();
|
return WList.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Execute() {
|
void Execute() {
|
||||||
while (hasWork()) {
|
while (hasWork()) {
|
||||||
WorkListUnit WLUnit = Dequeue();
|
WorkListUnit WLUnit = Dequeue();
|
||||||
|
@ -95,7 +95,7 @@ public:
|
||||||
// Visit the body.
|
// Visit the body.
|
||||||
SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
|
SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
|
||||||
Visit(FD->getBody());
|
Visit(FD->getBody());
|
||||||
|
|
||||||
// Mark the function as being PostVisited to indicate we have
|
// Mark the function as being PostVisited to indicate we have
|
||||||
// scanned the body.
|
// scanned the body.
|
||||||
VisitedFunctions[FD] = PostVisited;
|
VisitedFunctions[FD] = PostVisited;
|
||||||
|
@ -114,7 +114,7 @@ public:
|
||||||
void VisitCXXMemberCallExpr(CallExpr *CE);
|
void VisitCXXMemberCallExpr(CallExpr *CE);
|
||||||
void VisitStmt(Stmt *S) { VisitChildren(S); }
|
void VisitStmt(Stmt *S) { VisitChildren(S); }
|
||||||
void VisitChildren(Stmt *S);
|
void VisitChildren(Stmt *S);
|
||||||
|
|
||||||
void ReportVirtualCall(const CallExpr *CE, bool isPure);
|
void ReportVirtualCall(const CallExpr *CE, bool isPure);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -138,7 +138,7 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
|
||||||
void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
|
void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
|
||||||
VisitChildren(CE);
|
VisitChildren(CE);
|
||||||
bool callIsNonVirtual = false;
|
bool callIsNonVirtual = false;
|
||||||
|
|
||||||
// Several situations to elide for checking.
|
// Several situations to elide for checking.
|
||||||
if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
|
if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
|
||||||
// If the member access is fully qualified (i.e., X::F), then treat
|
// If the member access is fully qualified (i.e., X::F), then treat
|
||||||
|
@ -170,7 +170,7 @@ void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
|
||||||
void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
|
void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
|
||||||
SmallString<100> buf;
|
SmallString<100> buf;
|
||||||
llvm::raw_svector_ostream os(buf);
|
llvm::raw_svector_ostream os(buf);
|
||||||
|
|
||||||
os << "Call Path : ";
|
os << "Call Path : ";
|
||||||
// Name of current visiting CallExpr.
|
// Name of current visiting CallExpr.
|
||||||
os << *CE->getDirectCallee();
|
os << *CE->getDirectCallee();
|
||||||
|
@ -190,7 +190,7 @@ void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
|
||||||
PathDiagnosticLocation CELoc =
|
PathDiagnosticLocation CELoc =
|
||||||
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
|
||||||
SourceRange R = CE->getCallee()->getSourceRange();
|
SourceRange R = CE->getCallee()->getSourceRange();
|
||||||
|
|
||||||
if (isPure) {
|
if (isPure) {
|
||||||
os << "\n" << "Call pure virtual functions during construction or "
|
os << "\n" << "Call pure virtual functions during construction or "
|
||||||
<< "destruction may leads undefined behaviour";
|
<< "destruction may leads undefined behaviour";
|
||||||
|
|
|
@ -18,7 +18,7 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
|
||||||
const LangOptions &lang,
|
const LangOptions &lang,
|
||||||
const PathDiagnosticConsumers &PDC,
|
const PathDiagnosticConsumers &PDC,
|
||||||
StoreManagerCreator storemgr,
|
StoreManagerCreator storemgr,
|
||||||
ConstraintManagerCreator constraintmgr,
|
ConstraintManagerCreator constraintmgr,
|
||||||
CheckerManager *checkerMgr,
|
CheckerManager *checkerMgr,
|
||||||
AnalyzerOptions &Options,
|
AnalyzerOptions &Options,
|
||||||
CodeInjector *injector)
|
CodeInjector *injector)
|
||||||
|
|
|
@ -65,7 +65,7 @@ IPAKind AnalyzerOptions::getIPAMode() {
|
||||||
// Set the member variable.
|
// Set the member variable.
|
||||||
IPAMode = IPAConfig;
|
IPAMode = IPAConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
return IPAMode;
|
return IPAMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class CountKey {
|
||||||
unsigned BlockID;
|
unsigned BlockID;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CountKey(const StackFrameContext *CS, unsigned ID)
|
CountKey(const StackFrameContext *CS, unsigned ID)
|
||||||
: CallSite(CS), BlockID(ID) {}
|
: CallSite(CS), BlockID(ID) {}
|
||||||
|
|
||||||
bool operator==(const CountKey &RHS) const {
|
bool operator==(const CountKey &RHS) const {
|
||||||
|
@ -55,7 +55,7 @@ static inline CountMap::Factory& GetFactory(void *F) {
|
||||||
return *static_cast<CountMap::Factory*>(F);
|
return *static_cast<CountMap::Factory*>(F);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned BlockCounter::getNumVisited(const StackFrameContext *CallSite,
|
unsigned BlockCounter::getNumVisited(const StackFrameContext *CallSite,
|
||||||
unsigned BlockID) const {
|
unsigned BlockID) const {
|
||||||
CountMap M = GetMap(Data);
|
CountMap M = GetMap(Data);
|
||||||
CountMap::data_type* T = M.lookup(CountKey(CallSite, BlockID));
|
CountMap::data_type* T = M.lookup(CountKey(CallSite, BlockID));
|
||||||
|
@ -71,10 +71,10 @@ BlockCounter::Factory::~Factory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockCounter
|
BlockCounter
|
||||||
BlockCounter::Factory::IncrementCount(BlockCounter BC,
|
BlockCounter::Factory::IncrementCount(BlockCounter BC,
|
||||||
const StackFrameContext *CallSite,
|
const StackFrameContext *CallSite,
|
||||||
unsigned BlockID) {
|
unsigned BlockID) {
|
||||||
return BlockCounter(GetFactory(F).add(GetMap(BC.Data),
|
return BlockCounter(GetFactory(F).add(GetMap(BC.Data),
|
||||||
CountKey(CallSite, BlockID),
|
CountKey(CallSite, BlockID),
|
||||||
BC.getNumVisited(CallSite, BlockID)+1).getRoot());
|
BC.getNumVisited(CallSite, BlockID)+1).getRoot());
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,13 +81,13 @@ eventsDescribeSameCondition(PathDiagnosticEventPiece *X,
|
||||||
// those that came from TrackConstraintBRVisitor.
|
// those that came from TrackConstraintBRVisitor.
|
||||||
const void *tagPreferred = ConditionBRVisitor::getTag();
|
const void *tagPreferred = ConditionBRVisitor::getTag();
|
||||||
const void *tagLesser = TrackConstraintBRVisitor::getTag();
|
const void *tagLesser = TrackConstraintBRVisitor::getTag();
|
||||||
|
|
||||||
if (X->getLocation() != Y->getLocation())
|
if (X->getLocation() != Y->getLocation())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (X->getTag() == tagPreferred && Y->getTag() == tagLesser)
|
if (X->getTag() == tagPreferred && Y->getTag() == tagLesser)
|
||||||
return X;
|
return X;
|
||||||
|
|
||||||
if (Y->getTag() == tagPreferred && X->getTag() == tagLesser)
|
if (Y->getTag() == tagPreferred && X->getTag() == tagLesser)
|
||||||
return Y;
|
return Y;
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ static void removeRedundantMsgs(PathPieces &path) {
|
||||||
for (unsigned i = 0; i < N; ++i) {
|
for (unsigned i = 0; i < N; ++i) {
|
||||||
IntrusiveRefCntPtr<PathDiagnosticPiece> piece(path.front());
|
IntrusiveRefCntPtr<PathDiagnosticPiece> piece(path.front());
|
||||||
path.pop_front();
|
path.pop_front();
|
||||||
|
|
||||||
switch (piece->getKind()) {
|
switch (piece->getKind()) {
|
||||||
case clang::ento::PathDiagnosticPiece::Call:
|
case clang::ento::PathDiagnosticPiece::Call:
|
||||||
removeRedundantMsgs(cast<PathDiagnosticCallPiece>(piece)->path);
|
removeRedundantMsgs(cast<PathDiagnosticCallPiece>(piece)->path);
|
||||||
|
@ -123,7 +123,7 @@ static void removeRedundantMsgs(PathPieces &path) {
|
||||||
case clang::ento::PathDiagnosticPiece::Event: {
|
case clang::ento::PathDiagnosticPiece::Event: {
|
||||||
if (i == N-1)
|
if (i == N-1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (PathDiagnosticEventPiece *nextEvent =
|
if (PathDiagnosticEventPiece *nextEvent =
|
||||||
dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
|
dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
|
||||||
PathDiagnosticEventPiece *event =
|
PathDiagnosticEventPiece *event =
|
||||||
|
@ -157,13 +157,13 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
|
||||||
LocationContextMap &LCM) {
|
LocationContextMap &LCM) {
|
||||||
bool containsSomethingInteresting = false;
|
bool containsSomethingInteresting = false;
|
||||||
const unsigned N = pieces.size();
|
const unsigned N = pieces.size();
|
||||||
|
|
||||||
for (unsigned i = 0 ; i < N ; ++i) {
|
for (unsigned i = 0 ; i < N ; ++i) {
|
||||||
// Remove the front piece from the path. If it is still something we
|
// Remove the front piece from the path. If it is still something we
|
||||||
// want to keep once we are done, we will push it back on the end.
|
// want to keep once we are done, we will push it back on the end.
|
||||||
IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front());
|
IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front());
|
||||||
pieces.pop_front();
|
pieces.pop_front();
|
||||||
|
|
||||||
switch (piece->getKind()) {
|
switch (piece->getKind()) {
|
||||||
case PathDiagnosticPiece::Call: {
|
case PathDiagnosticPiece::Call: {
|
||||||
PathDiagnosticCallPiece *call = cast<PathDiagnosticCallPiece>(piece);
|
PathDiagnosticCallPiece *call = cast<PathDiagnosticCallPiece>(piece);
|
||||||
|
@ -176,7 +176,7 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
|
||||||
|
|
||||||
if (!removeUnneededCalls(call->path, R, LCM))
|
if (!removeUnneededCalls(call->path, R, LCM))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
containsSomethingInteresting = true;
|
containsSomethingInteresting = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
|
||||||
}
|
}
|
||||||
case PathDiagnosticPiece::Event: {
|
case PathDiagnosticPiece::Event: {
|
||||||
PathDiagnosticEventPiece *event = cast<PathDiagnosticEventPiece>(piece);
|
PathDiagnosticEventPiece *event = cast<PathDiagnosticEventPiece>(piece);
|
||||||
|
|
||||||
// We never throw away an event, but we do throw it away wholesale
|
// We never throw away an event, but we do throw it away wholesale
|
||||||
// as part of a path if we throw the entire path away.
|
// as part of a path if we throw the entire path away.
|
||||||
containsSomethingInteresting |= !event->isPrunable();
|
containsSomethingInteresting |= !event->isPrunable();
|
||||||
|
@ -198,10 +198,10 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
|
||||||
case PathDiagnosticPiece::ControlFlow:
|
case PathDiagnosticPiece::ControlFlow:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pieces.push_back(piece);
|
pieces.push_back(piece);
|
||||||
}
|
}
|
||||||
|
|
||||||
return containsSomethingInteresting;
|
return containsSomethingInteresting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ static bool hasImplicitBody(const Decl *D) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively scan through a path and make sure that all call pieces have
|
/// Recursively scan through a path and make sure that all call pieces have
|
||||||
/// valid locations.
|
/// valid locations.
|
||||||
static void
|
static void
|
||||||
adjustCallLocations(PathPieces &Pieces,
|
adjustCallLocations(PathPieces &Pieces,
|
||||||
PathDiagnosticLocation *LastCallLocation = nullptr) {
|
PathDiagnosticLocation *LastCallLocation = nullptr) {
|
||||||
|
@ -323,7 +323,7 @@ class PathDiagnosticBuilder : public BugReporterContext {
|
||||||
NodeMapClosure NMC;
|
NodeMapClosure NMC;
|
||||||
public:
|
public:
|
||||||
const LocationContext *LC;
|
const LocationContext *LC;
|
||||||
|
|
||||||
PathDiagnosticBuilder(GRBugReporter &br,
|
PathDiagnosticBuilder(GRBugReporter &br,
|
||||||
BugReport *r, InterExplodedGraphMap &Backmap,
|
BugReport *r, InterExplodedGraphMap &Backmap,
|
||||||
PathDiagnosticConsumer *pdc)
|
PathDiagnosticConsumer *pdc)
|
||||||
|
@ -339,7 +339,7 @@ public:
|
||||||
BugReport *getBugReport() { return R; }
|
BugReport *getBugReport() { return R; }
|
||||||
|
|
||||||
Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); }
|
Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); }
|
||||||
|
|
||||||
ParentMap& getParentMap() { return LC->getParentMap(); }
|
ParentMap& getParentMap() { return LC->getParentMap(); }
|
||||||
|
|
||||||
const Stmt *getParent(const Stmt *S) {
|
const Stmt *getParent(const Stmt *S) {
|
||||||
|
@ -957,7 +957,7 @@ static PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L,
|
||||||
|
|
||||||
if (firstCharOnly)
|
if (firstCharOnly)
|
||||||
L = PathDiagnosticLocation::createSingleLocation(L);
|
L = PathDiagnosticLocation::createSingleLocation(L);
|
||||||
|
|
||||||
return L;
|
return L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1001,7 +1001,7 @@ public:
|
||||||
|
|
||||||
~EdgeBuilder() {
|
~EdgeBuilder() {
|
||||||
while (!CLocs.empty()) popLocation();
|
while (!CLocs.empty()) popLocation();
|
||||||
|
|
||||||
// Finally, add an initial edge from the start location of the first
|
// Finally, add an initial edge from the start location of the first
|
||||||
// statement (if it doesn't already exist).
|
// statement (if it doesn't already exist).
|
||||||
PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin(
|
PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin(
|
||||||
|
@ -1016,7 +1016,7 @@ public:
|
||||||
popLocation();
|
popLocation();
|
||||||
PrevLoc = PathDiagnosticLocation();
|
PrevLoc = PathDiagnosticLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false,
|
void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false,
|
||||||
bool IsPostJump = false);
|
bool IsPostJump = false);
|
||||||
|
|
||||||
|
@ -1101,7 +1101,7 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) {
|
||||||
PrevLoc = NewLoc;
|
PrevLoc = NewLoc;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NewLocClean.asLocation() == PrevLocClean.asLocation())
|
if (NewLocClean.asLocation() == PrevLocClean.asLocation())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1242,7 +1242,7 @@ static void reversePropagateIntererstingSymbols(BugReport &R,
|
||||||
SVal V = State->getSVal(Ex, LCtx);
|
SVal V = State->getSVal(Ex, LCtx);
|
||||||
if (!(R.isInteresting(V) || IE.count(Ex)))
|
if (!(R.isInteresting(V) || IE.count(Ex)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (Ex->getStmtClass()) {
|
switch (Ex->getStmtClass()) {
|
||||||
default:
|
default:
|
||||||
if (!isa<CastExpr>(Ex))
|
if (!isa<CastExpr>(Ex))
|
||||||
|
@ -1260,7 +1260,7 @@ static void reversePropagateIntererstingSymbols(BugReport &R,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
R.markInteresting(V);
|
R.markInteresting(V);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1275,7 +1275,7 @@ static void reversePropagateInterestingSymbols(BugReport &R,
|
||||||
const Stmt *CallSite = Callee->getCallSite();
|
const Stmt *CallSite = Callee->getCallSite();
|
||||||
if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) {
|
if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) {
|
||||||
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) {
|
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) {
|
||||||
FunctionDecl::param_const_iterator PI = FD->param_begin(),
|
FunctionDecl::param_const_iterator PI = FD->param_begin(),
|
||||||
PE = FD->param_end();
|
PE = FD->param_end();
|
||||||
CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end();
|
CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end();
|
||||||
for (; AI != AE && PI != PE; ++AI, ++PI) {
|
for (; AI != AE && PI != PE; ++AI, ++PI) {
|
||||||
|
@ -1406,7 +1406,7 @@ static bool GenerateExtensivePathDiagnostic(
|
||||||
N->getState().get(), Ex,
|
N->getState().get(), Ex,
|
||||||
N->getLocationContext());
|
N->getLocationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
|
if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
|
||||||
const Stmt *S = CE->getCalleeContext()->getCallSite();
|
const Stmt *S = CE->getCalleeContext()->getCallSite();
|
||||||
if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) {
|
if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) {
|
||||||
|
@ -1414,7 +1414,7 @@ static bool GenerateExtensivePathDiagnostic(
|
||||||
N->getState().get(), Ex,
|
N->getState().get(), Ex,
|
||||||
N->getLocationContext());
|
N->getLocationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
PathDiagnosticCallPiece *C =
|
PathDiagnosticCallPiece *C =
|
||||||
PathDiagnosticCallPiece::construct(N, *CE, SM);
|
PathDiagnosticCallPiece::construct(N, *CE, SM);
|
||||||
LCM[&C->path] = CE->getCalleeContext();
|
LCM[&C->path] = CE->getCalleeContext();
|
||||||
|
@ -1427,7 +1427,7 @@ static bool GenerateExtensivePathDiagnostic(
|
||||||
CallStack.push_back(StackDiagPair(C, N));
|
CallStack.push_back(StackDiagPair(C, N));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop the call hierarchy if we are done walking the contents
|
// Pop the call hierarchy if we are done walking the contents
|
||||||
// of a function call.
|
// of a function call.
|
||||||
if (Optional<CallEnter> CE = P.getAs<CallEnter>()) {
|
if (Optional<CallEnter> CE = P.getAs<CallEnter>()) {
|
||||||
|
@ -1436,7 +1436,7 @@ static bool GenerateExtensivePathDiagnostic(
|
||||||
PathDiagnosticLocation pos =
|
PathDiagnosticLocation pos =
|
||||||
PathDiagnosticLocation::createBegin(D, SM);
|
PathDiagnosticLocation::createBegin(D, SM);
|
||||||
EB.addEdge(pos);
|
EB.addEdge(pos);
|
||||||
|
|
||||||
// Flush all locations, and pop the active path.
|
// Flush all locations, and pop the active path.
|
||||||
bool VisitedEntireCall = PD.isWithinCall();
|
bool VisitedEntireCall = PD.isWithinCall();
|
||||||
EB.flushLocations();
|
EB.flushLocations();
|
||||||
|
@ -1466,7 +1466,7 @@ static bool GenerateExtensivePathDiagnostic(
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that is important that we update the LocationContext
|
// Note that is important that we update the LocationContext
|
||||||
// after looking at CallExits. CallExit basically adds an
|
// after looking at CallExits. CallExit basically adds an
|
||||||
// edge in the *caller*, so we don't want to update the LocationContext
|
// edge in the *caller*, so we don't want to update the LocationContext
|
||||||
|
@ -1486,7 +1486,7 @@ static bool GenerateExtensivePathDiagnostic(
|
||||||
CalleeCtx, CallerCtx);
|
CalleeCtx, CallerCtx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we jumping to the head of a loop? Add a special diagnostic.
|
// Are we jumping to the head of a loop? Add a special diagnostic.
|
||||||
if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) {
|
if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) {
|
||||||
PathDiagnosticLocation L(Loop, SM, PDB.LC);
|
PathDiagnosticLocation L(Loop, SM, PDB.LC);
|
||||||
|
@ -1552,11 +1552,11 @@ static bool GenerateExtensivePathDiagnostic(
|
||||||
else
|
else
|
||||||
EB.addExtendedContext(PDB.getEnclosingStmtLocation(stmt).asStmt());
|
EB.addExtendedContext(PDB.getEnclosingStmtLocation(stmt).asStmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
if (!NextNode)
|
if (!NextNode)
|
||||||
|
@ -2410,7 +2410,7 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM,
|
||||||
// Trim edges on expressions that are consumed by
|
// Trim edges on expressions that are consumed by
|
||||||
// the parent expression.
|
// the parent expression.
|
||||||
if (isa<Expr>(s1End) && PM.isConsumedExpr(cast<Expr>(s1End))) {
|
if (isa<Expr>(s1End) && PM.isConsumedExpr(cast<Expr>(s1End))) {
|
||||||
removeEdge = true;
|
removeEdge = true;
|
||||||
}
|
}
|
||||||
// Trim edges where a lexical containment doesn't exist.
|
// Trim edges where a lexical containment doesn't exist.
|
||||||
// For example:
|
// For example:
|
||||||
|
@ -2557,7 +2557,7 @@ BugReport::~BugReport() {
|
||||||
const Decl *BugReport::getDeclWithIssue() const {
|
const Decl *BugReport::getDeclWithIssue() const {
|
||||||
if (DeclWithIssue)
|
if (DeclWithIssue)
|
||||||
return DeclWithIssue;
|
return DeclWithIssue;
|
||||||
|
|
||||||
const ExplodedNode *N = getErrorNode();
|
const ExplodedNode *N = getErrorNode();
|
||||||
if (!N)
|
if (!N)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -2973,14 +2973,14 @@ static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) {
|
||||||
|
|
||||||
for (PathPieces::const_iterator I = path.begin(), E = path.end();
|
for (PathPieces::const_iterator I = path.begin(), E = path.end();
|
||||||
I!=E; ++I) {
|
I!=E; ++I) {
|
||||||
|
|
||||||
PathDiagnosticPiece *piece = I->get();
|
PathDiagnosticPiece *piece = I->get();
|
||||||
|
|
||||||
// Recursively compact calls.
|
// Recursively compact calls.
|
||||||
if (PathDiagnosticCallPiece *call=dyn_cast<PathDiagnosticCallPiece>(piece)){
|
if (PathDiagnosticCallPiece *call=dyn_cast<PathDiagnosticCallPiece>(piece)){
|
||||||
CompactPathDiagnostic(call->path, SM);
|
CompactPathDiagnostic(call->path, SM);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the location of the PathDiagnosticPiece.
|
// Get the location of the PathDiagnosticPiece.
|
||||||
const FullSourceLoc Loc = piece->getLocation().asLocation();
|
const FullSourceLoc Loc = piece->getLocation().asLocation();
|
||||||
|
|
||||||
|
@ -3126,7 +3126,7 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD,
|
||||||
PD.resetPath();
|
PD.resetPath();
|
||||||
origReportConfigToken = R->getConfigurationChangeToken();
|
origReportConfigToken = R->getConfigurationChangeToken();
|
||||||
|
|
||||||
// Generate the very last diagnostic piece - the piece is visible before
|
// Generate the very last diagnostic piece - the piece is visible before
|
||||||
// the trace is expanded.
|
// the trace is expanded.
|
||||||
std::unique_ptr<PathDiagnosticPiece> LastPiece;
|
std::unique_ptr<PathDiagnosticPiece> LastPiece;
|
||||||
for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end();
|
for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end();
|
||||||
|
@ -3234,7 +3234,7 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
|
||||||
!DeclCtx->isBodyAutosynthesizedFromModelFile())
|
!DeclCtx->isBodyAutosynthesizedFromModelFile())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidSourceLoc = R->getLocation(getSourceManager()).isValid();
|
bool ValidSourceLoc = R->getLocation(getSourceManager()).isValid();
|
||||||
assert(ValidSourceLoc);
|
assert(ValidSourceLoc);
|
||||||
// If we mess up in a release build, we'd still prefer to just drop the bug
|
// If we mess up in a release build, we'd still prefer to just drop the bug
|
||||||
|
@ -3269,10 +3269,10 @@ namespace {
|
||||||
struct FRIEC_WLItem {
|
struct FRIEC_WLItem {
|
||||||
const ExplodedNode *N;
|
const ExplodedNode *N;
|
||||||
ExplodedNode::const_succ_iterator I, E;
|
ExplodedNode::const_succ_iterator I, E;
|
||||||
|
|
||||||
FRIEC_WLItem(const ExplodedNode *n)
|
FRIEC_WLItem(const ExplodedNode *n)
|
||||||
: N(n), I(N->succ_begin()), E(N->succ_end()) {}
|
: N(n), I(N->succ_begin()), E(N->succ_end()) {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static BugReport *
|
static BugReport *
|
||||||
|
@ -3324,21 +3324,21 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point we know that 'N' is not a sink and it has at least one
|
// At this point we know that 'N' is not a sink and it has at least one
|
||||||
// successor. Use a DFS worklist to find a non-sink end-of-path node.
|
// successor. Use a DFS worklist to find a non-sink end-of-path node.
|
||||||
typedef FRIEC_WLItem WLItem;
|
typedef FRIEC_WLItem WLItem;
|
||||||
typedef SmallVector<WLItem, 10> DFSWorkList;
|
typedef SmallVector<WLItem, 10> DFSWorkList;
|
||||||
llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
|
llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
|
||||||
|
|
||||||
DFSWorkList WL;
|
DFSWorkList WL;
|
||||||
WL.push_back(errorNode);
|
WL.push_back(errorNode);
|
||||||
Visited[errorNode] = 1;
|
Visited[errorNode] = 1;
|
||||||
|
|
||||||
while (!WL.empty()) {
|
while (!WL.empty()) {
|
||||||
WLItem &WI = WL.back();
|
WLItem &WI = WL.back();
|
||||||
assert(!WI.N->succ_empty());
|
assert(!WI.N->succ_empty());
|
||||||
|
|
||||||
for (; WI.I != WI.E; ++WI.I) {
|
for (; WI.I != WI.E; ++WI.I) {
|
||||||
const ExplodedNode *Succ = *WI.I;
|
const ExplodedNode *Succ = *WI.I;
|
||||||
// End-of-path node?
|
// End-of-path node?
|
||||||
if (Succ->succ_empty()) {
|
if (Succ->succ_empty()) {
|
||||||
// If we found an end-of-path node that is not a sink.
|
// If we found an end-of-path node that is not a sink.
|
||||||
|
|
|
@ -169,7 +169,7 @@ public:
|
||||||
bool InEnableNullFPSuppression) {
|
bool InEnableNullFPSuppression) {
|
||||||
if (!CallEvent::isCallStmt(S))
|
if (!CallEvent::isCallStmt(S))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// First, find when we processed the statement.
|
// First, find when we processed the statement.
|
||||||
do {
|
do {
|
||||||
if (Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>())
|
if (Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>())
|
||||||
|
@ -192,11 +192,11 @@ public:
|
||||||
Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>();
|
Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>();
|
||||||
if (!CEE)
|
if (!CEE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const StackFrameContext *CalleeContext = CEE->getCalleeContext();
|
const StackFrameContext *CalleeContext = CEE->getCalleeContext();
|
||||||
if (CalleeContext->getCallSite() != S)
|
if (CalleeContext->getCallSite() != S)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Check the return value.
|
// Check the return value.
|
||||||
ProgramStateRef State = Node->getState();
|
ProgramStateRef State = Node->getState();
|
||||||
SVal RetVal = State->getSVal(S, Node->getLocationContext());
|
SVal RetVal = State->getSVal(S, Node->getLocationContext());
|
||||||
|
@ -281,7 +281,7 @@ public:
|
||||||
EnableNullFPSuppression);
|
EnableNullFPSuppression);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're returning 0, we should track where that 0 came from.
|
// If we're returning 0, we should track where that 0 came from.
|
||||||
bugreporter::trackNullOrUndefValue(N, RetE, BR, /*IsArg*/ false,
|
bugreporter::trackNullOrUndefValue(N, RetE, BR, /*IsArg*/ false,
|
||||||
EnableNullFPSuppression);
|
EnableNullFPSuppression);
|
||||||
|
@ -472,7 +472,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
|
||||||
InitE = PIP->getInitializer()->getInit();
|
InitE = PIP->getInitializer()->getInit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, see if this is the store site:
|
// Otherwise, see if this is the store site:
|
||||||
// (1) Succ has this binding and Pred does not, i.e. this is
|
// (1) Succ has this binding and Pred does not, i.e. this is
|
||||||
// where the binding first occurred.
|
// where the binding first occurred.
|
||||||
|
@ -504,7 +504,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
|
||||||
if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
|
if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
|
||||||
if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
|
if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
|
||||||
const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl());
|
const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl());
|
||||||
|
|
||||||
ProgramStateManager &StateMgr = BRC.getStateManager();
|
ProgramStateManager &StateMgr = BRC.getStateManager();
|
||||||
CallEventManager &CallMgr = StateMgr.getCallEventManager();
|
CallEventManager &CallMgr = StateMgr.getCallEventManager();
|
||||||
|
|
||||||
|
@ -681,7 +681,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
|
||||||
else
|
else
|
||||||
os << "Assigning value";
|
os << "Assigning value";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (R->canPrintPretty()) {
|
if (R->canPrintPretty()) {
|
||||||
os << " to ";
|
os << " to ";
|
||||||
R->printPretty(os);
|
R->printPretty(os);
|
||||||
|
@ -931,7 +931,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
|
||||||
if (!N)
|
if (!N)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef state = N->getState();
|
ProgramStateRef state = N->getState();
|
||||||
|
|
||||||
// The message send could be nil due to the receiver being nil.
|
// The message send could be nil due to the receiver being nil.
|
||||||
|
@ -959,7 +959,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
|
||||||
assert(LVNode && "Unable to find the lvalue node.");
|
assert(LVNode && "Unable to find the lvalue node.");
|
||||||
ProgramStateRef LVState = LVNode->getState();
|
ProgramStateRef LVState = LVNode->getState();
|
||||||
SVal LVal = LVState->getSVal(Inner, LVNode->getLocationContext());
|
SVal LVal = LVState->getSVal(Inner, LVNode->getLocationContext());
|
||||||
|
|
||||||
if (LVState->isNull(LVal).isConstrainedTrue()) {
|
if (LVState->isNull(LVal).isConstrainedTrue()) {
|
||||||
// In case of C++ references, we want to differentiate between a null
|
// In case of C++ references, we want to differentiate between a null
|
||||||
// reference and reference to null pointer.
|
// reference and reference to null pointer.
|
||||||
|
@ -1162,11 +1162,11 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
|
||||||
const ExplodedNode *Prev,
|
const ExplodedNode *Prev,
|
||||||
BugReporterContext &BRC,
|
BugReporterContext &BRC,
|
||||||
BugReport &BR) {
|
BugReport &BR) {
|
||||||
|
|
||||||
ProgramPoint progPoint = N->getLocation();
|
ProgramPoint progPoint = N->getLocation();
|
||||||
ProgramStateRef CurrentState = N->getState();
|
ProgramStateRef CurrentState = N->getState();
|
||||||
ProgramStateRef PrevState = Prev->getState();
|
ProgramStateRef PrevState = Prev->getState();
|
||||||
|
|
||||||
// Compare the GDMs of the state, because that is where constraints
|
// Compare the GDMs of the state, because that is where constraints
|
||||||
// are managed. Note that ensure that we only look at nodes that
|
// are managed. Note that ensure that we only look at nodes that
|
||||||
// were generated by the analyzer engine proper, not checkers.
|
// were generated by the analyzer engine proper, not checkers.
|
||||||
|
@ -1177,16 +1177,16 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
|
||||||
// If an assumption was made on a branch, it should be caught
|
// If an assumption was made on a branch, it should be caught
|
||||||
// here by looking at the state transition.
|
// here by looking at the state transition.
|
||||||
if (Optional<BlockEdge> BE = progPoint.getAs<BlockEdge>()) {
|
if (Optional<BlockEdge> BE = progPoint.getAs<BlockEdge>()) {
|
||||||
const CFGBlock *srcBlk = BE->getSrc();
|
const CFGBlock *srcBlk = BE->getSrc();
|
||||||
if (const Stmt *term = srcBlk->getTerminator())
|
if (const Stmt *term = srcBlk->getTerminator())
|
||||||
return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC);
|
return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) {
|
if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) {
|
||||||
// FIXME: Assuming that BugReporter is a GRBugReporter is a layering
|
// FIXME: Assuming that BugReporter is a GRBugReporter is a layering
|
||||||
// violation.
|
// violation.
|
||||||
const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags =
|
const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags =
|
||||||
cast<GRBugReporter>(BRC.getBugReporter()).
|
cast<GRBugReporter>(BRC.getBugReporter()).
|
||||||
getEngine().geteagerlyAssumeBinOpBifurcationTags();
|
getEngine().geteagerlyAssumeBinOpBifurcationTags();
|
||||||
|
|
||||||
|
@ -1222,7 +1222,7 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term,
|
||||||
case Stmt::ConditionalOperatorClass:
|
case Stmt::ConditionalOperatorClass:
|
||||||
Cond = cast<ConditionalOperator>(Term)->getCond();
|
Cond = cast<ConditionalOperator>(Term)->getCond();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(Cond);
|
assert(Cond);
|
||||||
assert(srcBlk->succ_size() == 2);
|
assert(srcBlk->succ_size() == 2);
|
||||||
|
@ -1236,9 +1236,9 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
|
||||||
BugReporterContext &BRC,
|
BugReporterContext &BRC,
|
||||||
BugReport &R,
|
BugReport &R,
|
||||||
const ExplodedNode *N) {
|
const ExplodedNode *N) {
|
||||||
|
|
||||||
const Expr *Ex = Cond;
|
const Expr *Ex = Cond;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
Ex = Ex->IgnoreParenCasts();
|
Ex = Ex->IgnoreParenCasts();
|
||||||
switch (Ex->getStmtClass()) {
|
switch (Ex->getStmtClass()) {
|
||||||
|
@ -1294,7 +1294,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, raw_ostream &Out,
|
||||||
Out << '\'';
|
Out << '\'';
|
||||||
return quotes;
|
return quotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(Ex)) {
|
if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(Ex)) {
|
||||||
QualType OriginalTy = OriginalExpr->getType();
|
QualType OriginalTy = OriginalExpr->getType();
|
||||||
if (OriginalTy->isPointerType()) {
|
if (OriginalTy->isPointerType()) {
|
||||||
|
@ -1309,11 +1309,11 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, raw_ostream &Out,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Out << IL->getValue();
|
Out << IL->getValue();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1324,10 +1324,10 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
|
||||||
BugReporterContext &BRC,
|
BugReporterContext &BRC,
|
||||||
BugReport &R,
|
BugReport &R,
|
||||||
const ExplodedNode *N) {
|
const ExplodedNode *N) {
|
||||||
|
|
||||||
bool shouldInvert = false;
|
bool shouldInvert = false;
|
||||||
Optional<bool> shouldPrune;
|
Optional<bool> shouldPrune;
|
||||||
|
|
||||||
SmallString<128> LhsString, RhsString;
|
SmallString<128> LhsString, RhsString;
|
||||||
{
|
{
|
||||||
llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString);
|
llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString);
|
||||||
|
@ -1335,10 +1335,10 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
|
||||||
shouldPrune);
|
shouldPrune);
|
||||||
const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC, R, N,
|
const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC, R, N,
|
||||||
shouldPrune);
|
shouldPrune);
|
||||||
|
|
||||||
shouldInvert = !isVarLHS && isVarRHS;
|
shouldInvert = !isVarLHS && isVarRHS;
|
||||||
}
|
}
|
||||||
|
|
||||||
BinaryOperator::Opcode Op = BExpr->getOpcode();
|
BinaryOperator::Opcode Op = BExpr->getOpcode();
|
||||||
|
|
||||||
if (BinaryOperator::isAssignmentOp(Op)) {
|
if (BinaryOperator::isAssignmentOp(Op)) {
|
||||||
|
@ -1380,7 +1380,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (Op) {
|
switch (Op) {
|
||||||
case BO_EQ:
|
case BO_EQ:
|
||||||
Out << "equal to ";
|
Out << "equal to ";
|
||||||
|
@ -1392,7 +1392,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
|
||||||
Out << BinaryOperator::getOpcodeStr(Op) << ' ';
|
Out << BinaryOperator::getOpcodeStr(Op) << ' ';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Out << (shouldInvert ? LhsString : RhsString);
|
Out << (shouldInvert ? LhsString : RhsString);
|
||||||
const LocationContext *LCtx = N->getLocationContext();
|
const LocationContext *LCtx = N->getLocationContext();
|
||||||
PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
|
PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
|
||||||
|
@ -1416,7 +1416,7 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString,
|
||||||
SmallString<256> buf;
|
SmallString<256> buf;
|
||||||
llvm::raw_svector_ostream Out(buf);
|
llvm::raw_svector_ostream Out(buf);
|
||||||
Out << "Assuming " << LhsString << " is ";
|
Out << "Assuming " << LhsString << " is ";
|
||||||
|
|
||||||
QualType Ty = CondVarExpr->getType();
|
QualType Ty = CondVarExpr->getType();
|
||||||
|
|
||||||
if (Ty->isPointerType())
|
if (Ty->isPointerType())
|
||||||
|
@ -1444,10 +1444,10 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
PathDiagnosticPiece *
|
PathDiagnosticPiece *
|
||||||
ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
|
ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
|
||||||
const DeclRefExpr *DR,
|
const DeclRefExpr *DR,
|
||||||
|
@ -1462,11 +1462,11 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
|
||||||
|
|
||||||
SmallString<256> Buf;
|
SmallString<256> Buf;
|
||||||
llvm::raw_svector_ostream Out(Buf);
|
llvm::raw_svector_ostream Out(Buf);
|
||||||
|
|
||||||
Out << "Assuming '" << VD->getDeclName() << "' is ";
|
Out << "Assuming '" << VD->getDeclName() << "' is ";
|
||||||
|
|
||||||
QualType VDTy = VD->getType();
|
QualType VDTy = VD->getType();
|
||||||
|
|
||||||
if (VDTy->isPointerType())
|
if (VDTy->isPointerType())
|
||||||
Out << (tookTrue ? "non-null" : "null");
|
Out << (tookTrue ? "non-null" : "null");
|
||||||
else if (VDTy->isObjCObjectPointerType())
|
else if (VDTy->isObjCObjectPointerType())
|
||||||
|
@ -1480,7 +1480,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
|
||||||
PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
|
PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
|
||||||
PathDiagnosticEventPiece *event =
|
PathDiagnosticEventPiece *event =
|
||||||
new PathDiagnosticEventPiece(Loc, Out.str());
|
new PathDiagnosticEventPiece(Loc, Out.str());
|
||||||
|
|
||||||
const ProgramState *state = N->getState().get();
|
const ProgramState *state = N->getState().get();
|
||||||
if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) {
|
if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) {
|
||||||
if (report.isInteresting(R))
|
if (report.isInteresting(R))
|
||||||
|
@ -1615,13 +1615,13 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N,
|
||||||
// Function can only change the value passed in by address.
|
// Function can only change the value passed in by address.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is a const pointer value, the function does not intend to
|
// If it is a const pointer value, the function does not intend to
|
||||||
// change the value.
|
// change the value.
|
||||||
if (T->getPointeeType().isConstQualified())
|
if (T->getPointeeType().isConstQualified())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Mark the call site (LocationContext) as interesting if the value of the
|
// Mark the call site (LocationContext) as interesting if the value of the
|
||||||
// argument is undefined or '0'/'NULL'.
|
// argument is undefined or '0'/'NULL'.
|
||||||
SVal BoundVal = State->getSVal(R);
|
SVal BoundVal = State->getSVal(R);
|
||||||
if (BoundVal.isUndef() || BoundVal.isZeroConstant()) {
|
if (BoundVal.isUndef() || BoundVal.isZeroConstant()) {
|
||||||
|
|
|
@ -96,7 +96,7 @@ bool CallEvent::hasNonZeroCallbackArg() const {
|
||||||
if (isCallbackArg(getArgSVal(Idx), *I))
|
if (isCallbackArg(getArgSVal(Idx), *I))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
|
||||||
// below for efficiency.
|
// below for efficiency.
|
||||||
if (PreserveArgs.count(Idx))
|
if (PreserveArgs.count(Idx))
|
||||||
if (const MemRegion *MR = getArgSVal(Idx).getAsRegion())
|
if (const MemRegion *MR = getArgSVal(Idx).getAsRegion())
|
||||||
ETraits.setTrait(MR->StripCasts(),
|
ETraits.setTrait(MR->StripCasts(),
|
||||||
RegionAndSymbolInvalidationTraits::TK_PreserveContents);
|
RegionAndSymbolInvalidationTraits::TK_PreserveContents);
|
||||||
// TODO: Factor this out + handle the lower level const pointers.
|
// TODO: Factor this out + handle the lower level const pointers.
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Decl *D = getDecl();
|
const Decl *D = getDecl();
|
||||||
assert(D && "Cannot get a program point without a statement or decl");
|
assert(D && "Cannot get a program point without a statement or decl");
|
||||||
|
|
||||||
SourceLocation Loc = getSourceRange().getBegin();
|
SourceLocation Loc = getSourceRange().getBegin();
|
||||||
if (IsPreVisit)
|
if (IsPreVisit)
|
||||||
|
@ -265,7 +265,7 @@ QualType CallEvent::getDeclaredResultType(const Decl *D) {
|
||||||
|
|
||||||
return QualType();
|
return QualType();
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm_unreachable("unknown callable kind");
|
llvm_unreachable("unknown callable kind");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +336,7 @@ bool AnyFunctionCall::argumentsMayEscape() const {
|
||||||
if (!II)
|
if (!II)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// This set of "escaping" APIs is
|
// This set of "escaping" APIs is
|
||||||
|
|
||||||
// - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
|
// - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
|
||||||
// value into thread local storage. The value can later be retrieved with
|
// value into thread local storage. The value can later be retrieved with
|
||||||
|
@ -455,7 +455,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
|
||||||
// However, we should at least be able to search up and down our own class
|
// However, we should at least be able to search up and down our own class
|
||||||
// hierarchy, and some real bugs have been caught by checking this.
|
// hierarchy, and some real bugs have been caught by checking this.
|
||||||
assert(!RD->isDerivedFrom(MD->getParent()) && "Couldn't find known method");
|
assert(!RD->isDerivedFrom(MD->getParent()) && "Couldn't find known method");
|
||||||
|
|
||||||
// FIXME: This is checking that our DynamicTypeInfo is at least as good as
|
// FIXME: This is checking that our DynamicTypeInfo is at least as good as
|
||||||
// the static type. However, because we currently don't update
|
// the static type. However, because we currently don't update
|
||||||
// DynamicTypeInfo when an object is cast, we can't actually be sure the
|
// DynamicTypeInfo when an object is cast, we can't actually be sure the
|
||||||
|
@ -525,7 +525,7 @@ RuntimeDefinition CXXMemberCall::getRuntimeDefinition() const {
|
||||||
if (const MemberExpr *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee()))
|
if (const MemberExpr *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee()))
|
||||||
if (ME->hasQualifier())
|
if (ME->hasQualifier())
|
||||||
return AnyFunctionCall::getRuntimeDefinition();
|
return AnyFunctionCall::getRuntimeDefinition();
|
||||||
|
|
||||||
return CXXInstanceCall::getRuntimeDefinition();
|
return CXXInstanceCall::getRuntimeDefinition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,7 +628,7 @@ SVal ObjCMethodCall::getReceiverSVal() const {
|
||||||
// FIXME: Is this the best way to handle class receivers?
|
// FIXME: Is this the best way to handle class receivers?
|
||||||
if (!isInstanceMessage())
|
if (!isInstanceMessage())
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
||||||
if (const Expr *RecE = getOriginExpr()->getInstanceReceiver())
|
if (const Expr *RecE = getOriginExpr()->getInstanceReceiver())
|
||||||
return getSVal(RecE);
|
return getSVal(RecE);
|
||||||
|
|
||||||
|
@ -709,7 +709,7 @@ ObjCMessageKind ObjCMethodCall::getMessageKind() const {
|
||||||
return K;
|
return K;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const_cast<ObjCMethodCall *>(this)->Data
|
const_cast<ObjCMethodCall *>(this)->Data
|
||||||
= ObjCMessageDataTy(nullptr, 1).getOpaqueValue();
|
= ObjCMessageDataTy(nullptr, 1).getOpaqueValue();
|
||||||
assert(getMessageKind() == OCM_Message);
|
assert(getMessageKind() == OCM_Message);
|
||||||
|
@ -730,7 +730,7 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
|
||||||
getState()->getStateManager().getContext().getSourceManager();
|
getState()->getStateManager().getContext().getSourceManager();
|
||||||
|
|
||||||
// If the class interface is declared inside the main file, assume it is not
|
// If the class interface is declared inside the main file, assume it is not
|
||||||
// subcassed.
|
// subcassed.
|
||||||
// TODO: It could actually be subclassed if the subclass is private as well.
|
// TODO: It could actually be subclassed if the subclass is private as well.
|
||||||
// This is probably very rare.
|
// This is probably very rare.
|
||||||
SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc();
|
SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc();
|
||||||
|
|
|
@ -23,7 +23,7 @@ StringRef CheckerBase::getTagDescription() const {
|
||||||
|
|
||||||
CheckName CheckerBase::getCheckName() const { return Name; }
|
CheckName CheckerBase::getCheckName() const { return Name; }
|
||||||
|
|
||||||
CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName,
|
CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName,
|
||||||
StringRef Msg)
|
StringRef Msg)
|
||||||
: SimpleProgramPointTag(CheckerName, Msg) {}
|
: SimpleProgramPointTag(CheckerName, Msg) {}
|
||||||
|
|
||||||
|
|
|
@ -357,9 +357,9 @@ void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC,
|
||||||
ExplodedNodeSet &Dst,
|
ExplodedNodeSet &Dst,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
ExprEngine &Eng) {
|
ExprEngine &Eng) {
|
||||||
|
|
||||||
// We define the builder outside of the loop bacause if at least one checkers
|
// We define the builder outside of the loop bacause if at least one checkers
|
||||||
// creates a sucsessor for Pred, we do not need to generate an
|
// creates a sucsessor for Pred, we do not need to generate an
|
||||||
// autotransition for it.
|
// autotransition for it.
|
||||||
NodeBuilder Bldr(Pred, Dst, BC);
|
NodeBuilder Bldr(Pred, Dst, BC);
|
||||||
for (unsigned i = 0, e = EndFunctionCheckers.size(); i != e; ++i) {
|
for (unsigned i = 0, e = EndFunctionCheckers.size(); i != e; ++i) {
|
||||||
|
@ -467,7 +467,7 @@ bool CheckerManager::wantsRegionChangeUpdate(ProgramStateRef state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Run checkers for region changes.
|
/// \brief Run checkers for region changes.
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
|
CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
|
||||||
const InvalidatedSymbols *invalidated,
|
const InvalidatedSymbols *invalidated,
|
||||||
ArrayRef<const MemRegion *> ExplicitRegions,
|
ArrayRef<const MemRegion *> ExplicitRegions,
|
||||||
|
@ -478,7 +478,7 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
|
||||||
// bail out.
|
// bail out.
|
||||||
if (!state)
|
if (!state)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
state = RegionChangesCheckers[i].CheckFn(state, invalidated,
|
state = RegionChangesCheckers[i].CheckFn(state, invalidated,
|
||||||
ExplicitRegions, Regions, Call);
|
ExplicitRegions, Regions, Call);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
|
@ -506,7 +506,7 @@ CheckerManager::runCheckersForPointerEscape(ProgramStateRef State,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Run checkers for handling assumptions on symbolic values.
|
/// \brief Run checkers for handling assumptions on symbolic values.
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
|
CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
|
||||||
SVal Cond, bool Assumption) {
|
SVal Cond, bool Assumption) {
|
||||||
for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) {
|
for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) {
|
||||||
|
@ -558,7 +558,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
|
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
|
||||||
if (!anyEvaluated) {
|
if (!anyEvaluated) {
|
||||||
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
|
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
|
||||||
|
|
|
@ -94,7 +94,7 @@ void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
|
void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
|
||||||
SmallVectorImpl<CheckerOptInfo> &opts) const {
|
SmallVectorImpl<CheckerOptInfo> &opts) const {
|
||||||
// Sort checkers for efficient collection.
|
// Sort checkers for efficient collection.
|
||||||
std::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
|
std::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
|
||||||
|
|
|
@ -26,7 +26,7 @@ static DefinedSVal getLocFromSymbol(const ProgramStateRef &State,
|
||||||
}
|
}
|
||||||
|
|
||||||
ConditionTruthVal ConstraintManager::checkNull(ProgramStateRef State,
|
ConditionTruthVal ConstraintManager::checkNull(ProgramStateRef State,
|
||||||
SymbolRef Sym) {
|
SymbolRef Sym) {
|
||||||
QualType Ty = Sym->getType();
|
QualType Ty = Sym->getType();
|
||||||
DefinedSVal V = Loc::isLocType(Ty) ? getLocFromSymbol(State, Sym)
|
DefinedSVal V = Loc::isLocType(Ty) ? getLocFromSymbol(State, Sym)
|
||||||
: nonloc::SymbolVal(Sym);
|
: nonloc::SymbolVal(Sym);
|
||||||
|
|
|
@ -271,7 +271,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
|
||||||
|
|
||||||
bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
|
bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
|
||||||
unsigned Steps,
|
unsigned Steps,
|
||||||
ProgramStateRef InitState,
|
ProgramStateRef InitState,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
bool DidNotFinish = ExecuteWorkList(L, Steps, InitState);
|
bool DidNotFinish = ExecuteWorkList(L, Steps, InitState);
|
||||||
for (ExplodedGraph::eop_iterator I = G.eop_begin(), E = G.eop_end(); I != E;
|
for (ExplodedGraph::eop_iterator I = G.eop_begin(), E = G.eop_end(); I != E;
|
||||||
|
@ -386,7 +386,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Stmt::DoStmtClass:
|
case Stmt::DoStmtClass:
|
||||||
HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred);
|
HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred);
|
||||||
return;
|
return;
|
||||||
|
@ -456,7 +456,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
|
||||||
Pred->State, Pred);
|
Pred->State, Pred);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
|
void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
|
||||||
const CFGBlock * B, ExplodedNode *Pred) {
|
const CFGBlock * B, ExplodedNode *Pred) {
|
||||||
assert(B->succ_size() == 2);
|
assert(B->succ_size() == 2);
|
||||||
NodeBuilderContext Ctx(*this, B, Pred);
|
NodeBuilderContext Ctx(*this, B, Pred);
|
||||||
|
@ -491,7 +491,7 @@ void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
|
void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
|
||||||
ExplodedNode *Pred) {
|
ExplodedNode *Pred) {
|
||||||
assert(B);
|
assert(B);
|
||||||
assert(!B->empty());
|
assert(!B->empty());
|
||||||
|
|
|
@ -97,9 +97,9 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
|
||||||
const ReturnStmt *RS = cast<ReturnStmt>(S);
|
const ReturnStmt *RS = cast<ReturnStmt>(S);
|
||||||
if (const Expr *RE = RS->getRetValue())
|
if (const Expr *RE = RS->getRetValue())
|
||||||
return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder);
|
return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder);
|
||||||
return UndefinedVal();
|
return UndefinedVal();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle all other Stmt* using a lookup.
|
// Handle all other Stmt* using a lookup.
|
||||||
default:
|
default:
|
||||||
return lookupExpr(EnvironmentEntry(S, LCtx));
|
return lookupExpr(EnvironmentEntry(S, LCtx));
|
||||||
|
@ -194,16 +194,16 @@ void Environment::print(raw_ostream &Out, const char *NL,
|
||||||
|
|
||||||
for (Environment::iterator I = begin(), E = end(); I != E; ++I) {
|
for (Environment::iterator I = begin(), E = end(); I != E; ++I) {
|
||||||
const EnvironmentEntry &En = I.getKey();
|
const EnvironmentEntry &En = I.getKey();
|
||||||
|
|
||||||
if (isFirst) {
|
if (isFirst) {
|
||||||
Out << NL << NL
|
Out << NL << NL
|
||||||
<< "Expressions:"
|
<< "Expressions:"
|
||||||
<< NL;
|
<< NL;
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
} else {
|
} else {
|
||||||
Out << NL;
|
Out << NL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Stmt *S = En.getStmt();
|
const Stmt *S = En.getStmt();
|
||||||
assert(S != nullptr && "Expected non-null Stmt");
|
assert(S != nullptr && "Expected non-null Stmt");
|
||||||
|
|
||||||
|
|
|
@ -90,8 +90,8 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
|
||||||
// (7) The LocationContext is the same as the predecessor.
|
// (7) The LocationContext is the same as the predecessor.
|
||||||
// (8) Expressions that are *not* lvalue expressions.
|
// (8) Expressions that are *not* lvalue expressions.
|
||||||
// (9) The PostStmt isn't for a non-consumed Stmt or Expr.
|
// (9) The PostStmt isn't for a non-consumed Stmt or Expr.
|
||||||
// (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or
|
// (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or
|
||||||
// PreImplicitCall (so that we would be able to find it when retrying a
|
// PreImplicitCall (so that we would be able to find it when retrying a
|
||||||
// call with no inlining).
|
// call with no inlining).
|
||||||
// FIXME: It may be safe to reclaim PreCall and PostCall nodes as well.
|
// FIXME: It may be safe to reclaim PreCall and PostCall nodes as well.
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
|
||||||
const ExplodedNode *pred = *(node->pred_begin());
|
const ExplodedNode *pred = *(node->pred_begin());
|
||||||
if (pred->succ_size() != 1)
|
if (pred->succ_size() != 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const ExplodedNode *succ = *(node->succ_begin());
|
const ExplodedNode *succ = *(node->succ_begin());
|
||||||
if (succ->pred_size() != 1)
|
if (succ->pred_size() != 1)
|
||||||
return false;
|
return false;
|
||||||
|
@ -123,7 +123,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
|
||||||
|
|
||||||
// Conditions 5, 6, and 7.
|
// Conditions 5, 6, and 7.
|
||||||
ProgramStateRef state = node->getState();
|
ProgramStateRef state = node->getState();
|
||||||
ProgramStateRef pred_state = pred->getState();
|
ProgramStateRef pred_state = pred->getState();
|
||||||
if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
|
if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
|
||||||
progPoint.getLocationContext() != pred->getLocationContext())
|
progPoint.getLocationContext() != pred->getLocationContext())
|
||||||
return false;
|
return false;
|
||||||
|
@ -174,7 +174,7 @@ void ExplodedGraph::collectNode(ExplodedNode *node) {
|
||||||
FreeNodes.push_back(node);
|
FreeNodes.push_back(node);
|
||||||
Nodes.RemoveNode(node);
|
Nodes.RemoveNode(node);
|
||||||
--NumNodes;
|
--NumNodes;
|
||||||
node->~ExplodedNode();
|
node->~ExplodedNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
|
void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
|
||||||
|
|
|
@ -174,7 +174,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) {
|
||||||
return getCheckerManager().wantsRegionChangeUpdate(state);
|
return getCheckerManager().wantsRegionChangeUpdate(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
ExprEngine::processRegionChanges(ProgramStateRef state,
|
ExprEngine::processRegionChanges(ProgramStateRef state,
|
||||||
const InvalidatedSymbols *invalidated,
|
const InvalidatedSymbols *invalidated,
|
||||||
ArrayRef<const MemRegion *> Explicits,
|
ArrayRef<const MemRegion *> Explicits,
|
||||||
|
@ -315,7 +315,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
|
||||||
const CFGStmt S,
|
const CFGStmt S,
|
||||||
const ExplodedNode *Pred,
|
const ExplodedNode *Pred,
|
||||||
const LocationContext *LC) {
|
const LocationContext *LC) {
|
||||||
|
|
||||||
// Are we never purging state values?
|
// Are we never purging state values?
|
||||||
if (AMgr.options.AnalysisPurgeOpt == PurgeNone)
|
if (AMgr.options.AnalysisPurgeOpt == PurgeNone)
|
||||||
return false;
|
return false;
|
||||||
|
@ -327,7 +327,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
|
||||||
// Is this on a non-expression?
|
// Is this on a non-expression?
|
||||||
if (!isa<Expr>(S.getStmt()))
|
if (!isa<Expr>(S.getStmt()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Run before processing a call.
|
// Run before processing a call.
|
||||||
if (CallEvent::isCallStmt(S.getStmt()))
|
if (CallEvent::isCallStmt(S.getStmt()))
|
||||||
return true;
|
return true;
|
||||||
|
@ -512,7 +512,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init,
|
||||||
assert(Tmp.size() == 1 && "have not generated any new nodes yet");
|
assert(Tmp.size() == 1 && "have not generated any new nodes yet");
|
||||||
assert(*Tmp.begin() == Pred && "have not generated any new nodes yet");
|
assert(*Tmp.begin() == Pred && "have not generated any new nodes yet");
|
||||||
Tmp.clear();
|
Tmp.clear();
|
||||||
|
|
||||||
PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame);
|
PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame);
|
||||||
evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP);
|
evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP);
|
||||||
}
|
}
|
||||||
|
@ -775,7 +775,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
Engine.addAbortedBlock(node, currBldrCtx->getBlock());
|
Engine.addAbortedBlock(node, currBldrCtx->getBlock());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Stmt::ParenExprClass:
|
case Stmt::ParenExprClass:
|
||||||
llvm_unreachable("ParenExprs already handled.");
|
llvm_unreachable("ParenExprs already handled.");
|
||||||
case Stmt::GenericSelectionExprClass:
|
case Stmt::GenericSelectionExprClass:
|
||||||
|
@ -966,7 +966,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
|
|
||||||
ExplodedNodeSet preVisit;
|
ExplodedNodeSet preVisit;
|
||||||
getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
|
getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
|
||||||
|
|
||||||
ExplodedNodeSet Tmp;
|
ExplodedNodeSet Tmp;
|
||||||
StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx);
|
StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx);
|
||||||
|
|
||||||
|
@ -974,7 +974,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
QualType resultType = Ex->getType();
|
QualType resultType = Ex->getType();
|
||||||
|
|
||||||
for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end();
|
for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end();
|
||||||
it != et; ++it) {
|
it != et; ++it) {
|
||||||
ExplodedNode *N = *it;
|
ExplodedNode *N = *it;
|
||||||
const LocationContext *LCtx = N->getLocationContext();
|
const LocationContext *LCtx = N->getLocationContext();
|
||||||
SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx,
|
SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx,
|
||||||
|
@ -983,10 +983,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
ProgramStateRef state = N->getState()->BindExpr(Ex, LCtx, result);
|
ProgramStateRef state = N->getState()->BindExpr(Ex, LCtx, result);
|
||||||
Bldr2.generateNode(S, N, state);
|
Bldr2.generateNode(S, N, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
|
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
|
||||||
Bldr.addNodes(Dst);
|
Bldr.addNodes(Dst);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Stmt::ArraySubscriptExprClass:
|
case Stmt::ArraySubscriptExprClass:
|
||||||
|
@ -1031,7 +1031,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
}
|
}
|
||||||
|
|
||||||
Bldr.takeNodes(Pred);
|
Bldr.takeNodes(Pred);
|
||||||
|
|
||||||
if (AMgr.options.eagerlyAssumeBinOpBifurcation &&
|
if (AMgr.options.eagerlyAssumeBinOpBifurcation &&
|
||||||
(B->isRelationalOp() || B->isEqualityOp())) {
|
(B->isRelationalOp() || B->isEqualityOp())) {
|
||||||
ExplodedNodeSet Tmp;
|
ExplodedNodeSet Tmp;
|
||||||
|
@ -1076,7 +1076,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
Bldr.addNodes(Dst);
|
Bldr.addNodes(Dst);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Stmt::CXXCatchStmtClass: {
|
case Stmt::CXXCatchStmtClass: {
|
||||||
Bldr.takeNodes(Pred);
|
Bldr.takeNodes(Pred);
|
||||||
VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst);
|
VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst);
|
||||||
|
@ -1085,7 +1085,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
}
|
}
|
||||||
|
|
||||||
case Stmt::CXXTemporaryObjectExprClass:
|
case Stmt::CXXTemporaryObjectExprClass:
|
||||||
case Stmt::CXXConstructExprClass: {
|
case Stmt::CXXConstructExprClass: {
|
||||||
Bldr.takeNodes(Pred);
|
Bldr.takeNodes(Pred);
|
||||||
VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst);
|
VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst);
|
||||||
Bldr.addNodes(Dst);
|
Bldr.addNodes(Dst);
|
||||||
|
@ -1107,7 +1107,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S);
|
const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S);
|
||||||
getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
|
getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
|
||||||
|
|
||||||
for (ExplodedNodeSet::iterator i = PreVisit.begin(),
|
for (ExplodedNodeSet::iterator i = PreVisit.begin(),
|
||||||
e = PreVisit.end(); i != e ; ++i)
|
e = PreVisit.end(); i != e ; ++i)
|
||||||
VisitCXXDeleteExpr(CDE, *i, Dst);
|
VisitCXXDeleteExpr(CDE, *i, Dst);
|
||||||
|
|
||||||
|
@ -1173,18 +1173,18 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
case Stmt::CXXDynamicCastExprClass:
|
case Stmt::CXXDynamicCastExprClass:
|
||||||
case Stmt::CXXReinterpretCastExprClass:
|
case Stmt::CXXReinterpretCastExprClass:
|
||||||
case Stmt::CXXConstCastExprClass:
|
case Stmt::CXXConstCastExprClass:
|
||||||
case Stmt::CXXFunctionalCastExprClass:
|
case Stmt::CXXFunctionalCastExprClass:
|
||||||
case Stmt::ObjCBridgedCastExprClass: {
|
case Stmt::ObjCBridgedCastExprClass: {
|
||||||
Bldr.takeNodes(Pred);
|
Bldr.takeNodes(Pred);
|
||||||
const CastExpr *C = cast<CastExpr>(S);
|
const CastExpr *C = cast<CastExpr>(S);
|
||||||
// Handle the previsit checks.
|
// Handle the previsit checks.
|
||||||
ExplodedNodeSet dstPrevisit;
|
ExplodedNodeSet dstPrevisit;
|
||||||
getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this);
|
getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this);
|
||||||
|
|
||||||
// Handle the expression itself.
|
// Handle the expression itself.
|
||||||
ExplodedNodeSet dstExpr;
|
ExplodedNodeSet dstExpr;
|
||||||
for (ExplodedNodeSet::iterator i = dstPrevisit.begin(),
|
for (ExplodedNodeSet::iterator i = dstPrevisit.begin(),
|
||||||
e = dstPrevisit.end(); i != e ; ++i) {
|
e = dstPrevisit.end(); i != e ; ++i) {
|
||||||
VisitCast(C, C->getSubExpr(), *i, dstExpr);
|
VisitCast(C, C->getSubExpr(), *i, dstExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1201,7 +1201,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
Bldr.addNodes(Dst);
|
Bldr.addNodes(Dst);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Stmt::InitListExprClass:
|
case Stmt::InitListExprClass:
|
||||||
Bldr.takeNodes(Pred);
|
Bldr.takeNodes(Pred);
|
||||||
VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst);
|
VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst);
|
||||||
|
@ -1296,7 +1296,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
Bldr.takeNodes(Pred);
|
Bldr.takeNodes(Pred);
|
||||||
ProgramStateRef state = Pred->getState();
|
ProgramStateRef state = Pred->getState();
|
||||||
const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S);
|
const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S);
|
||||||
if (const Expr *Result = PE->getResultExpr()) {
|
if (const Expr *Result = PE->getResultExpr()) {
|
||||||
SVal V = state->getSVal(Result, Pred->getLocationContext());
|
SVal V = state->getSVal(Result, Pred->getLocationContext());
|
||||||
Bldr.generateNode(S, Pred,
|
Bldr.generateNode(S, Pred,
|
||||||
state->BindExpr(S, Pred->getLocationContext(), V));
|
state->BindExpr(S, Pred->getLocationContext(), V));
|
||||||
|
@ -1377,7 +1377,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
|
||||||
|
|
||||||
/// Block entrance. (Update counters).
|
/// Block entrance. (Update counters).
|
||||||
void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
|
void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
|
||||||
NodeBuilderWithSinks &nodeBuilder,
|
NodeBuilderWithSinks &nodeBuilder,
|
||||||
ExplodedNode *Pred) {
|
ExplodedNode *Pred) {
|
||||||
PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
|
PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
|
||||||
|
|
||||||
|
@ -1585,7 +1585,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the condition is still unknown, give up.
|
// If the condition is still unknown, give up.
|
||||||
if (X.isUnknownOrUndef()) {
|
if (X.isUnknownOrUndef()) {
|
||||||
builder.generateNode(PrevState, true, PredI);
|
builder.generateNode(PrevState, true, PredI);
|
||||||
|
@ -1752,7 +1752,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
|
||||||
DefinedOrUnknownSVal CondV = CondV_untested.castAs<DefinedOrUnknownSVal>();
|
DefinedOrUnknownSVal CondV = CondV_untested.castAs<DefinedOrUnknownSVal>();
|
||||||
|
|
||||||
ProgramStateRef DefaultSt = state;
|
ProgramStateRef DefaultSt = state;
|
||||||
|
|
||||||
iterator I = builder.begin(), EI = builder.end();
|
iterator I = builder.begin(), EI = builder.end();
|
||||||
bool defaultIsFeasible = I == EI;
|
bool defaultIsFeasible = I == EI;
|
||||||
|
|
||||||
|
@ -1760,7 +1760,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
|
||||||
// Successor may be pruned out during CFG construction.
|
// Successor may be pruned out during CFG construction.
|
||||||
if (!I.getBlock())
|
if (!I.getBlock())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const CaseStmt *Case = I.getCase();
|
const CaseStmt *Case = I.getCase();
|
||||||
|
|
||||||
// Evaluate the LHS of the case value.
|
// Evaluate the LHS of the case value.
|
||||||
|
@ -1851,7 +1851,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
|
||||||
|
|
||||||
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
|
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
|
||||||
// C permits "extern void v", and if you cast the address to a valid type,
|
// C permits "extern void v", and if you cast the address to a valid type,
|
||||||
// you can even do things with it. We simply pretend
|
// you can even do things with it. We simply pretend
|
||||||
assert(Ex->isGLValue() || VD->getType()->isVoidType());
|
assert(Ex->isGLValue() || VD->getType()->isVoidType());
|
||||||
SVal V = state->getLValue(VD, Pred->getLocationContext());
|
SVal V = state->getLValue(VD, Pred->getLocationContext());
|
||||||
|
|
||||||
|
@ -1902,7 +1902,7 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
|
||||||
|
|
||||||
const Expr *Base = A->getBase()->IgnoreParens();
|
const Expr *Base = A->getBase()->IgnoreParens();
|
||||||
const Expr *Idx = A->getIdx()->IgnoreParens();
|
const Expr *Idx = A->getIdx()->IgnoreParens();
|
||||||
|
|
||||||
|
|
||||||
ExplodedNodeSet checkerPreStmt;
|
ExplodedNodeSet checkerPreStmt;
|
||||||
getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this);
|
getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this);
|
||||||
|
@ -2066,14 +2066,14 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State,
|
||||||
return State;
|
return State;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State,
|
ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State,
|
||||||
const InvalidatedSymbols *Invalidated,
|
const InvalidatedSymbols *Invalidated,
|
||||||
ArrayRef<const MemRegion *> ExplicitRegions,
|
ArrayRef<const MemRegion *> ExplicitRegions,
|
||||||
ArrayRef<const MemRegion *> Regions,
|
ArrayRef<const MemRegion *> Regions,
|
||||||
const CallEvent *Call,
|
const CallEvent *Call,
|
||||||
RegionAndSymbolInvalidationTraits &ITraits) {
|
RegionAndSymbolInvalidationTraits &ITraits) {
|
||||||
|
|
||||||
if (!Invalidated || Invalidated->empty())
|
if (!Invalidated || Invalidated->empty())
|
||||||
return State;
|
return State;
|
||||||
|
|
||||||
|
@ -2084,7 +2084,7 @@ ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State,
|
||||||
PSK_EscapeOther,
|
PSK_EscapeOther,
|
||||||
&ITraits);
|
&ITraits);
|
||||||
|
|
||||||
// If the symbols were invalidated by a call, we want to find out which ones
|
// If the symbols were invalidated by a call, we want to find out which ones
|
||||||
// were invalidated directly due to being arguments to the call.
|
// were invalidated directly due to being arguments to the call.
|
||||||
InvalidatedSymbols SymbolsDirectlyInvalidated;
|
InvalidatedSymbols SymbolsDirectlyInvalidated;
|
||||||
for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
|
for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
|
||||||
|
@ -2144,13 +2144,13 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
|
||||||
Bldr.generateNode(L, state, Pred);
|
Bldr.generateNode(L, state, Pred);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
|
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
|
||||||
I!=E; ++I) {
|
I!=E; ++I) {
|
||||||
ExplodedNode *PredI = *I;
|
ExplodedNode *PredI = *I;
|
||||||
ProgramStateRef state = PredI->getState();
|
ProgramStateRef state = PredI->getState();
|
||||||
|
|
||||||
state = processPointerEscapedOnBind(state, location, Val);
|
state = processPointerEscapedOnBind(state, location, Val);
|
||||||
|
|
||||||
// When binding the value, pass on the hint that this is a initialization.
|
// When binding the value, pass on the hint that this is a initialization.
|
||||||
|
@ -2303,7 +2303,7 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst,
|
||||||
// "p = 0" is not noted as "Null pointer value stored to 'p'" but
|
// "p = 0" is not noted as "Null pointer value stored to 'p'" but
|
||||||
// instead "int *p" is noted as
|
// instead "int *p" is noted as
|
||||||
// "Variable 'p' initialized to a null pointer value"
|
// "Variable 'p' initialized to a null pointer value"
|
||||||
|
|
||||||
static SimpleProgramPointTag tag(TagProviderName, "Location");
|
static SimpleProgramPointTag tag(TagProviderName, "Location");
|
||||||
Bldr.generateNode(NodeEx, Pred, state, &tag);
|
Bldr.generateNode(NodeEx, Pred, state, &tag);
|
||||||
}
|
}
|
||||||
|
@ -2328,7 +2328,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst,
|
||||||
ExplodedNodeSet &Src,
|
ExplodedNodeSet &Src,
|
||||||
const Expr *Ex) {
|
const Expr *Ex) {
|
||||||
StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx);
|
StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx);
|
||||||
|
|
||||||
for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) {
|
for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) {
|
||||||
ExplodedNode *Pred = *I;
|
ExplodedNode *Pred = *I;
|
||||||
// Test if the previous node was as the same expression. This can happen
|
// Test if the previous node was as the same expression. This can happen
|
||||||
|
@ -2351,7 +2351,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst,
|
||||||
|
|
||||||
// First assume that the condition is true.
|
// First assume that the condition is true.
|
||||||
if (StateTrue) {
|
if (StateTrue) {
|
||||||
SVal Val = svalBuilder.makeIntVal(1U, Ex->getType());
|
SVal Val = svalBuilder.makeIntVal(1U, Ex->getType());
|
||||||
StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val);
|
StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val);
|
||||||
Bldr.generateNode(Ex, Pred, StateTrue, tags.first);
|
Bldr.generateNode(Ex, Pred, StateTrue, tags.first);
|
||||||
}
|
}
|
||||||
|
@ -2643,10 +2643,10 @@ struct DOTGraphTraits<ExplodedNode*> :
|
||||||
<< " NodeID: " << (const void*) N << "\\|";
|
<< " NodeID: " << (const void*) N << "\\|";
|
||||||
state->printDOT(Out);
|
state->printDOT(Out);
|
||||||
|
|
||||||
Out << "\\l";
|
Out << "\\l";
|
||||||
|
|
||||||
if (const ProgramPointTag *tag = Loc.getTag()) {
|
if (const ProgramPointTag *tag = Loc.getTag()) {
|
||||||
Out << "\\|Tag: " << tag->getTagDescription();
|
Out << "\\|Tag: " << tag->getTagDescription();
|
||||||
Out << "\\l";
|
Out << "\\l";
|
||||||
}
|
}
|
||||||
return Out.str();
|
return Out.str();
|
||||||
|
|
|
@ -25,23 +25,23 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
|
||||||
|
|
||||||
Expr *LHS = B->getLHS()->IgnoreParens();
|
Expr *LHS = B->getLHS()->IgnoreParens();
|
||||||
Expr *RHS = B->getRHS()->IgnoreParens();
|
Expr *RHS = B->getRHS()->IgnoreParens();
|
||||||
|
|
||||||
// FIXME: Prechecks eventually go in ::Visit().
|
// FIXME: Prechecks eventually go in ::Visit().
|
||||||
ExplodedNodeSet CheckedSet;
|
ExplodedNodeSet CheckedSet;
|
||||||
ExplodedNodeSet Tmp2;
|
ExplodedNodeSet Tmp2;
|
||||||
getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, B, *this);
|
getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, B, *this);
|
||||||
|
|
||||||
// With both the LHS and RHS evaluated, process the operation itself.
|
// With both the LHS and RHS evaluated, process the operation itself.
|
||||||
for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end();
|
for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end();
|
||||||
it != ei; ++it) {
|
it != ei; ++it) {
|
||||||
|
|
||||||
ProgramStateRef state = (*it)->getState();
|
ProgramStateRef state = (*it)->getState();
|
||||||
const LocationContext *LCtx = (*it)->getLocationContext();
|
const LocationContext *LCtx = (*it)->getLocationContext();
|
||||||
SVal LeftV = state->getSVal(LHS, LCtx);
|
SVal LeftV = state->getSVal(LHS, LCtx);
|
||||||
SVal RightV = state->getSVal(RHS, LCtx);
|
SVal RightV = state->getSVal(RHS, LCtx);
|
||||||
|
|
||||||
BinaryOperator::Opcode Op = B->getOpcode();
|
BinaryOperator::Opcode Op = B->getOpcode();
|
||||||
|
|
||||||
if (Op == BO_Assign) {
|
if (Op == BO_Assign) {
|
||||||
// EXPERIMENTAL: "Conjured" symbols.
|
// EXPERIMENTAL: "Conjured" symbols.
|
||||||
// FIXME: Handle structs.
|
// FIXME: Handle structs.
|
||||||
|
@ -57,7 +57,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
|
||||||
LeftV, RightV);
|
LeftV, RightV);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!B->isAssignmentOp()) {
|
if (!B->isAssignmentOp()) {
|
||||||
StmtNodeBuilder Bldr(*it, Tmp2, *currBldrCtx);
|
StmtNodeBuilder Bldr(*it, Tmp2, *currBldrCtx);
|
||||||
|
|
||||||
|
@ -90,19 +90,19 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
|
||||||
|
|
||||||
// Process non-assignments except commas or short-circuited
|
// Process non-assignments except commas or short-circuited
|
||||||
// logical expressions (LAnd and LOr).
|
// logical expressions (LAnd and LOr).
|
||||||
SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
|
SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
|
||||||
if (Result.isUnknown()) {
|
if (Result.isUnknown()) {
|
||||||
Bldr.generateNode(B, *it, state);
|
Bldr.generateNode(B, *it, state);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
state = state->BindExpr(B, LCtx, Result);
|
state = state->BindExpr(B, LCtx, Result);
|
||||||
Bldr.generateNode(B, *it, state);
|
Bldr.generateNode(B, *it, state);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (B->isCompoundAssignmentOp());
|
assert (B->isCompoundAssignmentOp());
|
||||||
|
|
||||||
switch (Op) {
|
switch (Op) {
|
||||||
default:
|
default:
|
||||||
llvm_unreachable("Invalid opcode for compound assignment.");
|
llvm_unreachable("Invalid opcode for compound assignment.");
|
||||||
|
@ -117,43 +117,43 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
|
||||||
case BO_XorAssign: Op = BO_Xor; break;
|
case BO_XorAssign: Op = BO_Xor; break;
|
||||||
case BO_OrAssign: Op = BO_Or; break;
|
case BO_OrAssign: Op = BO_Or; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform a load (the LHS). This performs the checks for
|
// Perform a load (the LHS). This performs the checks for
|
||||||
// null dereferences, and so on.
|
// null dereferences, and so on.
|
||||||
ExplodedNodeSet Tmp;
|
ExplodedNodeSet Tmp;
|
||||||
SVal location = LeftV;
|
SVal location = LeftV;
|
||||||
evalLoad(Tmp, B, LHS, *it, state, location);
|
evalLoad(Tmp, B, LHS, *it, state, location);
|
||||||
|
|
||||||
for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E;
|
for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E;
|
||||||
++I) {
|
++I) {
|
||||||
|
|
||||||
state = (*I)->getState();
|
state = (*I)->getState();
|
||||||
const LocationContext *LCtx = (*I)->getLocationContext();
|
const LocationContext *LCtx = (*I)->getLocationContext();
|
||||||
SVal V = state->getSVal(LHS, LCtx);
|
SVal V = state->getSVal(LHS, LCtx);
|
||||||
|
|
||||||
// Get the computation type.
|
// Get the computation type.
|
||||||
QualType CTy =
|
QualType CTy =
|
||||||
cast<CompoundAssignOperator>(B)->getComputationResultType();
|
cast<CompoundAssignOperator>(B)->getComputationResultType();
|
||||||
CTy = getContext().getCanonicalType(CTy);
|
CTy = getContext().getCanonicalType(CTy);
|
||||||
|
|
||||||
QualType CLHSTy =
|
QualType CLHSTy =
|
||||||
cast<CompoundAssignOperator>(B)->getComputationLHSType();
|
cast<CompoundAssignOperator>(B)->getComputationLHSType();
|
||||||
CLHSTy = getContext().getCanonicalType(CLHSTy);
|
CLHSTy = getContext().getCanonicalType(CLHSTy);
|
||||||
|
|
||||||
QualType LTy = getContext().getCanonicalType(LHS->getType());
|
QualType LTy = getContext().getCanonicalType(LHS->getType());
|
||||||
|
|
||||||
// Promote LHS.
|
// Promote LHS.
|
||||||
V = svalBuilder.evalCast(V, CLHSTy, LTy);
|
V = svalBuilder.evalCast(V, CLHSTy, LTy);
|
||||||
|
|
||||||
// Compute the result of the operation.
|
// Compute the result of the operation.
|
||||||
SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy),
|
SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy),
|
||||||
B->getType(), CTy);
|
B->getType(), CTy);
|
||||||
|
|
||||||
// EXPERIMENTAL: "Conjured" symbols.
|
// EXPERIMENTAL: "Conjured" symbols.
|
||||||
// FIXME: Handle structs.
|
// FIXME: Handle structs.
|
||||||
|
|
||||||
SVal LHSVal;
|
SVal LHSVal;
|
||||||
|
|
||||||
if (Result.isUnknown()) {
|
if (Result.isUnknown()) {
|
||||||
// The symbolic value is actually for the type of the left-hand side
|
// The symbolic value is actually for the type of the left-hand side
|
||||||
// expression, not the computation type, as this is the value the
|
// expression, not the computation type, as this is the value the
|
||||||
|
@ -168,42 +168,42 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
|
||||||
// computation type.
|
// computation type.
|
||||||
LHSVal = svalBuilder.evalCast(Result, LTy, CTy);
|
LHSVal = svalBuilder.evalCast(Result, LTy, CTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In C++, assignment and compound assignment operators return an
|
// In C++, assignment and compound assignment operators return an
|
||||||
// lvalue.
|
// lvalue.
|
||||||
if (B->isGLValue())
|
if (B->isGLValue())
|
||||||
state = state->BindExpr(B, LCtx, location);
|
state = state->BindExpr(B, LCtx, location);
|
||||||
else
|
else
|
||||||
state = state->BindExpr(B, LCtx, Result);
|
state = state->BindExpr(B, LCtx, Result);
|
||||||
|
|
||||||
evalStore(Tmp2, B, LHS, *I, state, location, LHSVal);
|
evalStore(Tmp2, B, LHS, *I, state, location, LHSVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: postvisits eventually go in ::Visit()
|
// FIXME: postvisits eventually go in ::Visit()
|
||||||
getCheckerManager().runCheckersForPostStmt(Dst, Tmp2, B, *this);
|
getCheckerManager().runCheckersForPostStmt(Dst, Tmp2, B, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
|
void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
|
|
||||||
CanQualType T = getContext().getCanonicalType(BE->getType());
|
CanQualType T = getContext().getCanonicalType(BE->getType());
|
||||||
|
|
||||||
// Get the value of the block itself.
|
// Get the value of the block itself.
|
||||||
SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T,
|
SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T,
|
||||||
Pred->getLocationContext(),
|
Pred->getLocationContext(),
|
||||||
currBldrCtx->blockCount());
|
currBldrCtx->blockCount());
|
||||||
|
|
||||||
ProgramStateRef State = Pred->getState();
|
ProgramStateRef State = Pred->getState();
|
||||||
|
|
||||||
// If we created a new MemRegion for the block, we should explicitly bind
|
// If we created a new MemRegion for the block, we should explicitly bind
|
||||||
// the captured variables.
|
// the captured variables.
|
||||||
if (const BlockDataRegion *BDR =
|
if (const BlockDataRegion *BDR =
|
||||||
dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
|
dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
|
||||||
|
|
||||||
BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
|
BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
|
||||||
E = BDR->referenced_vars_end();
|
E = BDR->referenced_vars_end();
|
||||||
|
|
||||||
for (; I != E; ++I) {
|
for (; I != E; ++I) {
|
||||||
const MemRegion *capturedR = I.getCapturedRegion();
|
const MemRegion *capturedR = I.getCapturedRegion();
|
||||||
const MemRegion *originalR = I.getOriginalRegion();
|
const MemRegion *originalR = I.getOriginalRegion();
|
||||||
|
@ -213,7 +213,7 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExplodedNodeSet Tmp;
|
ExplodedNodeSet Tmp;
|
||||||
StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
|
StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
|
||||||
Bldr.generateNode(BE, Pred,
|
Bldr.generateNode(BE, Pred,
|
||||||
|
@ -224,12 +224,12 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
|
||||||
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
|
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
|
void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
|
||||||
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
|
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
|
||||||
|
|
||||||
ExplodedNodeSet dstPreStmt;
|
ExplodedNodeSet dstPreStmt;
|
||||||
getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this);
|
getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this);
|
||||||
|
|
||||||
if (CastE->getCastKind() == CK_LValueToRValue) {
|
if (CastE->getCastKind() == CK_LValueToRValue) {
|
||||||
for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
|
for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
|
||||||
I!=E; ++I) {
|
I!=E; ++I) {
|
||||||
|
@ -240,18 +240,18 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other casts.
|
// All other casts.
|
||||||
QualType T = CastE->getType();
|
QualType T = CastE->getType();
|
||||||
QualType ExTy = Ex->getType();
|
QualType ExTy = Ex->getType();
|
||||||
|
|
||||||
if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
|
if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
|
||||||
T = ExCast->getTypeAsWritten();
|
T = ExCast->getTypeAsWritten();
|
||||||
|
|
||||||
StmtNodeBuilder Bldr(dstPreStmt, Dst, *currBldrCtx);
|
StmtNodeBuilder Bldr(dstPreStmt, Dst, *currBldrCtx);
|
||||||
for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
|
for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
|
||||||
I != E; ++I) {
|
I != E; ++I) {
|
||||||
|
|
||||||
Pred = *I;
|
Pred = *I;
|
||||||
ProgramStateRef state = Pred->getState();
|
ProgramStateRef state = Pred->getState();
|
||||||
const LocationContext *LCtx = Pred->getLocationContext();
|
const LocationContext *LCtx = Pred->getLocationContext();
|
||||||
|
@ -316,8 +316,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
|
||||||
case CK_IntegralComplexToFloatingComplex:
|
case CK_IntegralComplexToFloatingComplex:
|
||||||
case CK_CPointerToObjCPointerCast:
|
case CK_CPointerToObjCPointerCast:
|
||||||
case CK_BlockPointerToObjCPointerCast:
|
case CK_BlockPointerToObjCPointerCast:
|
||||||
case CK_AnyPointerToBlockPointerCast:
|
case CK_AnyPointerToBlockPointerCast:
|
||||||
case CK_ObjCObjectLValueCast:
|
case CK_ObjCObjectLValueCast:
|
||||||
case CK_ZeroToOCLEvent:
|
case CK_ZeroToOCLEvent:
|
||||||
case CK_LValueBitCast: {
|
case CK_LValueBitCast: {
|
||||||
// Delegate to SValBuilder to process.
|
// Delegate to SValBuilder to process.
|
||||||
|
@ -371,7 +371,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
|
||||||
svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, resultType,
|
svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, resultType,
|
||||||
currBldrCtx->blockCount());
|
currBldrCtx->blockCount());
|
||||||
state = state->BindExpr(CastE, LCtx, NewSym);
|
state = state->BindExpr(CastE, LCtx, NewSym);
|
||||||
} else
|
} else
|
||||||
// Else, bind to the derived region value.
|
// Else, bind to the derived region value.
|
||||||
state = state->BindExpr(CastE, LCtx, val);
|
state = state->BindExpr(CastE, LCtx, val);
|
||||||
}
|
}
|
||||||
|
@ -417,7 +417,7 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
|
||||||
|
|
||||||
const Expr *Init = CL->getInitializer();
|
const Expr *Init = CL->getInitializer();
|
||||||
SVal V = State->getSVal(CL->getInitializer(), LCtx);
|
SVal V = State->getSVal(CL->getInitializer(), LCtx);
|
||||||
|
|
||||||
if (isa<CXXConstructExpr>(Init)) {
|
if (isa<CXXConstructExpr>(Init)) {
|
||||||
// No work needed. Just pass the value up to this expression.
|
// No work needed. Just pass the value up to this expression.
|
||||||
} else {
|
} else {
|
||||||
|
@ -450,11 +450,11 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
|
||||||
Dst.insert(Pred);
|
Dst.insert(Pred);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: all pre/post visits should eventually be handled by ::Visit().
|
// FIXME: all pre/post visits should eventually be handled by ::Visit().
|
||||||
ExplodedNodeSet dstPreVisit;
|
ExplodedNodeSet dstPreVisit;
|
||||||
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
|
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
|
||||||
|
|
||||||
ExplodedNodeSet dstEvaluated;
|
ExplodedNodeSet dstEvaluated;
|
||||||
StmtNodeBuilder B(dstPreVisit, dstEvaluated, *currBldrCtx);
|
StmtNodeBuilder B(dstPreVisit, dstEvaluated, *currBldrCtx);
|
||||||
for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
|
for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
|
||||||
|
@ -485,7 +485,7 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
|
||||||
assert(InitVal.getAs<nonloc::LazyCompoundVal>());
|
assert(InitVal.getAs<nonloc::LazyCompoundVal>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recover some path-sensitivity if a scalar value evaluated to
|
// Recover some path-sensitivity if a scalar value evaluated to
|
||||||
// UnknownVal.
|
// UnknownVal.
|
||||||
if (InitVal.isUnknown()) {
|
if (InitVal.isUnknown()) {
|
||||||
|
@ -596,7 +596,7 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
|
||||||
(T->isArrayType() || T->isRecordType() || T->isVectorType() ||
|
(T->isArrayType() || T->isRecordType() || T->isVectorType() ||
|
||||||
T->isAnyComplexType())) {
|
T->isAnyComplexType())) {
|
||||||
llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList();
|
llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList();
|
||||||
|
|
||||||
// Handle base case where the initializer has no elements.
|
// Handle base case where the initializer has no elements.
|
||||||
// e.g: static int* myArray[] = {};
|
// e.g: static int* myArray[] = {};
|
||||||
if (NumInitElements == 0) {
|
if (NumInitElements == 0) {
|
||||||
|
@ -604,13 +604,13 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
|
||||||
B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V));
|
B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (InitListExpr::const_reverse_iterator it = IE->rbegin(),
|
for (InitListExpr::const_reverse_iterator it = IE->rbegin(),
|
||||||
ei = IE->rend(); it != ei; ++it) {
|
ei = IE->rend(); it != ei; ++it) {
|
||||||
SVal V = state->getSVal(cast<Expr>(*it), LCtx);
|
SVal V = state->getSVal(cast<Expr>(*it), LCtx);
|
||||||
vals = getBasicVals().consVals(V, vals);
|
vals = getBasicVals().consVals(V, vals);
|
||||||
}
|
}
|
||||||
|
|
||||||
B.generateNode(IE, Pred,
|
B.generateNode(IE, Pred,
|
||||||
state->BindExpr(IE, LCtx,
|
state->BindExpr(IE, LCtx,
|
||||||
svalBuilder.makeCompoundVal(T, vals)));
|
svalBuilder.makeCompoundVal(T, vals)));
|
||||||
|
@ -632,7 +632,7 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::VisitGuardedExpr(const Expr *Ex,
|
void ExprEngine::VisitGuardedExpr(const Expr *Ex,
|
||||||
const Expr *L,
|
const Expr *L,
|
||||||
const Expr *R,
|
const Expr *R,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
|
@ -692,7 +692,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::
|
void ExprEngine::
|
||||||
VisitOffsetOfExpr(const OffsetOfExpr *OOE,
|
VisitOffsetOfExpr(const OffsetOfExpr *OOE,
|
||||||
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
|
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
|
||||||
StmtNodeBuilder B(Pred, Dst, *currBldrCtx);
|
StmtNodeBuilder B(Pred, Dst, *currBldrCtx);
|
||||||
APSInt IV;
|
APSInt IV;
|
||||||
|
@ -728,7 +728,7 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
|
||||||
if (Ex->getKind() == UETT_SizeOf) {
|
if (Ex->getKind() == UETT_SizeOf) {
|
||||||
if (!T->isIncompleteType() && !T->isConstantSizeType()) {
|
if (!T->isIncompleteType() && !T->isConstantSizeType()) {
|
||||||
assert(T->isVariableArrayType() && "Unknown non-constant-sized type.");
|
assert(T->isVariableArrayType() && "Unknown non-constant-sized type.");
|
||||||
|
|
||||||
// FIXME: Add support for VLA type arguments and VLA expressions.
|
// FIXME: Add support for VLA type arguments and VLA expressions.
|
||||||
// When that happens, we should probably refactor VLASizeChecker's code.
|
// When that happens, we should probably refactor VLASizeChecker's code.
|
||||||
continue;
|
continue;
|
||||||
|
@ -739,10 +739,10 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
APSInt Value = Ex->EvaluateKnownConstInt(getContext());
|
APSInt Value = Ex->EvaluateKnownConstInt(getContext());
|
||||||
CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue());
|
CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue());
|
||||||
|
|
||||||
ProgramStateRef state = (*I)->getState();
|
ProgramStateRef state = (*I)->getState();
|
||||||
state = state->BindExpr(Ex, (*I)->getLocationContext(),
|
state = state->BindExpr(Ex, (*I)->getLocationContext(),
|
||||||
svalBuilder.makeIntVal(amt.getQuantity(),
|
svalBuilder.makeIntVal(amt.getQuantity(),
|
||||||
|
@ -753,7 +753,7 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
|
||||||
getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
|
getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
// FIXME: Prechecks eventually go in ::Visit().
|
// FIXME: Prechecks eventually go in ::Visit().
|
||||||
|
@ -775,13 +775,13 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
||||||
}
|
}
|
||||||
case UO_Real: {
|
case UO_Real: {
|
||||||
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
||||||
|
|
||||||
// FIXME: We don't have complex SValues yet.
|
// FIXME: We don't have complex SValues yet.
|
||||||
if (Ex->getType()->isAnyComplexType()) {
|
if (Ex->getType()->isAnyComplexType()) {
|
||||||
// Just report "Unknown."
|
// Just report "Unknown."
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all other types, UO_Real is an identity operation.
|
// For all other types, UO_Real is an identity operation.
|
||||||
assert (U->getType() == Ex->getType());
|
assert (U->getType() == Ex->getType());
|
||||||
ProgramStateRef state = (*I)->getState();
|
ProgramStateRef state = (*I)->getState();
|
||||||
|
@ -790,8 +790,8 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
||||||
state->getSVal(Ex, LCtx)));
|
state->getSVal(Ex, LCtx)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case UO_Imag: {
|
case UO_Imag: {
|
||||||
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
||||||
// FIXME: We don't have complex SValues yet.
|
// FIXME: We don't have complex SValues yet.
|
||||||
if (Ex->getType()->isAnyComplexType()) {
|
if (Ex->getType()->isAnyComplexType()) {
|
||||||
|
@ -805,7 +805,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
||||||
Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, X));
|
Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, X));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case UO_Plus:
|
case UO_Plus:
|
||||||
assert(!U->isGLValue());
|
assert(!U->isGLValue());
|
||||||
// FALL-THROUGH.
|
// FALL-THROUGH.
|
||||||
|
@ -818,7 +818,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
||||||
// Unary "+" is a no-op, similar to a parentheses. We still have places
|
// Unary "+" is a no-op, similar to a parentheses. We still have places
|
||||||
// where it may be a block-level expression, so we need to
|
// where it may be a block-level expression, so we need to
|
||||||
// generate an extra node that just propagates the value of the
|
// generate an extra node that just propagates the value of the
|
||||||
// subexpression.
|
// subexpression.
|
||||||
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
||||||
ProgramStateRef state = (*I)->getState();
|
ProgramStateRef state = (*I)->getState();
|
||||||
const LocationContext *LCtx = (*I)->getLocationContext();
|
const LocationContext *LCtx = (*I)->getLocationContext();
|
||||||
|
@ -826,7 +826,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
||||||
state->getSVal(Ex, LCtx)));
|
state->getSVal(Ex, LCtx)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case UO_LNot:
|
case UO_LNot:
|
||||||
case UO_Minus:
|
case UO_Minus:
|
||||||
case UO_Not: {
|
case UO_Not: {
|
||||||
|
@ -834,15 +834,15 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
||||||
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
||||||
ProgramStateRef state = (*I)->getState();
|
ProgramStateRef state = (*I)->getState();
|
||||||
const LocationContext *LCtx = (*I)->getLocationContext();
|
const LocationContext *LCtx = (*I)->getLocationContext();
|
||||||
|
|
||||||
// Get the value of the subexpression.
|
// Get the value of the subexpression.
|
||||||
SVal V = state->getSVal(Ex, LCtx);
|
SVal V = state->getSVal(Ex, LCtx);
|
||||||
|
|
||||||
if (V.isUnknownOrUndef()) {
|
if (V.isUnknownOrUndef()) {
|
||||||
Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V));
|
Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (U->getOpcode()) {
|
switch (U->getOpcode()) {
|
||||||
default:
|
default:
|
||||||
llvm_unreachable("Invalid Opcode.");
|
llvm_unreachable("Invalid Opcode.");
|
||||||
|
@ -859,7 +859,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
||||||
//
|
//
|
||||||
// Note: technically we do "E == 0", but this is the same in the
|
// Note: technically we do "E == 0", but this is the same in the
|
||||||
// transfer functions as "0 == E".
|
// transfer functions as "0 == E".
|
||||||
SVal Result;
|
SVal Result;
|
||||||
if (Optional<Loc> LV = V.getAs<Loc>()) {
|
if (Optional<Loc> LV = V.getAs<Loc>()) {
|
||||||
Loc X = svalBuilder.makeNull();
|
Loc X = svalBuilder.makeNull();
|
||||||
Result = evalBinOp(state, BO_EQ, *LV, X, U->getType());
|
Result = evalBinOp(state, BO_EQ, *LV, X, U->getType());
|
||||||
|
@ -872,8 +872,8 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
|
||||||
Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X,
|
Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X,
|
||||||
U->getType());
|
U->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
state = state->BindExpr(U, LCtx, Result);
|
state = state->BindExpr(U, LCtx, Result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Bldr.generateNode(U, *I, state);
|
Bldr.generateNode(U, *I, state);
|
||||||
|
@ -891,81 +891,81 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
|
||||||
// Handle ++ and -- (both pre- and post-increment).
|
// Handle ++ and -- (both pre- and post-increment).
|
||||||
assert (U->isIncrementDecrementOp());
|
assert (U->isIncrementDecrementOp());
|
||||||
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
const Expr *Ex = U->getSubExpr()->IgnoreParens();
|
||||||
|
|
||||||
const LocationContext *LCtx = Pred->getLocationContext();
|
const LocationContext *LCtx = Pred->getLocationContext();
|
||||||
ProgramStateRef state = Pred->getState();
|
ProgramStateRef state = Pred->getState();
|
||||||
SVal loc = state->getSVal(Ex, LCtx);
|
SVal loc = state->getSVal(Ex, LCtx);
|
||||||
|
|
||||||
// Perform a load.
|
// Perform a load.
|
||||||
ExplodedNodeSet Tmp;
|
ExplodedNodeSet Tmp;
|
||||||
evalLoad(Tmp, U, Ex, Pred, state, loc);
|
evalLoad(Tmp, U, Ex, Pred, state, loc);
|
||||||
|
|
||||||
ExplodedNodeSet Dst2;
|
ExplodedNodeSet Dst2;
|
||||||
StmtNodeBuilder Bldr(Tmp, Dst2, *currBldrCtx);
|
StmtNodeBuilder Bldr(Tmp, Dst2, *currBldrCtx);
|
||||||
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end();I!=E;++I) {
|
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end();I!=E;++I) {
|
||||||
|
|
||||||
state = (*I)->getState();
|
state = (*I)->getState();
|
||||||
assert(LCtx == (*I)->getLocationContext());
|
assert(LCtx == (*I)->getLocationContext());
|
||||||
SVal V2_untested = state->getSVal(Ex, LCtx);
|
SVal V2_untested = state->getSVal(Ex, LCtx);
|
||||||
|
|
||||||
// Propagate unknown and undefined values.
|
// Propagate unknown and undefined values.
|
||||||
if (V2_untested.isUnknownOrUndef()) {
|
if (V2_untested.isUnknownOrUndef()) {
|
||||||
Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V2_untested));
|
Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V2_untested));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DefinedSVal V2 = V2_untested.castAs<DefinedSVal>();
|
DefinedSVal V2 = V2_untested.castAs<DefinedSVal>();
|
||||||
|
|
||||||
// Handle all other values.
|
// Handle all other values.
|
||||||
BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub;
|
BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub;
|
||||||
|
|
||||||
// If the UnaryOperator has non-location type, use its type to create the
|
// If the UnaryOperator has non-location type, use its type to create the
|
||||||
// constant value. If the UnaryOperator has location type, create the
|
// constant value. If the UnaryOperator has location type, create the
|
||||||
// constant with int type and pointer width.
|
// constant with int type and pointer width.
|
||||||
SVal RHS;
|
SVal RHS;
|
||||||
|
|
||||||
if (U->getType()->isAnyPointerType())
|
if (U->getType()->isAnyPointerType())
|
||||||
RHS = svalBuilder.makeArrayIndex(1);
|
RHS = svalBuilder.makeArrayIndex(1);
|
||||||
else if (U->getType()->isIntegralOrEnumerationType())
|
else if (U->getType()->isIntegralOrEnumerationType())
|
||||||
RHS = svalBuilder.makeIntVal(1, U->getType());
|
RHS = svalBuilder.makeIntVal(1, U->getType());
|
||||||
else
|
else
|
||||||
RHS = UnknownVal();
|
RHS = UnknownVal();
|
||||||
|
|
||||||
SVal Result = evalBinOp(state, Op, V2, RHS, U->getType());
|
SVal Result = evalBinOp(state, Op, V2, RHS, U->getType());
|
||||||
|
|
||||||
// Conjure a new symbol if necessary to recover precision.
|
// Conjure a new symbol if necessary to recover precision.
|
||||||
if (Result.isUnknown()){
|
if (Result.isUnknown()){
|
||||||
DefinedOrUnknownSVal SymVal =
|
DefinedOrUnknownSVal SymVal =
|
||||||
svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx,
|
svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx,
|
||||||
currBldrCtx->blockCount());
|
currBldrCtx->blockCount());
|
||||||
Result = SymVal;
|
Result = SymVal;
|
||||||
|
|
||||||
// If the value is a location, ++/-- should always preserve
|
// If the value is a location, ++/-- should always preserve
|
||||||
// non-nullness. Check if the original value was non-null, and if so
|
// non-nullness. Check if the original value was non-null, and if so
|
||||||
// propagate that constraint.
|
// propagate that constraint.
|
||||||
if (Loc::isLocType(U->getType())) {
|
if (Loc::isLocType(U->getType())) {
|
||||||
DefinedOrUnknownSVal Constraint =
|
DefinedOrUnknownSVal Constraint =
|
||||||
svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType()));
|
svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType()));
|
||||||
|
|
||||||
if (!state->assume(Constraint, true)) {
|
if (!state->assume(Constraint, true)) {
|
||||||
// It isn't feasible for the original value to be null.
|
// It isn't feasible for the original value to be null.
|
||||||
// Propagate this constraint.
|
// Propagate this constraint.
|
||||||
Constraint = svalBuilder.evalEQ(state, SymVal,
|
Constraint = svalBuilder.evalEQ(state, SymVal,
|
||||||
svalBuilder.makeZeroVal(U->getType()));
|
svalBuilder.makeZeroVal(U->getType()));
|
||||||
|
|
||||||
|
|
||||||
state = state->assume(Constraint, false);
|
state = state->assume(Constraint, false);
|
||||||
assert(state);
|
assert(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since the lvalue-to-rvalue conversion is explicit in the AST,
|
// Since the lvalue-to-rvalue conversion is explicit in the AST,
|
||||||
// we bind an l-value if the operator is prefix and an lvalue (in C++).
|
// we bind an l-value if the operator is prefix and an lvalue (in C++).
|
||||||
if (U->isGLValue())
|
if (U->isGLValue())
|
||||||
state = state->BindExpr(U, LCtx, loc);
|
state = state->BindExpr(U, LCtx, loc);
|
||||||
else
|
else
|
||||||
state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result);
|
state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result);
|
||||||
|
|
||||||
// Perform the store.
|
// Perform the store.
|
||||||
Bldr.takeNodes(*I);
|
Bldr.takeNodes(*I);
|
||||||
ExplodedNodeSet Dst3;
|
ExplodedNodeSet Dst3;
|
||||||
|
|
|
@ -300,7 +300,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
|
||||||
const MemRegion *Dest,
|
const MemRegion *Dest,
|
||||||
const Stmt *S,
|
const Stmt *S,
|
||||||
bool IsBaseDtor,
|
bool IsBaseDtor,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
const LocationContext *LCtx = Pred->getLocationContext();
|
const LocationContext *LCtx = Pred->getLocationContext();
|
||||||
ProgramStateRef State = Pred->getState();
|
ProgramStateRef State = Pred->getState();
|
||||||
|
@ -373,7 +373,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
|
||||||
// Also, we need to decide how allocators actually work -- they're not
|
// Also, we need to decide how allocators actually work -- they're not
|
||||||
// really part of the CXXNewExpr because they happen BEFORE the
|
// really part of the CXXNewExpr because they happen BEFORE the
|
||||||
// CXXConstructExpr subexpression. See PR12014 for some discussion.
|
// CXXConstructExpr subexpression. See PR12014 for some discussion.
|
||||||
|
|
||||||
unsigned blockCount = currBldrCtx->blockCount();
|
unsigned blockCount = currBldrCtx->blockCount();
|
||||||
const LocationContext *LCtx = Pred->getLocationContext();
|
const LocationContext *LCtx = Pred->getLocationContext();
|
||||||
DefinedOrUnknownSVal symVal = UnknownVal();
|
DefinedOrUnknownSVal symVal = UnknownVal();
|
||||||
|
@ -392,8 +392,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
|
||||||
IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1);
|
IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We assume all standard global 'operator new' functions allocate memory in
|
// We assume all standard global 'operator new' functions allocate memory in
|
||||||
// heap. We realize this is an approximation that might not correctly model
|
// heap. We realize this is an approximation that might not correctly model
|
||||||
// a custom global allocator.
|
// a custom global allocator.
|
||||||
if (IsStandardGlobalOpNewFunction)
|
if (IsStandardGlobalOpNewFunction)
|
||||||
symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount);
|
symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount);
|
||||||
|
@ -472,7 +472,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
|
void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
|
||||||
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
|
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
|
||||||
StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
|
StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
|
||||||
ProgramStateRef state = Pred->getState();
|
ProgramStateRef state = Pred->getState();
|
||||||
|
|
|
@ -44,19 +44,19 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
|
||||||
|
|
||||||
const CFG *CalleeCFG = calleeCtx->getCFG();
|
const CFG *CalleeCFG = calleeCtx->getCFG();
|
||||||
const CFGBlock *Entry = &(CalleeCFG->getEntry());
|
const CFGBlock *Entry = &(CalleeCFG->getEntry());
|
||||||
|
|
||||||
// Validate the CFG.
|
// Validate the CFG.
|
||||||
assert(Entry->empty());
|
assert(Entry->empty());
|
||||||
assert(Entry->succ_size() == 1);
|
assert(Entry->succ_size() == 1);
|
||||||
|
|
||||||
// Get the solitary successor.
|
// Get the solitary successor.
|
||||||
const CFGBlock *Succ = *(Entry->succ_begin());
|
const CFGBlock *Succ = *(Entry->succ_begin());
|
||||||
|
|
||||||
// Construct an edge representing the starting location in the callee.
|
// Construct an edge representing the starting location in the callee.
|
||||||
BlockEdge Loc(Entry, Succ, calleeCtx);
|
BlockEdge Loc(Entry, Succ, calleeCtx);
|
||||||
|
|
||||||
ProgramStateRef state = Pred->getState();
|
ProgramStateRef state = Pred->getState();
|
||||||
|
|
||||||
// Construct a new node and add it to the worklist.
|
// Construct a new node and add it to the worklist.
|
||||||
bool isNew;
|
bool isNew;
|
||||||
ExplodedNode *Node = G.getNode(Loc, state, false, &isNew);
|
ExplodedNode *Node = G.getNode(Loc, state, false, &isNew);
|
||||||
|
@ -207,8 +207,8 @@ static bool isTemporaryPRValue(const CXXConstructExpr *E, SVal V) {
|
||||||
return isa<CXXTempObjectRegion>(MR);
|
return isa<CXXTempObjectRegion>(MR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The call exit is simulated with a sequence of nodes, which occur between
|
/// The call exit is simulated with a sequence of nodes, which occur between
|
||||||
/// CallExitBegin and CallExitEnd. The following operations occur between the
|
/// CallExitBegin and CallExitEnd. The following operations occur between the
|
||||||
/// two program points:
|
/// two program points:
|
||||||
/// 1. CallExitBegin (triggers the start of call exit sequence)
|
/// 1. CallExitBegin (triggers the start of call exit sequence)
|
||||||
/// 2. Bind the return value
|
/// 2. Bind the return value
|
||||||
|
@ -220,12 +220,12 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
|
||||||
PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext());
|
PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext());
|
||||||
const StackFrameContext *calleeCtx =
|
const StackFrameContext *calleeCtx =
|
||||||
CEBNode->getLocationContext()->getCurrentStackFrame();
|
CEBNode->getLocationContext()->getCurrentStackFrame();
|
||||||
|
|
||||||
// The parent context might not be a stack frame, so make sure we
|
// The parent context might not be a stack frame, so make sure we
|
||||||
// look up the first enclosing stack frame.
|
// look up the first enclosing stack frame.
|
||||||
const StackFrameContext *callerCtx =
|
const StackFrameContext *callerCtx =
|
||||||
calleeCtx->getParent()->getCurrentStackFrame();
|
calleeCtx->getParent()->getCurrentStackFrame();
|
||||||
|
|
||||||
const Stmt *CE = calleeCtx->getCallSite();
|
const Stmt *CE = calleeCtx->getCallSite();
|
||||||
ProgramStateRef state = CEBNode->getState();
|
ProgramStateRef state = CEBNode->getState();
|
||||||
// Find the last statement in the function and the corresponding basic block.
|
// Find the last statement in the function and the corresponding basic block.
|
||||||
|
@ -429,7 +429,7 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
|
||||||
cast<BlockDecl>(D),
|
cast<BlockDecl>(D),
|
||||||
BR);
|
BR);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This may be NULL, but that's fine.
|
// This may be NULL, but that's fine.
|
||||||
const Expr *CallE = Call.getOriginExpr();
|
const Expr *CallE = Call.getOriginExpr();
|
||||||
|
|
||||||
|
@ -439,8 +439,8 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
|
||||||
CalleeADC->getStackFrame(ParentOfCallee, CallE,
|
CalleeADC->getStackFrame(ParentOfCallee, CallE,
|
||||||
currBldrCtx->getBlock(),
|
currBldrCtx->getBlock(),
|
||||||
currStmtIdx);
|
currStmtIdx);
|
||||||
|
|
||||||
|
|
||||||
CallEnter Loc(CallE, CalleeSFC, CurLC);
|
CallEnter Loc(CallE, CalleeSFC, CurLC);
|
||||||
|
|
||||||
// Construct a new state which contains the mapping from actual to
|
// Construct a new state which contains the mapping from actual to
|
||||||
|
@ -769,7 +769,7 @@ static bool mayInlineDecl(AnalysisDeclContext *CalleeADC,
|
||||||
if (!Ctx.getSourceManager().isInMainFile(FD->getLocation()))
|
if (!Ctx.getSourceManager().isInMainFile(FD->getLocation()))
|
||||||
if (isContainerMethod(Ctx, FD))
|
if (isContainerMethod(Ctx, FD))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Conditionally control the inlining of the destructor of C++ shared_ptr.
|
// Conditionally control the inlining of the destructor of C++ shared_ptr.
|
||||||
// We don't currently do a good job modeling shared_ptr because we can't
|
// We don't currently do a good job modeling shared_ptr because we can't
|
||||||
// see the reference count, so treating as opaque is probably the best
|
// see the reference count, so treating as opaque is probably the best
|
||||||
|
@ -992,12 +992,12 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg,
|
||||||
|
|
||||||
void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
|
void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
|
|
||||||
ExplodedNodeSet dstPreVisit;
|
ExplodedNodeSet dstPreVisit;
|
||||||
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, RS, *this);
|
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, RS, *this);
|
||||||
|
|
||||||
StmtNodeBuilder B(dstPreVisit, Dst, *currBldrCtx);
|
StmtNodeBuilder B(dstPreVisit, Dst, *currBldrCtx);
|
||||||
|
|
||||||
if (RS->getRetValue()) {
|
if (RS->getRetValue()) {
|
||||||
for (ExplodedNodeSet::iterator it = dstPreVisit.begin(),
|
for (ExplodedNodeSet::iterator it = dstPreVisit.begin(),
|
||||||
ei = dstPreVisit.end(); it != ei; ++it) {
|
ei = dstPreVisit.end(); it != ei; ++it) {
|
||||||
|
|
|
@ -19,18 +19,18 @@
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
using namespace ento;
|
using namespace ento;
|
||||||
|
|
||||||
void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
|
void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
ProgramStateRef state = Pred->getState();
|
ProgramStateRef state = Pred->getState();
|
||||||
const LocationContext *LCtx = Pred->getLocationContext();
|
const LocationContext *LCtx = Pred->getLocationContext();
|
||||||
SVal baseVal = state->getSVal(Ex->getBase(), LCtx);
|
SVal baseVal = state->getSVal(Ex->getBase(), LCtx);
|
||||||
SVal location = state->getLValue(Ex->getDecl(), baseVal);
|
SVal location = state->getLValue(Ex->getDecl(), baseVal);
|
||||||
|
|
||||||
ExplodedNodeSet dstIvar;
|
ExplodedNodeSet dstIvar;
|
||||||
StmtNodeBuilder Bldr(Pred, dstIvar, *currBldrCtx);
|
StmtNodeBuilder Bldr(Pred, dstIvar, *currBldrCtx);
|
||||||
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location));
|
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location));
|
||||||
|
|
||||||
// Perform the post-condition check of the ObjCIvarRefExpr and store
|
// Perform the post-condition check of the ObjCIvarRefExpr and store
|
||||||
// the created nodes in 'Dst'.
|
// the created nodes in 'Dst'.
|
||||||
getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this);
|
getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this);
|
||||||
|
@ -45,7 +45,7 @@ void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
|
||||||
void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
|
void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
|
|
||||||
// ObjCForCollectionStmts are processed in two places. This method
|
// ObjCForCollectionStmts are processed in two places. This method
|
||||||
// handles the case where an ObjCForCollectionStmt* occurs as one of the
|
// handles the case where an ObjCForCollectionStmt* occurs as one of the
|
||||||
// statements within a basic block. This transfer function does two things:
|
// statements within a basic block. This transfer function does two things:
|
||||||
|
@ -74,7 +74,7 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
|
||||||
const Stmt *elem = S->getElement();
|
const Stmt *elem = S->getElement();
|
||||||
ProgramStateRef state = Pred->getState();
|
ProgramStateRef state = Pred->getState();
|
||||||
SVal elementV;
|
SVal elementV;
|
||||||
|
|
||||||
if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) {
|
if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) {
|
||||||
const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
|
const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
|
||||||
assert(elemD->getInit() == nullptr);
|
assert(elemD->getInit() == nullptr);
|
||||||
|
@ -83,7 +83,7 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
|
||||||
else {
|
else {
|
||||||
elementV = state->getSVal(elem, Pred->getLocationContext());
|
elementV = state->getSVal(elem, Pred->getLocationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
ExplodedNodeSet dstLocation;
|
ExplodedNodeSet dstLocation;
|
||||||
evalLocation(dstLocation, S, elem, Pred, state, elementV, nullptr, false);
|
evalLocation(dstLocation, S, elem, Pred, state, elementV, nullptr, false);
|
||||||
|
|
||||||
|
@ -95,17 +95,17 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
|
||||||
Pred = *NI;
|
Pred = *NI;
|
||||||
ProgramStateRef state = Pred->getState();
|
ProgramStateRef state = Pred->getState();
|
||||||
const LocationContext *LCtx = Pred->getLocationContext();
|
const LocationContext *LCtx = Pred->getLocationContext();
|
||||||
|
|
||||||
// Handle the case where the container still has elements.
|
// Handle the case where the container still has elements.
|
||||||
SVal TrueV = svalBuilder.makeTruthVal(1);
|
SVal TrueV = svalBuilder.makeTruthVal(1);
|
||||||
ProgramStateRef hasElems = state->BindExpr(S, LCtx, TrueV);
|
ProgramStateRef hasElems = state->BindExpr(S, LCtx, TrueV);
|
||||||
|
|
||||||
// Handle the case where the container has no elements.
|
// Handle the case where the container has no elements.
|
||||||
SVal FalseV = svalBuilder.makeTruthVal(0);
|
SVal FalseV = svalBuilder.makeTruthVal(0);
|
||||||
ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV);
|
ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV);
|
||||||
|
|
||||||
if (Optional<loc::MemRegionVal> MV = elementV.getAs<loc::MemRegionVal>())
|
if (Optional<loc::MemRegionVal> MV = elementV.getAs<loc::MemRegionVal>())
|
||||||
if (const TypedValueRegion *R =
|
if (const TypedValueRegion *R =
|
||||||
dyn_cast<TypedValueRegion>(MV->getRegion())) {
|
dyn_cast<TypedValueRegion>(MV->getRegion())) {
|
||||||
// FIXME: The proper thing to do is to really iterate over the
|
// FIXME: The proper thing to do is to really iterate over the
|
||||||
// container. We will do this with dispatch logic to the store.
|
// container. We will do this with dispatch logic to the store.
|
||||||
|
@ -116,12 +116,12 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
|
||||||
currBldrCtx->blockCount());
|
currBldrCtx->blockCount());
|
||||||
SVal V = svalBuilder.makeLoc(Sym);
|
SVal V = svalBuilder.makeLoc(Sym);
|
||||||
hasElems = hasElems->bindLoc(elementV, V);
|
hasElems = hasElems->bindLoc(elementV, V);
|
||||||
|
|
||||||
// Bind the location to 'nil' on the false branch.
|
// Bind the location to 'nil' on the false branch.
|
||||||
SVal nilV = svalBuilder.makeIntVal(0, T);
|
SVal nilV = svalBuilder.makeIntVal(0, T);
|
||||||
noElems = noElems->bindLoc(elementV, nilV);
|
noElems = noElems->bindLoc(elementV, nilV);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the new nodes.
|
// Create the new nodes.
|
||||||
Bldr.generateNode(S, Pred, hasElems);
|
Bldr.generateNode(S, Pred, hasElems);
|
||||||
Bldr.generateNode(S, Pred, noElems);
|
Bldr.generateNode(S, Pred, noElems);
|
||||||
|
@ -156,7 +156,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
|
||||||
ExplodedNode *Pred = *DI;
|
ExplodedNode *Pred = *DI;
|
||||||
ProgramStateRef State = Pred->getState();
|
ProgramStateRef State = Pred->getState();
|
||||||
CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State);
|
CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State);
|
||||||
|
|
||||||
if (UpdatedMsg->isInstanceMessage()) {
|
if (UpdatedMsg->isInstanceMessage()) {
|
||||||
SVal recVal = UpdatedMsg->getReceiverSVal();
|
SVal recVal = UpdatedMsg->getReceiverSVal();
|
||||||
if (!recVal.isUndef()) {
|
if (!recVal.isUndef()) {
|
||||||
|
@ -166,7 +166,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
|
||||||
|
|
||||||
ProgramStateRef notNilState, nilState;
|
ProgramStateRef notNilState, nilState;
|
||||||
std::tie(notNilState, nilState) = State->assume(receiverVal);
|
std::tie(notNilState, nilState) = State->assume(receiverVal);
|
||||||
|
|
||||||
// There are three cases: can be nil or non-nil, must be nil, must be
|
// There are three cases: can be nil or non-nil, must be nil, must be
|
||||||
// non-nil. We ignore must be nil, and merge the rest two into non-nil.
|
// non-nil. We ignore must be nil, and merge the rest two into non-nil.
|
||||||
// FIXME: This ignores many potential bugs (<rdar://problem/11733396>).
|
// FIXME: This ignores many potential bugs (<rdar://problem/11733396>).
|
||||||
|
@ -174,7 +174,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
|
||||||
if (nilState && !notNilState) {
|
if (nilState && !notNilState) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the "raise" message was sent.
|
// Check if the "raise" message was sent.
|
||||||
assert(notNilState);
|
assert(notNilState);
|
||||||
if (ObjCNoRet.isImplicitNoReturn(ME)) {
|
if (ObjCNoRet.isImplicitNoReturn(ME)) {
|
||||||
|
@ -183,7 +183,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
|
||||||
Bldr.generateSink(ME, Pred, State);
|
Bldr.generateSink(ME, Pred, State);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a transition to non-Nil state.
|
// Generate a transition to non-Nil state.
|
||||||
if (notNilState != State) {
|
if (notNilState != State) {
|
||||||
Pred = Bldr.generateNode(ME, Pred, notNilState);
|
Pred = Bldr.generateNode(ME, Pred, notNilState);
|
||||||
|
@ -203,7 +203,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
|
||||||
|
|
||||||
defaultEvalCall(Bldr, Pred, *UpdatedMsg);
|
defaultEvalCall(Bldr, Pred, *UpdatedMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExplodedNodeSet dstPostvisit;
|
ExplodedNodeSet dstPostvisit;
|
||||||
getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval,
|
getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval,
|
||||||
*Msg, *this);
|
*Msg, *this);
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
##===- clang/lib/StaticAnalyzer/Core/Makefile --------------*- Makefile -*-===##
|
##===- clang/lib/StaticAnalyzer/Core/Makefile --------------*- Makefile -*-===##
|
||||||
#
|
#
|
||||||
# The LLVM Compiler Infrastructure
|
# The LLVM Compiler Infrastructure
|
||||||
#
|
#
|
||||||
# This file is distributed under the University of Illinois Open Source
|
# This file is distributed under the University of Illinois Open Source
|
||||||
# License. See LICENSE.TXT for details.
|
# License. See LICENSE.TXT for details.
|
||||||
#
|
#
|
||||||
##===----------------------------------------------------------------------===##
|
##===----------------------------------------------------------------------===##
|
||||||
#
|
#
|
||||||
# This implements analyses built on top of source-level CFGs.
|
# This implements analyses built on top of source-level CFGs.
|
||||||
#
|
#
|
||||||
##===----------------------------------------------------------------------===##
|
##===----------------------------------------------------------------------===##
|
||||||
|
|
||||||
|
|
|
@ -756,7 +756,7 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
|
||||||
return cast<VarRegion>(I.getCapturedRegion());
|
return cast<VarRegion>(I.getCapturedRegion());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LC = LC->getParent();
|
LC = LC->getParent();
|
||||||
}
|
}
|
||||||
return (const StackFrameContext *)nullptr;
|
return (const StackFrameContext *)nullptr;
|
||||||
|
@ -788,18 +788,18 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
|
||||||
else
|
else
|
||||||
sReg = getGlobalsRegion();
|
sReg = getGlobalsRegion();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally handle static locals.
|
// Finally handle static locals.
|
||||||
} else {
|
} else {
|
||||||
// FIXME: Once we implement scope handling, we will need to properly lookup
|
// FIXME: Once we implement scope handling, we will need to properly lookup
|
||||||
// 'D' to the proper LocationContext.
|
// 'D' to the proper LocationContext.
|
||||||
const DeclContext *DC = D->getDeclContext();
|
const DeclContext *DC = D->getDeclContext();
|
||||||
llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V =
|
llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V =
|
||||||
getStackOrCaptureRegionForDeclContext(LC, DC, D);
|
getStackOrCaptureRegionForDeclContext(LC, DC, D);
|
||||||
|
|
||||||
if (V.is<const VarRegion*>())
|
if (V.is<const VarRegion*>())
|
||||||
return V.get<const VarRegion*>();
|
return V.get<const VarRegion*>();
|
||||||
|
|
||||||
const StackFrameContext *STC = V.get<const StackFrameContext*>();
|
const StackFrameContext *STC = V.get<const StackFrameContext*>();
|
||||||
|
|
||||||
if (!STC)
|
if (!STC)
|
||||||
|
@ -1239,7 +1239,7 @@ RegionOffset MemRegion::getAsOffset() const {
|
||||||
Ty = SR->getSymbol()->getType()->getPointeeType();
|
Ty = SR->getSymbol()->getType()->getPointeeType();
|
||||||
RootIsSymbolic = true;
|
RootIsSymbolic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl();
|
const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl();
|
||||||
if (!Child) {
|
if (!Child) {
|
||||||
// We cannot compute the offset of the base class.
|
// We cannot compute the offset of the base class.
|
||||||
|
@ -1290,7 +1290,7 @@ RegionOffset MemRegion::getAsOffset() const {
|
||||||
if (Optional<nonloc::ConcreteInt> CI =
|
if (Optional<nonloc::ConcreteInt> CI =
|
||||||
Index.getAs<nonloc::ConcreteInt>()) {
|
Index.getAs<nonloc::ConcreteInt>()) {
|
||||||
// Don't bother calculating precise offsets if we already have a
|
// Don't bother calculating precise offsets if we already have a
|
||||||
// symbolic offset somewhere in the chain.
|
// symbolic offset somewhere in the chain.
|
||||||
if (SymbolicOffsetBase)
|
if (SymbolicOffsetBase)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1324,7 +1324,7 @@ RegionOffset MemRegion::getAsOffset() const {
|
||||||
|
|
||||||
// Get the field number.
|
// Get the field number.
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
for (RecordDecl::field_iterator FI = RD->field_begin(),
|
for (RecordDecl::field_iterator FI = RD->field_begin(),
|
||||||
FE = RD->field_end(); FI != FE; ++FI, ++idx)
|
FE = RD->field_end(); FI != FE; ++FI, ++idx)
|
||||||
if (FR->getDecl() == *FI)
|
if (FR->getDecl() == *FI)
|
||||||
break;
|
break;
|
||||||
|
@ -1420,7 +1420,7 @@ BlockDataRegion::referenced_vars_begin() const {
|
||||||
|
|
||||||
BumpVector<const MemRegion*> *VecOriginal =
|
BumpVector<const MemRegion*> *VecOriginal =
|
||||||
static_cast<BumpVector<const MemRegion*>*>(OriginalVars);
|
static_cast<BumpVector<const MemRegion*>*>(OriginalVars);
|
||||||
|
|
||||||
return BlockDataRegion::referenced_vars_iterator(Vec->begin(),
|
return BlockDataRegion::referenced_vars_iterator(Vec->begin(),
|
||||||
VecOriginal->begin());
|
VecOriginal->begin());
|
||||||
}
|
}
|
||||||
|
@ -1456,12 +1456,12 @@ const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const {
|
||||||
// RegionAndSymbolInvalidationTraits
|
// RegionAndSymbolInvalidationTraits
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym,
|
void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym,
|
||||||
InvalidationKinds IK) {
|
InvalidationKinds IK) {
|
||||||
SymTraitsMap[Sym] |= IK;
|
SymTraitsMap[Sym] |= IK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR,
|
void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR,
|
||||||
InvalidationKinds IK) {
|
InvalidationKinds IK) {
|
||||||
assert(MR);
|
assert(MR);
|
||||||
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
|
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
|
||||||
|
@ -1470,13 +1470,13 @@ void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR,
|
||||||
MRTraitsMap[MR] |= IK;
|
MRTraitsMap[MR] |= IK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym,
|
bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym,
|
||||||
InvalidationKinds IK) {
|
InvalidationKinds IK) {
|
||||||
const_symbol_iterator I = SymTraitsMap.find(Sym);
|
const_symbol_iterator I = SymTraitsMap.find(Sym);
|
||||||
if (I != SymTraitsMap.end())
|
if (I != SymTraitsMap.end())
|
||||||
return I->second & IK;
|
return I->second & IK;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR,
|
bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR,
|
||||||
|
|
|
@ -179,7 +179,7 @@ void PathDiagnostic::resetDiagnosticLocationToMainFile() {
|
||||||
// Reset the report containing declaration and location.
|
// Reset the report containing declaration and location.
|
||||||
DeclWithIssue = CP->getCaller();
|
DeclWithIssue = CP->getCaller();
|
||||||
Loc = CP->getLocation();
|
Loc = CP->getLocation();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(
|
||||||
std::unique_ptr<PathDiagnostic> D) {
|
std::unique_ptr<PathDiagnostic> D) {
|
||||||
if (!D || D->path.empty())
|
if (!D || D->path.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We need to flatten the locations (convert Stmt* to locations) because
|
// We need to flatten the locations (convert Stmt* to locations) because
|
||||||
// the referenced statements may be freed by the time the diagnostics
|
// the referenced statements may be freed by the time the diagnostics
|
||||||
// are emitted.
|
// are emitted.
|
||||||
|
@ -221,12 +221,12 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(
|
||||||
++I) {
|
++I) {
|
||||||
const PathDiagnosticPiece *piece = I->get();
|
const PathDiagnosticPiece *piece = I->get();
|
||||||
FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc();
|
FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc();
|
||||||
|
|
||||||
if (FID.isInvalid()) {
|
if (FID.isInvalid()) {
|
||||||
FID = SMgr.getFileID(L);
|
FID = SMgr.getFileID(L);
|
||||||
} else if (SMgr.getFileID(L) != FID)
|
} else if (SMgr.getFileID(L) != FID)
|
||||||
return; // FIXME: Emit a warning?
|
return; // FIXME: Emit a warning?
|
||||||
|
|
||||||
// Check the source ranges.
|
// Check the source ranges.
|
||||||
ArrayRef<SourceRange> Ranges = piece->getRanges();
|
ArrayRef<SourceRange> Ranges = piece->getRanges();
|
||||||
for (ArrayRef<SourceRange>::iterator I = Ranges.begin(),
|
for (ArrayRef<SourceRange>::iterator I = Ranges.begin(),
|
||||||
|
@ -238,7 +238,7 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(
|
||||||
if (!L.isFileID() || SMgr.getFileID(L) != FID)
|
if (!L.isFileID() || SMgr.getFileID(L) != FID)
|
||||||
return; // FIXME: Emit a warning?
|
return; // FIXME: Emit a warning?
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const PathDiagnosticCallPiece *call =
|
if (const PathDiagnosticCallPiece *call =
|
||||||
dyn_cast<PathDiagnosticCallPiece>(piece)) {
|
dyn_cast<PathDiagnosticCallPiece>(piece)) {
|
||||||
WorkList.push_back(&call->path);
|
WorkList.push_back(&call->path);
|
||||||
|
@ -249,10 +249,10 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FID.isInvalid())
|
if (FID.isInvalid())
|
||||||
return; // FIXME: Emit a warning?
|
return; // FIXME: Emit a warning?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Profile the node to see if we already have something matching it
|
// Profile the node to see if we already have something matching it
|
||||||
llvm::FoldingSetNodeID profile;
|
llvm::FoldingSetNodeID profile;
|
||||||
|
@ -318,7 +318,7 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X,
|
||||||
const PathDiagnosticPiece &Y) {
|
const PathDiagnosticPiece &Y) {
|
||||||
if (X.getKind() != Y.getKind())
|
if (X.getKind() != Y.getKind())
|
||||||
return X.getKind() < Y.getKind();
|
return X.getKind() < Y.getKind();
|
||||||
|
|
||||||
FullSourceLoc XL = X.getLocation().asLocation();
|
FullSourceLoc XL = X.getLocation().asLocation();
|
||||||
FullSourceLoc YL = Y.getLocation().asLocation();
|
FullSourceLoc YL = Y.getLocation().asLocation();
|
||||||
if (XL != YL)
|
if (XL != YL)
|
||||||
|
@ -331,7 +331,7 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X,
|
||||||
return X.getRanges().size() < Y.getRanges().size();
|
return X.getRanges().size() < Y.getRanges().size();
|
||||||
|
|
||||||
const SourceManager &SM = XL.getManager();
|
const SourceManager &SM = XL.getManager();
|
||||||
|
|
||||||
for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) {
|
for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) {
|
||||||
SourceRange XR = X.getRanges()[i];
|
SourceRange XR = X.getRanges()[i];
|
||||||
SourceRange YR = Y.getRanges()[i];
|
SourceRange YR = Y.getRanges()[i];
|
||||||
|
@ -341,7 +341,7 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X,
|
||||||
return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd());
|
return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (X.getKind()) {
|
switch (X.getKind()) {
|
||||||
case clang::ento::PathDiagnosticPiece::ControlFlow:
|
case clang::ento::PathDiagnosticPiece::ControlFlow:
|
||||||
return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X),
|
return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X),
|
||||||
|
@ -418,9 +418,9 @@ void PathDiagnosticConsumer::FlushDiagnostics(
|
||||||
PathDiagnosticConsumer::FilesMade *Files) {
|
PathDiagnosticConsumer::FilesMade *Files) {
|
||||||
if (flushed)
|
if (flushed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
flushed = true;
|
flushed = true;
|
||||||
|
|
||||||
std::vector<const PathDiagnostic *> BatchDiags;
|
std::vector<const PathDiagnostic *> BatchDiags;
|
||||||
for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(),
|
for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(),
|
||||||
et = Diags.end(); it != et; ++it) {
|
et = Diags.end(); it != et; ++it) {
|
||||||
|
@ -448,7 +448,7 @@ void PathDiagnosticConsumer::FlushDiagnostics(
|
||||||
const PathDiagnostic *D = *it;
|
const PathDiagnostic *D = *it;
|
||||||
delete D;
|
delete D;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear out the FoldingSet.
|
// Clear out the FoldingSet.
|
||||||
Diags.clear();
|
Diags.clear();
|
||||||
}
|
}
|
||||||
|
@ -470,7 +470,7 @@ void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD,
|
||||||
Entry = new (Entry) PDFileEntry(NodeID);
|
Entry = new (Entry) PDFileEntry(NodeID);
|
||||||
Set.InsertNode(Entry, InsertPos);
|
Set.InsertNode(Entry, InsertPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate persistent storage for the file name.
|
// Allocate persistent storage for the file name.
|
||||||
char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1);
|
char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1);
|
||||||
memcpy(FileName_cstr, FileName.data(), FileName.size());
|
memcpy(FileName_cstr, FileName.data(), FileName.size());
|
||||||
|
@ -845,7 +845,7 @@ PathDiagnosticRange
|
||||||
SourceRange R = S->getSourceRange();
|
SourceRange R = S->getSourceRange();
|
||||||
if (R.isValid())
|
if (R.isValid())
|
||||||
return R;
|
return R;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DeclK:
|
case DeclK:
|
||||||
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
|
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
|
||||||
|
@ -947,7 +947,7 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D,
|
||||||
|
|
||||||
Out << "constructor";
|
Out << "constructor";
|
||||||
describeClass(Out, MD->getParent(), " for ");
|
describeClass(Out, MD->getParent(), " for ");
|
||||||
|
|
||||||
} else if (isa<CXXDestructorDecl>(MD)) {
|
} else if (isa<CXXDestructorDecl>(MD)) {
|
||||||
if (!MD->isUserProvided()) {
|
if (!MD->isUserProvided()) {
|
||||||
Out << "destructor";
|
Out << "destructor";
|
||||||
|
@ -1039,7 +1039,7 @@ static void compute_path_size(const PathPieces &pieces, unsigned &size) {
|
||||||
for (PathPieces::const_iterator it = pieces.begin(),
|
for (PathPieces::const_iterator it = pieces.begin(),
|
||||||
et = pieces.end(); it != et; ++it) {
|
et = pieces.end(); it != et; ++it) {
|
||||||
const PathDiagnosticPiece *piece = it->get();
|
const PathDiagnosticPiece *piece = it->get();
|
||||||
if (const PathDiagnosticCallPiece *cp =
|
if (const PathDiagnosticCallPiece *cp =
|
||||||
dyn_cast<PathDiagnosticCallPiece>(piece)) {
|
dyn_cast<PathDiagnosticCallPiece>(piece)) {
|
||||||
compute_path_size(cp->path, size);
|
compute_path_size(cp->path, size);
|
||||||
}
|
}
|
||||||
|
@ -1075,12 +1075,12 @@ void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
|
||||||
I != E; ++I) {
|
I != E; ++I) {
|
||||||
ID.AddInteger(I->getBegin().getRawEncoding());
|
ID.AddInteger(I->getBegin().getRawEncoding());
|
||||||
ID.AddInteger(I->getEnd().getRawEncoding());
|
ID.AddInteger(I->getEnd().getRawEncoding());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const {
|
void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const {
|
||||||
PathDiagnosticPiece::Profile(ID);
|
PathDiagnosticPiece::Profile(ID);
|
||||||
for (PathPieces::const_iterator it = path.begin(),
|
for (PathPieces::const_iterator it = path.begin(),
|
||||||
et = path.end(); it != et; ++it) {
|
et = path.end(); it != et; ++it) {
|
||||||
ID.Add(**it);
|
ID.Add(**it);
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,7 +171,7 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
|
||||||
--indent;
|
--indent;
|
||||||
Indent(o, indent) << "</array>\n";
|
Indent(o, indent) << "</array>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output the call depth.
|
// Output the call depth.
|
||||||
Indent(o, indent) << "<key>depth</key>";
|
Indent(o, indent) << "<key>depth</key>";
|
||||||
EmitInteger(o, depth) << '\n';
|
EmitInteger(o, depth) << '\n';
|
||||||
|
@ -187,7 +187,7 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
|
||||||
Indent(o, indent) << "<key>message</key>\n";
|
Indent(o, indent) << "<key>message</key>\n";
|
||||||
Indent(o, indent);
|
Indent(o, indent);
|
||||||
EmitString(o, P.getString()) << '\n';
|
EmitString(o, P.getString()) << '\n';
|
||||||
|
|
||||||
// Finish up.
|
// Finish up.
|
||||||
--indent;
|
--indent;
|
||||||
Indent(o, indent); o << "</dict>\n";
|
Indent(o, indent); o << "</dict>\n";
|
||||||
|
@ -208,9 +208,9 @@ static void ReportCall(raw_ostream &o,
|
||||||
const LangOptions &LangOpts,
|
const LangOptions &LangOpts,
|
||||||
unsigned indent,
|
unsigned indent,
|
||||||
unsigned depth) {
|
unsigned depth) {
|
||||||
|
|
||||||
IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter =
|
IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter =
|
||||||
P.getCallEnterEvent();
|
P.getCallEnterEvent();
|
||||||
|
|
||||||
if (callEnter)
|
if (callEnter)
|
||||||
ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true,
|
ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true,
|
||||||
|
@ -218,18 +218,18 @@ static void ReportCall(raw_ostream &o,
|
||||||
|
|
||||||
IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller =
|
IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller =
|
||||||
P.getCallEnterWithinCallerEvent();
|
P.getCallEnterWithinCallerEvent();
|
||||||
|
|
||||||
++depth;
|
++depth;
|
||||||
|
|
||||||
if (callEnterWithinCaller)
|
if (callEnterWithinCaller)
|
||||||
ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts,
|
ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts,
|
||||||
indent, depth, true);
|
indent, depth, true);
|
||||||
|
|
||||||
for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I)
|
for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I)
|
||||||
ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true);
|
ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true);
|
||||||
|
|
||||||
--depth;
|
--depth;
|
||||||
|
|
||||||
IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit =
|
IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit =
|
||||||
P.getCallExitEvent();
|
P.getCallExitEvent();
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
|
||||||
if (!Diags.empty())
|
if (!Diags.empty())
|
||||||
SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager();
|
SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager();
|
||||||
|
|
||||||
|
|
||||||
for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(),
|
for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(),
|
||||||
DE = Diags.end(); DI != DE; ++DI) {
|
DE = Diags.end(); DI != DE; ++DI) {
|
||||||
|
|
||||||
|
@ -374,7 +374,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
|
||||||
|
|
||||||
o << " <array>\n";
|
o << " <array>\n";
|
||||||
|
|
||||||
for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
|
for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
|
||||||
I != E; ++I)
|
I != E; ++I)
|
||||||
ReportDiag(o, **I, FM, *SM, LangOpts);
|
ReportDiag(o, **I, FM, *SM, LangOpts);
|
||||||
|
|
||||||
|
@ -389,7 +389,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
|
||||||
EmitString(o, D->getBugType()) << '\n';
|
EmitString(o, D->getBugType()) << '\n';
|
||||||
o << " <key>check_name</key>";
|
o << " <key>check_name</key>";
|
||||||
EmitString(o, D->getCheckName()) << '\n';
|
EmitString(o, D->getCheckName()) << '\n';
|
||||||
|
|
||||||
// Output information about the semantic context where
|
// Output information about the semantic context where
|
||||||
// the issue occurred.
|
// the issue occurred.
|
||||||
if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
|
if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
|
||||||
|
@ -423,7 +423,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
|
||||||
// Output the bug hash for issue unique-ing. Currently, it's just an
|
// Output the bug hash for issue unique-ing. Currently, it's just an
|
||||||
// offset from the beginning of the function.
|
// offset from the beginning of the function.
|
||||||
if (const Stmt *Body = DeclWithIssue->getBody()) {
|
if (const Stmt *Body = DeclWithIssue->getBody()) {
|
||||||
|
|
||||||
// If the bug uniqueing location exists, use it for the hash.
|
// If the bug uniqueing location exists, use it for the hash.
|
||||||
// For example, this ensures that two leaks reported on the same line
|
// For example, this ensures that two leaks reported on the same line
|
||||||
// will have different issue_hashes and that the hash will identify
|
// will have different issue_hashes and that the hash will identify
|
||||||
|
@ -486,5 +486,5 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
|
||||||
o << " </array>\n";
|
o << " </array>\n";
|
||||||
|
|
||||||
// Finish.
|
// Finish.
|
||||||
o << "</dict>\n</plist>";
|
o << "</dict>\n</plist>";
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ void ProgramStateRelease(const ProgramState *state) {
|
||||||
if (--s->refCount == 0) {
|
if (--s->refCount == 0) {
|
||||||
ProgramStateManager &Mgr = s->getStateManager();
|
ProgramStateManager &Mgr = s->getStateManager();
|
||||||
Mgr.StateSet.RemoveNode(s);
|
Mgr.StateSet.RemoveNode(s);
|
||||||
s->~ProgramState();
|
s->~ProgramState();
|
||||||
Mgr.freeStates.push_back(s);
|
Mgr.freeStates.push_back(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ ProgramStateManager::~ProgramStateManager() {
|
||||||
I->second.second(I->second.first);
|
I->second.second(I->second.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
ProgramStateManager::removeDeadBindings(ProgramStateRef state,
|
ProgramStateManager::removeDeadBindings(ProgramStateRef state,
|
||||||
const StackFrameContext *LCtx,
|
const StackFrameContext *LCtx,
|
||||||
SymbolReaper& SymReaper) {
|
SymbolReaper& SymReaper) {
|
||||||
|
@ -113,7 +113,7 @@ ProgramStateManager::removeDeadBindings(ProgramStateRef state,
|
||||||
|
|
||||||
ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, bool notifyChanges) const {
|
ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, bool notifyChanges) const {
|
||||||
ProgramStateManager &Mgr = getStateManager();
|
ProgramStateManager &Mgr = getStateManager();
|
||||||
ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
|
ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
|
||||||
LV, V));
|
LV, V));
|
||||||
const MemRegion *MR = LV.getAsRegion();
|
const MemRegion *MR = LV.getAsRegion();
|
||||||
if (MR && Mgr.getOwningEngine() && notifyChanges)
|
if (MR && Mgr.getOwningEngine() && notifyChanges)
|
||||||
|
@ -127,15 +127,15 @@ ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const {
|
||||||
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
|
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
|
||||||
const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V);
|
const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V);
|
||||||
ProgramStateRef new_state = makeWithStore(newStore);
|
ProgramStateRef new_state = makeWithStore(newStore);
|
||||||
return Mgr.getOwningEngine() ?
|
return Mgr.getOwningEngine() ?
|
||||||
Mgr.getOwningEngine()->processRegionChange(new_state, R) :
|
Mgr.getOwningEngine()->processRegionChange(new_state, R) :
|
||||||
new_state;
|
new_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ArrayRef<const MemRegion *> RegionList;
|
typedef ArrayRef<const MemRegion *> RegionList;
|
||||||
typedef ArrayRef<SVal> ValueList;
|
typedef ArrayRef<SVal> ValueList;
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
ProgramState::invalidateRegions(RegionList Regions,
|
ProgramState::invalidateRegions(RegionList Regions,
|
||||||
const Expr *E, unsigned Count,
|
const Expr *E, unsigned Count,
|
||||||
const LocationContext *LCtx,
|
const LocationContext *LCtx,
|
||||||
|
@ -197,11 +197,11 @@ ProgramState::invalidateRegionsImpl(ValueList Values,
|
||||||
if (CausedByPointerEscape) {
|
if (CausedByPointerEscape) {
|
||||||
newState = Eng->notifyCheckersOfPointerEscape(newState, IS,
|
newState = Eng->notifyCheckersOfPointerEscape(newState, IS,
|
||||||
TopLevelInvalidated,
|
TopLevelInvalidated,
|
||||||
Invalidated, Call,
|
Invalidated, Call,
|
||||||
*ITraits);
|
*ITraits);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Eng->processRegionChanges(newState, IS, TopLevelInvalidated,
|
return Eng->processRegionChanges(newState, IS, TopLevelInvalidated,
|
||||||
Invalidated, Call);
|
Invalidated, Call);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ ProgramStateRef ProgramState::killBinding(Loc LV) const {
|
||||||
return makeWithStore(newStore);
|
return makeWithStore(newStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
ProgramState::enterStackFrame(const CallEvent &Call,
|
ProgramState::enterStackFrame(const CallEvent &Call,
|
||||||
const StackFrameContext *CalleeCtx) const {
|
const StackFrameContext *CalleeCtx) const {
|
||||||
const StoreRef &NewStore =
|
const StoreRef &NewStore =
|
||||||
|
@ -275,7 +275,7 @@ SVal ProgramState::getSVal(Loc location, QualType T) const {
|
||||||
// symbol for the call to foo(); the type of that symbol is 'char',
|
// symbol for the call to foo(); the type of that symbol is 'char',
|
||||||
// not unsigned.
|
// not unsigned.
|
||||||
const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int);
|
const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int);
|
||||||
|
|
||||||
if (V.getAs<Loc>())
|
if (V.getAs<Loc>())
|
||||||
return loc::ConcreteInt(NewV);
|
return loc::ConcreteInt(NewV);
|
||||||
else
|
else
|
||||||
|
@ -283,7 +283,7 @@ SVal ProgramState::getSVal(Loc location, QualType T) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return V;
|
return V;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,11 +353,11 @@ ConditionTruthVal ProgramState::isNull(SVal V) const {
|
||||||
|
|
||||||
if (V.isConstant())
|
if (V.isConstant())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SymbolRef Sym = V.getAsSymbol(/* IncludeBaseRegion */ true);
|
SymbolRef Sym = V.getAsSymbol(/* IncludeBaseRegion */ true);
|
||||||
if (!Sym)
|
if (!Sym)
|
||||||
return ConditionTruthVal();
|
return ConditionTruthVal();
|
||||||
|
|
||||||
return getStateManager().ConstraintMgr->isNull(this, Sym);
|
return getStateManager().ConstraintMgr->isNull(this, Sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +390,7 @@ ProgramStateRef ProgramStateManager::getPersistentState(ProgramState &State) {
|
||||||
ProgramState *newState = nullptr;
|
ProgramState *newState = nullptr;
|
||||||
if (!freeStates.empty()) {
|
if (!freeStates.empty()) {
|
||||||
newState = freeStates.back();
|
newState = freeStates.back();
|
||||||
freeStates.pop_back();
|
freeStates.pop_back();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newState = (ProgramState*) Alloc.Allocate<ProgramState>();
|
newState = (ProgramState*) Alloc.Allocate<ProgramState>();
|
||||||
|
@ -530,10 +530,10 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) {
|
||||||
bool wasVisited = !visited.insert(sym).second;
|
bool wasVisited = !visited.insert(sym).second;
|
||||||
if (wasVisited)
|
if (wasVisited)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!visitor.VisitSymbol(sym))
|
if (!visitor.VisitSymbol(sym))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// TODO: should be rewritten using SymExpr::symbol_iterator.
|
// TODO: should be rewritten using SymExpr::symbol_iterator.
|
||||||
switch (sym->getKind()) {
|
switch (sym->getKind()) {
|
||||||
case SymExpr::RegionValueKind:
|
case SymExpr::RegionValueKind:
|
||||||
|
@ -582,11 +582,11 @@ bool ScanReachableSymbols::scan(SVal val) {
|
||||||
bool ScanReachableSymbols::scan(const MemRegion *R) {
|
bool ScanReachableSymbols::scan(const MemRegion *R) {
|
||||||
if (isa<MemSpaceRegion>(R))
|
if (isa<MemSpaceRegion>(R))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
bool wasVisited = !visited.insert(R).second;
|
bool wasVisited = !visited.insert(R).second;
|
||||||
if (wasVisited)
|
if (wasVisited)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!visitor.VisitMemRegion(R))
|
if (!visitor.VisitMemRegion(R))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -722,14 +722,14 @@ bool ProgramState::isTainted(const MemRegion *Reg, TaintTagType K) const {
|
||||||
bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const {
|
bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const {
|
||||||
if (!Sym)
|
if (!Sym)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Traverse all the symbols this symbol depends on to see if any are tainted.
|
// Traverse all the symbols this symbol depends on to see if any are tainted.
|
||||||
bool Tainted = false;
|
bool Tainted = false;
|
||||||
for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end();
|
for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end();
|
||||||
SI != SE; ++SI) {
|
SI != SE; ++SI) {
|
||||||
if (!isa<SymbolData>(*SI))
|
if (!isa<SymbolData>(*SI))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const TaintTagType *Tag = get<TaintMap>(*SI);
|
const TaintTagType *Tag = get<TaintMap>(*SI);
|
||||||
Tainted = (Tag && *Tag == Kind);
|
Tainted = (Tag && *Tag == Kind);
|
||||||
|
|
||||||
|
@ -748,7 +748,7 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const {
|
||||||
if (Tainted)
|
if (Tainted)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Tainted;
|
return Tainted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -365,7 +365,7 @@ ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State,
|
||||||
|
|
||||||
/// Scan all symbols referenced by the constraints. If the symbol is not alive
|
/// Scan all symbols referenced by the constraints. If the symbol is not alive
|
||||||
/// as marked in LSymbols, mark it as dead in DSymbols.
|
/// as marked in LSymbols, mark it as dead in DSymbols.
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RangeConstraintManager::removeDeadBindings(ProgramStateRef state,
|
RangeConstraintManager::removeDeadBindings(ProgramStateRef state,
|
||||||
SymbolReaper& SymReaper) {
|
SymbolReaper& SymReaper) {
|
||||||
|
|
||||||
|
@ -415,7 +415,7 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) {
|
||||||
// As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1,
|
// As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1,
|
||||||
// UINT_MAX, 0, 1, and 2.
|
// UINT_MAX, 0, 1, and 2.
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym,
|
RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym,
|
||||||
const llvm::APSInt &Int,
|
const llvm::APSInt &Int,
|
||||||
const llvm::APSInt &Adjustment) {
|
const llvm::APSInt &Adjustment) {
|
||||||
|
@ -435,7 +435,7 @@ RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym,
|
||||||
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym,
|
RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym,
|
||||||
const llvm::APSInt &Int,
|
const llvm::APSInt &Int,
|
||||||
const llvm::APSInt &Adjustment) {
|
const llvm::APSInt &Adjustment) {
|
||||||
|
@ -450,7 +450,7 @@ RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym,
|
||||||
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym,
|
RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym,
|
||||||
const llvm::APSInt &Int,
|
const llvm::APSInt &Int,
|
||||||
const llvm::APSInt &Adjustment) {
|
const llvm::APSInt &Adjustment) {
|
||||||
|
@ -479,7 +479,7 @@ RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym,
|
||||||
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym,
|
RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym,
|
||||||
const llvm::APSInt &Int,
|
const llvm::APSInt &Int,
|
||||||
const llvm::APSInt &Adjustment) {
|
const llvm::APSInt &Adjustment) {
|
||||||
|
@ -508,7 +508,7 @@ RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym,
|
||||||
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym,
|
RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym,
|
||||||
const llvm::APSInt &Int,
|
const llvm::APSInt &Int,
|
||||||
const llvm::APSInt &Adjustment) {
|
const llvm::APSInt &Adjustment) {
|
||||||
|
@ -537,7 +537,7 @@ RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym,
|
||||||
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramStateRef
|
ProgramStateRef
|
||||||
RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym,
|
RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym,
|
||||||
const llvm::APSInt &Int,
|
const llvm::APSInt &Int,
|
||||||
const llvm::APSInt &Adjustment) {
|
const llvm::APSInt &Adjustment) {
|
||||||
|
|
|
@ -464,9 +464,9 @@ public: // Part of public interface to class.
|
||||||
StoreRef killBinding(Store ST, Loc L) override;
|
StoreRef killBinding(Store ST, Loc L) override;
|
||||||
|
|
||||||
void incrementReferenceCount(Store store) override {
|
void incrementReferenceCount(Store store) override {
|
||||||
getRegionBindings(store).manualRetain();
|
getRegionBindings(store).manualRetain();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the StoreManager supports it, decrement the reference count of
|
/// If the StoreManager supports it, decrement the reference count of
|
||||||
/// the specified Store object. If the reference count hits 0, the memory
|
/// the specified Store object. If the reference count hits 0, the memory
|
||||||
/// associated with the object is recycled.
|
/// associated with the object is recycled.
|
||||||
|
@ -508,7 +508,7 @@ public: // Part of public interface to class.
|
||||||
SVal getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
|
SVal getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
|
||||||
const TypedValueRegion *R,
|
const TypedValueRegion *R,
|
||||||
QualType Ty);
|
QualType Ty);
|
||||||
|
|
||||||
SVal getLazyBinding(const SubRegion *LazyBindingRegion,
|
SVal getLazyBinding(const SubRegion *LazyBindingRegion,
|
||||||
RegionBindingsRef LazyBinding);
|
RegionBindingsRef LazyBinding);
|
||||||
|
|
||||||
|
@ -987,8 +987,8 @@ void invalidateRegionsWorker::VisitBinding(SVal V) {
|
||||||
void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
|
void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
|
||||||
const ClusterBindings *C) {
|
const ClusterBindings *C) {
|
||||||
|
|
||||||
bool PreserveRegionsContents =
|
bool PreserveRegionsContents =
|
||||||
ITraits.hasTrait(baseR,
|
ITraits.hasTrait(baseR,
|
||||||
RegionAndSymbolInvalidationTraits::TK_PreserveContents);
|
RegionAndSymbolInvalidationTraits::TK_PreserveContents);
|
||||||
|
|
||||||
if (C) {
|
if (C) {
|
||||||
|
@ -1456,7 +1456,7 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
|
||||||
// through to look for lazy compound value. It is like a field region.
|
// through to look for lazy compound value. It is like a field region.
|
||||||
Result = findLazyBinding(B, cast<SubRegion>(BaseReg->getSuperRegion()),
|
Result = findLazyBinding(B, cast<SubRegion>(BaseReg->getSuperRegion()),
|
||||||
originalRegion);
|
originalRegion);
|
||||||
|
|
||||||
if (Result.second)
|
if (Result.second)
|
||||||
Result.second = MRMgr.getCXXBaseObjectRegionWithSuper(BaseReg,
|
Result.second = MRMgr.getCXXBaseObjectRegionWithSuper(BaseReg,
|
||||||
Result.second);
|
Result.second);
|
||||||
|
@ -1502,7 +1502,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
|
||||||
return svalBuilder.makeIntVal(c, T);
|
return svalBuilder.makeIntVal(c, T);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for loads from a code text region. For such loads, just give up.
|
// Check for loads from a code text region. For such loads, just give up.
|
||||||
if (isa<CodeTextRegion>(superR))
|
if (isa<CodeTextRegion>(superR))
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
@ -1514,12 +1514,12 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
|
||||||
// return *y;
|
// return *y;
|
||||||
// FIXME: This is a hack, and doesn't do anything really intelligent yet.
|
// FIXME: This is a hack, and doesn't do anything really intelligent yet.
|
||||||
const RegionRawOffset &O = R->getAsArrayOffset();
|
const RegionRawOffset &O = R->getAsArrayOffset();
|
||||||
|
|
||||||
// If we cannot reason about the offset, return an unknown value.
|
// If we cannot reason about the offset, return an unknown value.
|
||||||
if (!O.getRegion())
|
if (!O.getRegion())
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
||||||
if (const TypedValueRegion *baseR =
|
if (const TypedValueRegion *baseR =
|
||||||
dyn_cast_or_null<TypedValueRegion>(O.getRegion())) {
|
dyn_cast_or_null<TypedValueRegion>(O.getRegion())) {
|
||||||
QualType baseT = baseR->getValueType();
|
QualType baseT = baseR->getValueType();
|
||||||
if (baseT->isScalarType()) {
|
if (baseT->isScalarType()) {
|
||||||
|
@ -1610,7 +1610,7 @@ SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion,
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
SVal
|
SVal
|
||||||
RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
|
RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
|
||||||
const TypedValueRegion *R,
|
const TypedValueRegion *R,
|
||||||
|
@ -1664,7 +1664,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
|
||||||
if (!index.isConstant())
|
if (!index.isConstant())
|
||||||
hasSymbolicIndex = true;
|
hasSymbolicIndex = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If our super region is a field or element itself, walk up the region
|
// If our super region is a field or element itself, walk up the region
|
||||||
// hierarchy to see if there is a default value installed in an ancestor.
|
// hierarchy to see if there is a default value installed in an ancestor.
|
||||||
SR = dyn_cast<SubRegion>(Base);
|
SR = dyn_cast<SubRegion>(Base);
|
||||||
|
@ -1674,7 +1674,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
|
||||||
if (isa<ElementRegion>(R)) {
|
if (isa<ElementRegion>(R)) {
|
||||||
// Currently we don't reason specially about Clang-style vectors. Check
|
// Currently we don't reason specially about Clang-style vectors. Check
|
||||||
// if superR is a vector and if so return Unknown.
|
// if superR is a vector and if so return Unknown.
|
||||||
if (const TypedValueRegion *typedSuperR =
|
if (const TypedValueRegion *typedSuperR =
|
||||||
dyn_cast<TypedValueRegion>(R->getSuperRegion())) {
|
dyn_cast<TypedValueRegion>(R->getSuperRegion())) {
|
||||||
if (typedSuperR->getValueType()->isVectorType())
|
if (typedSuperR->getValueType()->isVectorType())
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
@ -1801,7 +1801,7 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) {
|
||||||
List.insert(List.end(), InnerList.begin(), InnerList.end());
|
List.insert(List.end(), InnerList.begin(), InnerList.end());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
List.push_back(V);
|
List.push_back(V);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1838,7 +1838,7 @@ SVal RegionStoreManager::getBindingForArray(RegionBindingsConstRef B,
|
||||||
const TypedValueRegion *R) {
|
const TypedValueRegion *R) {
|
||||||
assert(Ctx.getAsConstantArrayType(R->getValueType()) &&
|
assert(Ctx.getAsConstantArrayType(R->getValueType()) &&
|
||||||
"Only constant array types can have compound bindings.");
|
"Only constant array types can have compound bindings.");
|
||||||
|
|
||||||
return createLazyBinding(B, R);
|
return createLazyBinding(B, R);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2012,11 +2012,11 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
|
||||||
QualType T = R->getValueType();
|
QualType T = R->getValueType();
|
||||||
assert(T->isVectorType());
|
assert(T->isVectorType());
|
||||||
const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs.
|
const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs.
|
||||||
|
|
||||||
// Handle lazy compound values and symbolic values.
|
// Handle lazy compound values and symbolic values.
|
||||||
if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>())
|
if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>())
|
||||||
return bindAggregate(B, R, V);
|
return bindAggregate(B, R, V);
|
||||||
|
|
||||||
// We may get non-CompoundVal accidentally due to imprecise cast logic or
|
// We may get non-CompoundVal accidentally due to imprecise cast logic or
|
||||||
// that we are binding symbolic struct value. Kill the field values, and if
|
// that we are binding symbolic struct value. Kill the field values, and if
|
||||||
// the value is symbolic go and bind it as a "default" binding.
|
// the value is symbolic go and bind it as a "default" binding.
|
||||||
|
@ -2033,7 +2033,7 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
|
||||||
for ( ; index != numElements ; ++index) {
|
for ( ; index != numElements ; ++index) {
|
||||||
if (VI == VE)
|
if (VI == VE)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
NonLoc Idx = svalBuilder.makeArrayIndex(index);
|
NonLoc Idx = svalBuilder.makeArrayIndex(index);
|
||||||
const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx);
|
const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx);
|
||||||
|
|
||||||
|
@ -2075,7 +2075,7 @@ RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B,
|
||||||
}
|
}
|
||||||
|
|
||||||
RegionBindingsRef NewB = B;
|
RegionBindingsRef NewB = B;
|
||||||
|
|
||||||
for (FieldVector::iterator I = Fields.begin(), E = Fields.end(); I != E; ++I){
|
for (FieldVector::iterator I = Fields.begin(), E = Fields.end(); I != E; ++I){
|
||||||
const FieldRegion *SourceFR = MRMgr.getFieldRegion(*I, LCV.getRegion());
|
const FieldRegion *SourceFR = MRMgr.getFieldRegion(*I, LCV.getRegion());
|
||||||
SVal V = getBindingForField(getRegionBindings(LCV.getStore()), SourceFR);
|
SVal V = getBindingForField(getRegionBindings(LCV.getStore()), SourceFR);
|
||||||
|
@ -2259,7 +2259,7 @@ void removeDeadBindingsWorker::VisitBinding(SVal V) {
|
||||||
// If V is a region, then add it to the worklist.
|
// If V is a region, then add it to the worklist.
|
||||||
if (const MemRegion *R = V.getAsRegion()) {
|
if (const MemRegion *R = V.getAsRegion()) {
|
||||||
AddToWorkList(R);
|
AddToWorkList(R);
|
||||||
|
|
||||||
// All regions captured by a block are also live.
|
// All regions captured by a block are also live.
|
||||||
if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
|
if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
|
||||||
BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(),
|
BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(),
|
||||||
|
@ -2268,7 +2268,7 @@ void removeDeadBindingsWorker::VisitBinding(SVal V) {
|
||||||
AddToWorkList(I.getCapturedRegion());
|
AddToWorkList(I.getCapturedRegion());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update the set of live symbols.
|
// Update the set of live symbols.
|
||||||
for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end();
|
for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end();
|
||||||
|
|
|
@ -91,7 +91,7 @@ nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){
|
||||||
return makeTruthVal(boolean->getValue());
|
return makeTruthVal(boolean->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
DefinedOrUnknownSVal
|
DefinedOrUnknownSVal
|
||||||
SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) {
|
SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) {
|
||||||
QualType T = region->getValueType();
|
QualType T = region->getValueType();
|
||||||
|
|
||||||
|
@ -146,10 +146,10 @@ DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const Stmt *stmt,
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
||||||
SymbolRef sym = SymMgr.conjureSymbol(stmt, LCtx, type, visitCount);
|
SymbolRef sym = SymMgr.conjureSymbol(stmt, LCtx, type, visitCount);
|
||||||
|
|
||||||
if (Loc::isLocType(type))
|
if (Loc::isLocType(type))
|
||||||
return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
|
return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
|
||||||
|
|
||||||
return nonloc::SymbolVal(sym);
|
return nonloc::SymbolVal(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +307,7 @@ SVal SValBuilder::makeSymExprValNN(ProgramStateRef State,
|
||||||
QualType ResultTy) {
|
QualType ResultTy) {
|
||||||
if (!State->isTainted(RHS) && !State->isTainted(LHS))
|
if (!State->isTainted(RHS) && !State->isTainted(LHS))
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
||||||
const SymExpr *symLHS = LHS.getAsSymExpr();
|
const SymExpr *symLHS = LHS.getAsSymExpr();
|
||||||
const SymExpr *symRHS = RHS.getAsSymExpr();
|
const SymExpr *symRHS = RHS.getAsSymExpr();
|
||||||
// TODO: When the Max Complexity is reached, we should conjure a symbol
|
// TODO: When the Max Complexity is reached, we should conjure a symbol
|
||||||
|
@ -430,7 +430,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
|
||||||
if (shouldBeModeledWithNoOp(Context, Context.getPointerType(castTy),
|
if (shouldBeModeledWithNoOp(Context, Context.getPointerType(castTy),
|
||||||
Context.getPointerType(originalTy)))
|
Context.getPointerType(originalTy)))
|
||||||
return val;
|
return val;
|
||||||
|
|
||||||
// Check for casts from pointers to integers.
|
// Check for casts from pointers to integers.
|
||||||
if (castTy->isIntegralOrEnumerationType() && Loc::isLocType(originalTy))
|
if (castTy->isIntegralOrEnumerationType() && Loc::isLocType(originalTy))
|
||||||
return evalCastFromLoc(val.castAs<Loc>(), castTy);
|
return evalCastFromLoc(val.castAs<Loc>(), castTy);
|
||||||
|
|
|
@ -638,7 +638,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
|
||||||
// on the ABI).
|
// on the ABI).
|
||||||
// FIXME: we can probably do a comparison against other MemRegions, though.
|
// FIXME: we can probably do a comparison against other MemRegions, though.
|
||||||
// FIXME: is there a way to tell if two labels refer to the same location?
|
// FIXME: is there a way to tell if two labels refer to the same location?
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
||||||
case loc::ConcreteIntKind: {
|
case loc::ConcreteIntKind: {
|
||||||
// If one of the operands is a symbol and the other is a constant,
|
// If one of the operands is a symbol and the other is a constant,
|
||||||
|
@ -863,7 +863,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
|
||||||
// Special case: rhs is a zero constant.
|
// Special case: rhs is a zero constant.
|
||||||
if (rhs.isZeroConstant())
|
if (rhs.isZeroConstant())
|
||||||
return lhs;
|
return lhs;
|
||||||
|
|
||||||
// We are dealing with pointer arithmetic.
|
// We are dealing with pointer arithmetic.
|
||||||
|
|
||||||
// Handle pointer arithmetic on constant values.
|
// Handle pointer arithmetic on constant values.
|
||||||
|
@ -880,7 +880,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
|
||||||
// Offset the increment by the pointer size.
|
// Offset the increment by the pointer size.
|
||||||
llvm::APSInt Multiplicand(rightI.getBitWidth(), /* isUnsigned */ true);
|
llvm::APSInt Multiplicand(rightI.getBitWidth(), /* isUnsigned */ true);
|
||||||
rightI *= Multiplicand;
|
rightI *= Multiplicand;
|
||||||
|
|
||||||
// Compute the adjusted pointer.
|
// Compute the adjusted pointer.
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case BO_Add:
|
case BO_Add:
|
||||||
|
@ -922,7 +922,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
|
||||||
superR, getContext()));
|
superR, getContext()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
}
|
}
|
||||||
|
|
||||||
const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state,
|
const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state,
|
||||||
|
|
|
@ -52,7 +52,7 @@ StoreRef StoreManager::BindDefault(Store store, const MemRegion *R, SVal V) {
|
||||||
return StoreRef(store, *this);
|
return StoreRef(store, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ElementRegion *StoreManager::GetElementZeroRegion(const MemRegion *R,
|
const ElementRegion *StoreManager::GetElementZeroRegion(const MemRegion *R,
|
||||||
QualType T) {
|
QualType T) {
|
||||||
NonLoc idx = svalBuilder.makeZeroArrayIndex();
|
NonLoc idx = svalBuilder.makeZeroArrayIndex();
|
||||||
assert(!T.isNull());
|
assert(!T.isNull());
|
||||||
|
@ -366,22 +366,22 @@ SVal StoreManager::evalDynamicCast(SVal Base, QualType TargetType,
|
||||||
/// as another region.
|
/// as another region.
|
||||||
SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R,
|
SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R,
|
||||||
QualType castTy, bool performTestOnly) {
|
QualType castTy, bool performTestOnly) {
|
||||||
|
|
||||||
if (castTy.isNull() || V.isUnknownOrUndef())
|
if (castTy.isNull() || V.isUnknownOrUndef())
|
||||||
return V;
|
return V;
|
||||||
|
|
||||||
ASTContext &Ctx = svalBuilder.getContext();
|
ASTContext &Ctx = svalBuilder.getContext();
|
||||||
|
|
||||||
if (performTestOnly) {
|
if (performTestOnly) {
|
||||||
// Automatically translate references to pointers.
|
// Automatically translate references to pointers.
|
||||||
QualType T = R->getValueType();
|
QualType T = R->getValueType();
|
||||||
if (const ReferenceType *RT = T->getAs<ReferenceType>())
|
if (const ReferenceType *RT = T->getAs<ReferenceType>())
|
||||||
T = Ctx.getPointerType(RT->getPointeeType());
|
T = Ctx.getPointerType(RT->getPointeeType());
|
||||||
|
|
||||||
assert(svalBuilder.getContext().hasSameUnqualifiedType(castTy, T));
|
assert(svalBuilder.getContext().hasSameUnqualifiedType(castTy, T));
|
||||||
return V;
|
return V;
|
||||||
}
|
}
|
||||||
|
|
||||||
return svalBuilder.dispatchCast(V, castTy);
|
return svalBuilder.dispatchCast(V, castTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,7 +424,7 @@ SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) {
|
||||||
return getLValueFieldOrIvar(decl, base);
|
return getLValueFieldOrIvar(decl, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
|
SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
|
||||||
SVal Base) {
|
SVal Base) {
|
||||||
|
|
||||||
// If the base is an unknown or undefined value, just return it back.
|
// If the base is an unknown or undefined value, just return it back.
|
||||||
|
|
|
@ -409,7 +409,7 @@ bool SymbolReaper::maybeDead(SymbolRef sym) {
|
||||||
bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
|
bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
|
||||||
if (RegionRoots.count(MR))
|
if (RegionRoots.count(MR))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
MR = MR->getBaseRegion();
|
MR = MR->getBaseRegion();
|
||||||
|
|
||||||
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
|
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
|
||||||
|
@ -442,9 +442,9 @@ bool SymbolReaper::isLive(SymbolRef sym) {
|
||||||
markDependentsLive(sym);
|
markDependentsLive(sym);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KnownLive;
|
bool KnownLive;
|
||||||
|
|
||||||
switch (sym->getKind()) {
|
switch (sym->getKind()) {
|
||||||
case SymExpr::RegionValueKind:
|
case SymExpr::RegionValueKind:
|
||||||
KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion());
|
KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion());
|
||||||
|
@ -525,7 +525,7 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
|
||||||
|
|
||||||
if (!includeStoreBindings)
|
if (!includeStoreBindings)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
unsigned &cachedQuery =
|
unsigned &cachedQuery =
|
||||||
const_cast<SymbolReaper*>(this)->includedRegionCache[VR];
|
const_cast<SymbolReaper*>(this)->includedRegionCache[VR];
|
||||||
|
|
||||||
|
@ -535,12 +535,12 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
|
||||||
|
|
||||||
// Query the store to see if the region occurs in any live bindings.
|
// Query the store to see if the region occurs in any live bindings.
|
||||||
if (Store store = reapedStore.getStore()) {
|
if (Store store = reapedStore.getStore()) {
|
||||||
bool hasRegion =
|
bool hasRegion =
|
||||||
reapedStore.getStoreManager().includedInBindings(store, VR);
|
reapedStore.getStoreManager().includedInBindings(store, VR);
|
||||||
cachedQuery = hasRegion ? 1 : 2;
|
cachedQuery = hasRegion ? 1 : 2;
|
||||||
return hasRegion;
|
return hasRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,7 @@ public:
|
||||||
/// The local declaration to all declarations ratio might be very small when
|
/// The local declaration to all declarations ratio might be very small when
|
||||||
/// working with a PCH file.
|
/// working with a PCH file.
|
||||||
SetOfDecls LocalTUDecls;
|
SetOfDecls LocalTUDecls;
|
||||||
|
|
||||||
// Set of PathDiagnosticConsumers. Owned by AnalysisManager.
|
// Set of PathDiagnosticConsumers. Owned by AnalysisManager.
|
||||||
PathDiagnosticConsumers PathConsumers;
|
PathDiagnosticConsumers PathConsumers;
|
||||||
|
|
||||||
|
@ -364,7 +364,7 @@ public:
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VisitBlockDecl(BlockDecl *BD) {
|
bool VisitBlockDecl(BlockDecl *BD) {
|
||||||
if (BD->hasBody()) {
|
if (BD->hasBody()) {
|
||||||
assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
|
assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
|
||||||
|
@ -475,7 +475,7 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
|
||||||
|
|
||||||
CallGraphNode *N = *I;
|
CallGraphNode *N = *I;
|
||||||
Decl *D = N->getDecl();
|
Decl *D = N->getDecl();
|
||||||
|
|
||||||
// Skip the abstract root node.
|
// Skip the abstract root node.
|
||||||
if (!D)
|
if (!D)
|
||||||
continue;
|
continue;
|
||||||
|
@ -679,11 +679,11 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
|
||||||
case LangOptions::NonGC:
|
case LangOptions::NonGC:
|
||||||
ActionExprEngine(D, false, IMode, Visited);
|
ActionExprEngine(D, false, IMode, Visited);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LangOptions::GCOnly:
|
case LangOptions::GCOnly:
|
||||||
ActionExprEngine(D, true, IMode, Visited);
|
ActionExprEngine(D, true, IMode, Visited);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LangOptions::HybridGC:
|
case LangOptions::HybridGC:
|
||||||
ActionExprEngine(D, false, IMode, Visited);
|
ActionExprEngine(D, false, IMode, Visited);
|
||||||
ActionExprEngine(D, true, IMode, Visited);
|
ActionExprEngine(D, true, IMode, Visited);
|
||||||
|
|
|
@ -42,7 +42,7 @@ public:
|
||||||
ClangCheckerRegistry(ArrayRef<std::string> plugins,
|
ClangCheckerRegistry(ArrayRef<std::string> plugins,
|
||||||
DiagnosticsEngine *diags = nullptr);
|
DiagnosticsEngine *diags = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
ClangCheckerRegistry::ClangCheckerRegistry(ArrayRef<std::string> plugins,
|
ClangCheckerRegistry::ClangCheckerRegistry(ArrayRef<std::string> plugins,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
##===- clang/lib/StaticAnalyzer/Frontend/Makefile ----------*- Makefile -*-===##
|
##===- clang/lib/StaticAnalyzer/Frontend/Makefile ----------*- Makefile -*-===##
|
||||||
#
|
#
|
||||||
# The LLVM Compiler Infrastructure
|
# The LLVM Compiler Infrastructure
|
||||||
#
|
#
|
||||||
# This file is distributed under the University of Illinois Open Source
|
# This file is distributed under the University of Illinois Open Source
|
||||||
# License. See LICENSE.TXT for details.
|
# License. See LICENSE.TXT for details.
|
||||||
#
|
#
|
||||||
##===----------------------------------------------------------------------===##
|
##===----------------------------------------------------------------------===##
|
||||||
#
|
#
|
||||||
# Starting point into the static analyzer land for the driver.
|
# Starting point into the static analyzer land for the driver.
|
||||||
|
|
|
@ -282,7 +282,7 @@ sub Analyze {
|
||||||
# We save the output file in the 'crashes' directory if clang encounters
|
# We save the output file in the 'crashes' directory if clang encounters
|
||||||
# any problems with the file.
|
# any problems with the file.
|
||||||
my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir);
|
my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir);
|
||||||
|
|
||||||
my $OutputStream = silent_system($HtmlDir, $Cmd, @CmdArgs);
|
my $OutputStream = silent_system($HtmlDir, $Cmd, @CmdArgs);
|
||||||
while ( <$OutputStream> ) {
|
while ( <$OutputStream> ) {
|
||||||
print $ofh $_;
|
print $ofh $_;
|
||||||
|
|
|
@ -1400,7 +1400,7 @@ my $ForceDisplayHelp = 0;
|
||||||
|
|
||||||
sub ProcessArgs {
|
sub ProcessArgs {
|
||||||
my $Args = shift;
|
my $Args = shift;
|
||||||
|
|
||||||
while (@$Args) {
|
while (@$Args) {
|
||||||
|
|
||||||
# Scan for options we recognize.
|
# Scan for options we recognize.
|
||||||
|
|
|
@ -5,7 +5,7 @@ CmpRuns - A simple tool for comparing two static analyzer runs to determine
|
||||||
which reports have been added, removed, or changed.
|
which reports have been added, removed, or changed.
|
||||||
|
|
||||||
This is designed to support automated testing using the static analyzer, from
|
This is designed to support automated testing using the static analyzer, from
|
||||||
two perspectives:
|
two perspectives:
|
||||||
1. To monitor changes in the static analyzer's reports on real code bases, for
|
1. To monitor changes in the static analyzer's reports on real code bases, for
|
||||||
regression testing.
|
regression testing.
|
||||||
|
|
||||||
|
@ -19,11 +19,11 @@ Usage:
|
||||||
#
|
#
|
||||||
resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty)
|
resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty)
|
||||||
resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty)
|
resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty)
|
||||||
|
|
||||||
# Generate a relation from diagnostics in run A to diagnostics in run B
|
# Generate a relation from diagnostics in run A to diagnostics in run B
|
||||||
# to obtain a list of triples (a, b, confidence).
|
# to obtain a list of triples (a, b, confidence).
|
||||||
diff = compareResults(resultsA, resultsB)
|
diff = compareResults(resultsA, resultsB)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -32,7 +32,7 @@ import CmpRuns
|
||||||
|
|
||||||
# Information about analysis run:
|
# Information about analysis run:
|
||||||
# path - the analysis output directory
|
# path - the analysis output directory
|
||||||
# root - the name of the root directory, which will be disregarded when
|
# root - the name of the root directory, which will be disregarded when
|
||||||
# determining the source file name
|
# determining the source file name
|
||||||
class SingleRunInfo:
|
class SingleRunInfo:
|
||||||
def __init__(self, path, root="", verboseLog=None):
|
def __init__(self, path, root="", verboseLog=None):
|
||||||
|
@ -56,7 +56,7 @@ class AnalysisDiagnostic:
|
||||||
|
|
||||||
def getLine(self):
|
def getLine(self):
|
||||||
return self._loc['line']
|
return self._loc['line']
|
||||||
|
|
||||||
def getColumn(self):
|
def getColumn(self):
|
||||||
return self._loc['col']
|
return self._loc['col']
|
||||||
|
|
||||||
|
@ -80,12 +80,12 @@ class AnalysisDiagnostic:
|
||||||
return os.path.join(self._report.run.path, self._htmlReport)
|
return os.path.join(self._report.run.path, self._htmlReport)
|
||||||
|
|
||||||
def getReadableName(self):
|
def getReadableName(self):
|
||||||
return '%s:%d:%d, %s: %s' % (self.getFileName(), self.getLine(),
|
return '%s:%d:%d, %s: %s' % (self.getFileName(), self.getLine(),
|
||||||
self.getColumn(), self.getCategory(),
|
self.getColumn(), self.getCategory(),
|
||||||
self.getDescription())
|
self.getDescription())
|
||||||
|
|
||||||
# Note, the data format is not an API and may change from one analyzer
|
# Note, the data format is not an API and may change from one analyzer
|
||||||
# version to another.
|
# version to another.
|
||||||
def getRawData(self):
|
def getRawData(self):
|
||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ class multidict:
|
||||||
self.data = {}
|
self.data = {}
|
||||||
for key,value in elts:
|
for key,value in elts:
|
||||||
self[key] = value
|
self[key] = value
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
return self.data[item]
|
return self.data[item]
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
|
@ -134,15 +134,15 @@ class AnalysisRun:
|
||||||
# Cumulative list of all diagnostics from all the reports.
|
# Cumulative list of all diagnostics from all the reports.
|
||||||
self.diagnostics = []
|
self.diagnostics = []
|
||||||
self.clang_version = None
|
self.clang_version = None
|
||||||
|
|
||||||
def getClangVersion(self):
|
def getClangVersion(self):
|
||||||
return self.clang_version
|
return self.clang_version
|
||||||
|
|
||||||
def readSingleFile(self, p, deleteEmpty):
|
def readSingleFile(self, p, deleteEmpty):
|
||||||
data = plistlib.readPlist(p)
|
data = plistlib.readPlist(p)
|
||||||
|
|
||||||
# We want to retrieve the clang version even if there are no
|
# We want to retrieve the clang version even if there are no
|
||||||
# reports. Assume that all reports were created using the same
|
# reports. Assume that all reports were created using the same
|
||||||
# clang version (this is always true and is more efficient).
|
# clang version (this is always true and is more efficient).
|
||||||
if 'clang_version' in data:
|
if 'clang_version' in data:
|
||||||
if self.clang_version == None:
|
if self.clang_version == None:
|
||||||
|
@ -166,9 +166,9 @@ class AnalysisRun:
|
||||||
htmlFiles.append(d.pop('HTMLDiagnostics_files')[0])
|
htmlFiles.append(d.pop('HTMLDiagnostics_files')[0])
|
||||||
else:
|
else:
|
||||||
htmlFiles = [None] * len(data['diagnostics'])
|
htmlFiles = [None] * len(data['diagnostics'])
|
||||||
|
|
||||||
report = AnalysisReport(self, data.pop('files'))
|
report = AnalysisReport(self, data.pop('files'))
|
||||||
diagnostics = [AnalysisDiagnostic(d, report, h)
|
diagnostics = [AnalysisDiagnostic(d, report, h)
|
||||||
for d,h in zip(data.pop('diagnostics'),
|
for d,h in zip(data.pop('diagnostics'),
|
||||||
htmlFiles)]
|
htmlFiles)]
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ class AnalysisRun:
|
||||||
self.diagnostics.extend(diagnostics)
|
self.diagnostics.extend(diagnostics)
|
||||||
|
|
||||||
|
|
||||||
# Backward compatibility API.
|
# Backward compatibility API.
|
||||||
def loadResults(path, opts, root = "", deleteEmpty=True):
|
def loadResults(path, opts, root = "", deleteEmpty=True):
|
||||||
return loadResultsFromSingleRun(SingleRunInfo(path, root, opts.verboseLog),
|
return loadResultsFromSingleRun(SingleRunInfo(path, root, opts.verboseLog),
|
||||||
deleteEmpty)
|
deleteEmpty)
|
||||||
|
@ -257,7 +257,7 @@ def dumpScanBuildResultsDiff(dirA, dirB, opts, deleteEmpty=True):
|
||||||
# Load the run results.
|
# Load the run results.
|
||||||
resultsA = loadResults(dirA, opts, opts.rootA, deleteEmpty)
|
resultsA = loadResults(dirA, opts, opts.rootA, deleteEmpty)
|
||||||
resultsB = loadResults(dirB, opts, opts.rootB, deleteEmpty)
|
resultsB = loadResults(dirB, opts, opts.rootB, deleteEmpty)
|
||||||
|
|
||||||
# Open the verbose log, if given.
|
# Open the verbose log, if given.
|
||||||
if opts.verboseLog:
|
if opts.verboseLog:
|
||||||
auxLog = open(opts.verboseLog, "wb")
|
auxLog = open(opts.verboseLog, "wb")
|
||||||
|
@ -285,7 +285,7 @@ def dumpScanBuildResultsDiff(dirA, dirB, opts, deleteEmpty=True):
|
||||||
b.getReadableName())
|
b.getReadableName())
|
||||||
foundDiffs += 1
|
foundDiffs += 1
|
||||||
if auxLog:
|
if auxLog:
|
||||||
print >>auxLog, ("('CHANGED', %r, %r, %r, %r)"
|
print >>auxLog, ("('CHANGED', %r, %r, %r, %r)"
|
||||||
% (a.getReadableName(),
|
% (a.getReadableName(),
|
||||||
b.getReadableName(),
|
b.getReadableName(),
|
||||||
a.getReport(),
|
a.getReport(),
|
||||||
|
@ -299,7 +299,7 @@ def dumpScanBuildResultsDiff(dirA, dirB, opts, deleteEmpty=True):
|
||||||
if auxLog:
|
if auxLog:
|
||||||
print >>auxLog, "('TOTAL NEW REPORTS', %r)" % TotalReports
|
print >>auxLog, "('TOTAL NEW REPORTS', %r)" % TotalReports
|
||||||
print >>auxLog, "('TOTAL DIFFERENCES', %r)" % foundDiffs
|
print >>auxLog, "('TOTAL DIFFERENCES', %r)" % foundDiffs
|
||||||
|
|
||||||
return foundDiffs, len(resultsA.diagnostics), len(resultsB.diagnostics)
|
return foundDiffs, len(resultsA.diagnostics), len(resultsB.diagnostics)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -322,7 +322,7 @@ def main():
|
||||||
|
|
||||||
dirA,dirB = args
|
dirA,dirB = args
|
||||||
|
|
||||||
dumpScanBuildResultsDiff(dirA, dirB, opts)
|
dumpScanBuildResultsDiff(dirA, dirB, opts)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Static Analyzer qualification infrastructure: adding a new project to
|
Static Analyzer qualification infrastructure: adding a new project to
|
||||||
the Repository Directory.
|
the Repository Directory.
|
||||||
|
|
||||||
Add a new project for testing: build it and add to the Project Map file.
|
Add a new project for testing: build it and add to the Project Map file.
|
||||||
Assumes it's being run from the Repository Directory.
|
Assumes it's being run from the Repository Directory.
|
||||||
The project directory should be added inside the Repository Directory and
|
The project directory should be added inside the Repository Directory and
|
||||||
have the same name as the project ID
|
have the same name as the project ID
|
||||||
|
|
||||||
The project should use the following files for set up:
|
The project should use the following files for set up:
|
||||||
- pre_run_static_analyzer.sh - prepare the build environment.
|
- pre_run_static_analyzer.sh - prepare the build environment.
|
||||||
Ex: make clean can be a part of it.
|
Ex: make clean can be a part of it.
|
||||||
- run_static_analyzer.cmd - a list of commands to run through scan-build.
|
- run_static_analyzer.cmd - a list of commands to run through scan-build.
|
||||||
Each command should be on a separate line.
|
Each command should be on a separate line.
|
||||||
Choose from: configure, make, xcodebuild
|
Choose from: configure, make, xcodebuild
|
||||||
"""
|
"""
|
||||||
import SATestBuild
|
import SATestBuild
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ def isExistingProject(PMapFile, projectID) :
|
||||||
for I in PMapReader:
|
for I in PMapReader:
|
||||||
if projectID == I[0]:
|
if projectID == I[0]:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Add a new project for testing: build it and add to the Project Map file.
|
# Add a new project for testing: build it and add to the Project Map file.
|
||||||
# Params:
|
# Params:
|
||||||
|
@ -39,7 +39,7 @@ def addNewProject(ID, BuildMode) :
|
||||||
if not os.path.exists(Dir):
|
if not os.path.exists(Dir):
|
||||||
print "Error: Project directory is missing: %s" % Dir
|
print "Error: Project directory is missing: %s" % Dir
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
# Build the project.
|
# Build the project.
|
||||||
SATestBuild.testProject(ID, BuildMode, IsReferenceBuild=True, Dir=Dir)
|
SATestBuild.testProject(ID, BuildMode, IsReferenceBuild=True, Dir=Dir)
|
||||||
|
|
||||||
|
@ -51,19 +51,19 @@ def addNewProject(ID, BuildMode) :
|
||||||
print "Warning: Creating the Project Map file!!"
|
print "Warning: Creating the Project Map file!!"
|
||||||
PMapFile = open(ProjectMapPath, "w+b")
|
PMapFile = open(ProjectMapPath, "w+b")
|
||||||
try:
|
try:
|
||||||
if (isExistingProject(PMapFile, ID)) :
|
if (isExistingProject(PMapFile, ID)) :
|
||||||
print >> sys.stdout, 'Warning: Project with ID \'', ID, \
|
print >> sys.stdout, 'Warning: Project with ID \'', ID, \
|
||||||
'\' already exists.'
|
'\' already exists.'
|
||||||
print >> sys.stdout, "Reference output has been regenerated."
|
print >> sys.stdout, "Reference output has been regenerated."
|
||||||
else:
|
else:
|
||||||
PMapWriter = csv.writer(PMapFile)
|
PMapWriter = csv.writer(PMapFile)
|
||||||
PMapWriter.writerow( (ID, int(BuildMode)) );
|
PMapWriter.writerow( (ID, int(BuildMode)) );
|
||||||
print "The project map is updated: ", ProjectMapPath
|
print "The project map is updated: ", ProjectMapPath
|
||||||
finally:
|
finally:
|
||||||
PMapFile.close()
|
PMapFile.close()
|
||||||
|
|
||||||
|
|
||||||
# TODO: Add an option not to build.
|
|
||||||
|
# TODO: Add an option not to build.
|
||||||
# TODO: Set the path to the Repository directory.
|
# TODO: Set the path to the Repository directory.
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
|
@ -73,10 +73,10 @@ if __name__ == '__main__':
|
||||||
'1 for scan_build; ' \
|
'1 for scan_build; ' \
|
||||||
'2 for single file c++11 project'
|
'2 for single file c++11 project'
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
BuildMode = 1
|
BuildMode = 1
|
||||||
if (len(sys.argv) >= 3):
|
if (len(sys.argv) >= 3):
|
||||||
BuildMode = int(sys.argv[2])
|
BuildMode = int(sys.argv[2])
|
||||||
assert((BuildMode == 0) | (BuildMode == 1) | (BuildMode == 2))
|
assert((BuildMode == 0) | (BuildMode == 1) | (BuildMode == 2))
|
||||||
|
|
||||||
addNewProject(sys.argv[1], BuildMode)
|
addNewProject(sys.argv[1], BuildMode)
|
||||||
|
|
|
@ -6,8 +6,8 @@ Static Analyzer qualification infrastructure.
|
||||||
The goal is to test the analyzer against different projects, check for failures,
|
The goal is to test the analyzer against different projects, check for failures,
|
||||||
compare results, and measure performance.
|
compare results, and measure performance.
|
||||||
|
|
||||||
Repository Directory will contain sources of the projects as well as the
|
Repository Directory will contain sources of the projects as well as the
|
||||||
information on how to build them and the expected output.
|
information on how to build them and the expected output.
|
||||||
Repository Directory structure:
|
Repository Directory structure:
|
||||||
- ProjectMap file
|
- ProjectMap file
|
||||||
- Historical Performance Data
|
- Historical Performance Data
|
||||||
|
@ -19,16 +19,16 @@ Repository Directory structure:
|
||||||
Note that the build tree must be inside the project dir.
|
Note that the build tree must be inside the project dir.
|
||||||
|
|
||||||
To test the build of the analyzer one would:
|
To test the build of the analyzer one would:
|
||||||
- Copy over a copy of the Repository Directory. (TODO: Prefer to ensure that
|
- Copy over a copy of the Repository Directory. (TODO: Prefer to ensure that
|
||||||
the build directory does not pollute the repository to min network traffic).
|
the build directory does not pollute the repository to min network traffic).
|
||||||
- Build all projects, until error. Produce logs to report errors.
|
- Build all projects, until error. Produce logs to report errors.
|
||||||
- Compare results.
|
- Compare results.
|
||||||
|
|
||||||
The files which should be kept around for failure investigations:
|
The files which should be kept around for failure investigations:
|
||||||
RepositoryCopy/Project DirI/ScanBuildResults
|
RepositoryCopy/Project DirI/ScanBuildResults
|
||||||
RepositoryCopy/Project DirI/run_static_analyzer.log
|
RepositoryCopy/Project DirI/run_static_analyzer.log
|
||||||
|
|
||||||
Assumptions (TODO: shouldn't need to assume these.):
|
Assumptions (TODO: shouldn't need to assume these.):
|
||||||
The script is being run from the Repository Directory.
|
The script is being run from the Repository Directory.
|
||||||
The compiler for scan-build and scan-build are in the PATH.
|
The compiler for scan-build and scan-build are in the PATH.
|
||||||
export PATH=/Users/zaks/workspace/c2llvm/build/Release+Asserts/bin:$PATH
|
export PATH=/Users/zaks/workspace/c2llvm/build/Release+Asserts/bin:$PATH
|
||||||
|
@ -36,7 +36,7 @@ Assumptions (TODO: shouldn't need to assume these.):
|
||||||
For more logging, set the env variables:
|
For more logging, set the env variables:
|
||||||
zaks:TI zaks$ export CCC_ANALYZER_LOG=1
|
zaks:TI zaks$ export CCC_ANALYZER_LOG=1
|
||||||
zaks:TI zaks$ export CCC_ANALYZER_VERBOSE=1
|
zaks:TI zaks$ export CCC_ANALYZER_VERBOSE=1
|
||||||
|
|
||||||
The list of checkers tested are hardcoded in the Checkers variable.
|
The list of checkers tested are hardcoded in the Checkers variable.
|
||||||
For testing additional checkers, use the SA_ADDITIONAL_CHECKERS environment
|
For testing additional checkers, use the SA_ADDITIONAL_CHECKERS environment
|
||||||
variable. It should contain a comma separated list.
|
variable. It should contain a comma separated list.
|
||||||
|
@ -120,16 +120,16 @@ class flushfile(object):
|
||||||
sys.stdout = flushfile(sys.stdout)
|
sys.stdout = flushfile(sys.stdout)
|
||||||
|
|
||||||
def getProjectMapPath():
|
def getProjectMapPath():
|
||||||
ProjectMapPath = os.path.join(os.path.abspath(os.curdir),
|
ProjectMapPath = os.path.join(os.path.abspath(os.curdir),
|
||||||
ProjectMapFile)
|
ProjectMapFile)
|
||||||
if not os.path.exists(ProjectMapPath):
|
if not os.path.exists(ProjectMapPath):
|
||||||
print "Error: Cannot find the Project Map file " + ProjectMapPath +\
|
print "Error: Cannot find the Project Map file " + ProjectMapPath +\
|
||||||
"\nRunning script for the wrong directory?"
|
"\nRunning script for the wrong directory?"
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
return ProjectMapPath
|
return ProjectMapPath
|
||||||
|
|
||||||
def getProjectDir(ID):
|
def getProjectDir(ID):
|
||||||
return os.path.join(os.path.abspath(os.curdir), ID)
|
return os.path.join(os.path.abspath(os.curdir), ID)
|
||||||
|
|
||||||
def getSBOutputDirName(IsReferenceBuild) :
|
def getSBOutputDirName(IsReferenceBuild) :
|
||||||
if IsReferenceBuild == True :
|
if IsReferenceBuild == True :
|
||||||
|
@ -156,13 +156,13 @@ ProjectMapFile = "projectMap.csv"
|
||||||
# Names of the project specific scripts.
|
# Names of the project specific scripts.
|
||||||
# The script that needs to be executed before the build can start.
|
# The script that needs to be executed before the build can start.
|
||||||
CleanupScript = "cleanup_run_static_analyzer.sh"
|
CleanupScript = "cleanup_run_static_analyzer.sh"
|
||||||
# This is a file containing commands for scan-build.
|
# This is a file containing commands for scan-build.
|
||||||
BuildScript = "run_static_analyzer.cmd"
|
BuildScript = "run_static_analyzer.cmd"
|
||||||
|
|
||||||
# The log file name.
|
# The log file name.
|
||||||
LogFolderName = "Logs"
|
LogFolderName = "Logs"
|
||||||
BuildLogName = "run_static_analyzer.log"
|
BuildLogName = "run_static_analyzer.log"
|
||||||
# Summary file - contains the summary of the failures. Ex: This info can be be
|
# Summary file - contains the summary of the failures. Ex: This info can be be
|
||||||
# displayed when buildbot detects a build failure.
|
# displayed when buildbot detects a build failure.
|
||||||
NumOfFailuresInSummary = 10
|
NumOfFailuresInSummary = 10
|
||||||
FailuresSummaryFileName = "failures.txt"
|
FailuresSummaryFileName = "failures.txt"
|
||||||
|
@ -189,21 +189,21 @@ def runCleanupScript(Dir, PBuildLogFile):
|
||||||
ScriptPath = os.path.join(Dir, CleanupScript)
|
ScriptPath = os.path.join(Dir, CleanupScript)
|
||||||
if os.path.exists(ScriptPath):
|
if os.path.exists(ScriptPath):
|
||||||
try:
|
try:
|
||||||
if Verbose == 1:
|
if Verbose == 1:
|
||||||
print " Executing: %s" % (ScriptPath,)
|
print " Executing: %s" % (ScriptPath,)
|
||||||
check_call("chmod +x %s" % ScriptPath, cwd = Dir,
|
check_call("chmod +x %s" % ScriptPath, cwd = Dir,
|
||||||
stderr=PBuildLogFile,
|
stderr=PBuildLogFile,
|
||||||
stdout=PBuildLogFile,
|
stdout=PBuildLogFile,
|
||||||
shell=True)
|
shell=True)
|
||||||
check_call(ScriptPath, cwd = Dir, stderr=PBuildLogFile,
|
check_call(ScriptPath, cwd = Dir, stderr=PBuildLogFile,
|
||||||
stdout=PBuildLogFile,
|
stdout=PBuildLogFile,
|
||||||
shell=True)
|
shell=True)
|
||||||
except:
|
except:
|
||||||
print "Error: The pre-processing step failed. See ", \
|
print "Error: The pre-processing step failed. See ", \
|
||||||
PBuildLogFile.name, " for details."
|
PBuildLogFile.name, " for details."
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
# Build the project with scan-build by reading in the commands and
|
# Build the project with scan-build by reading in the commands and
|
||||||
# prefixing them with the scan-build options.
|
# prefixing them with the scan-build options.
|
||||||
def runScanBuild(Dir, SBOutputDir, PBuildLogFile):
|
def runScanBuild(Dir, SBOutputDir, PBuildLogFile):
|
||||||
BuildScriptPath = os.path.join(Dir, BuildScript)
|
BuildScriptPath = os.path.join(Dir, BuildScript)
|
||||||
|
@ -212,9 +212,9 @@ def runScanBuild(Dir, SBOutputDir, PBuildLogFile):
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
SBOptions = "--use-analyzer " + Clang + " "
|
SBOptions = "--use-analyzer " + Clang + " "
|
||||||
SBOptions += "-plist-html -o " + SBOutputDir + " "
|
SBOptions += "-plist-html -o " + SBOutputDir + " "
|
||||||
SBOptions += "-enable-checker " + Checkers + " "
|
SBOptions += "-enable-checker " + Checkers + " "
|
||||||
SBOptions += "--keep-empty "
|
SBOptions += "--keep-empty "
|
||||||
# Always use ccc-analyze to ensure that we can locate the failures
|
# Always use ccc-analyze to ensure that we can locate the failures
|
||||||
# directory.
|
# directory.
|
||||||
SBOptions += "--override-compiler "
|
SBOptions += "--override-compiler "
|
||||||
try:
|
try:
|
||||||
|
@ -231,10 +231,10 @@ def runScanBuild(Dir, SBOutputDir, PBuildLogFile):
|
||||||
"-j" not in Command:
|
"-j" not in Command:
|
||||||
Command += " -j%d" % Jobs
|
Command += " -j%d" % Jobs
|
||||||
SBCommand = SBPrefix + Command
|
SBCommand = SBPrefix + Command
|
||||||
if Verbose == 1:
|
if Verbose == 1:
|
||||||
print " Executing: %s" % (SBCommand,)
|
print " Executing: %s" % (SBCommand,)
|
||||||
check_call(SBCommand, cwd = Dir, stderr=PBuildLogFile,
|
check_call(SBCommand, cwd = Dir, stderr=PBuildLogFile,
|
||||||
stdout=PBuildLogFile,
|
stdout=PBuildLogFile,
|
||||||
shell=True)
|
shell=True)
|
||||||
except:
|
except:
|
||||||
print "Error: scan-build failed. See ",PBuildLogFile.name,\
|
print "Error: scan-build failed. See ",PBuildLogFile.name,\
|
||||||
|
@ -249,97 +249,97 @@ def hasNoExtension(FileName):
|
||||||
|
|
||||||
def isValidSingleInputFile(FileName):
|
def isValidSingleInputFile(FileName):
|
||||||
(Root, Ext) = os.path.splitext(FileName)
|
(Root, Ext) = os.path.splitext(FileName)
|
||||||
if ((Ext == ".i") | (Ext == ".ii") |
|
if ((Ext == ".i") | (Ext == ".ii") |
|
||||||
(Ext == ".c") | (Ext == ".cpp") |
|
(Ext == ".c") | (Ext == ".cpp") |
|
||||||
(Ext == ".m") | (Ext == "")) :
|
(Ext == ".m") | (Ext == "")) :
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Run analysis on a set of preprocessed files.
|
# Run analysis on a set of preprocessed files.
|
||||||
def runAnalyzePreprocessed(Dir, SBOutputDir, Mode):
|
def runAnalyzePreprocessed(Dir, SBOutputDir, Mode):
|
||||||
if os.path.exists(os.path.join(Dir, BuildScript)):
|
if os.path.exists(os.path.join(Dir, BuildScript)):
|
||||||
print "Error: The preprocessed files project should not contain %s" % \
|
print "Error: The preprocessed files project should not contain %s" % \
|
||||||
BuildScript
|
BuildScript
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
CmdPrefix = Clang + " -cc1 -analyze -analyzer-output=plist -w "
|
CmdPrefix = Clang + " -cc1 -analyze -analyzer-output=plist -w "
|
||||||
CmdPrefix += "-analyzer-checker=" + Checkers +" -fcxx-exceptions -fblocks "
|
CmdPrefix += "-analyzer-checker=" + Checkers +" -fcxx-exceptions -fblocks "
|
||||||
|
|
||||||
if (Mode == 2) :
|
if (Mode == 2) :
|
||||||
CmdPrefix += "-std=c++11 "
|
CmdPrefix += "-std=c++11 "
|
||||||
|
|
||||||
PlistPath = os.path.join(Dir, SBOutputDir, "date")
|
PlistPath = os.path.join(Dir, SBOutputDir, "date")
|
||||||
FailPath = os.path.join(PlistPath, "failures");
|
FailPath = os.path.join(PlistPath, "failures");
|
||||||
os.makedirs(FailPath);
|
os.makedirs(FailPath);
|
||||||
|
|
||||||
for FullFileName in glob.glob(Dir + "/*"):
|
for FullFileName in glob.glob(Dir + "/*"):
|
||||||
FileName = os.path.basename(FullFileName)
|
FileName = os.path.basename(FullFileName)
|
||||||
Failed = False
|
Failed = False
|
||||||
|
|
||||||
# Only run the analyzes on supported files.
|
# Only run the analyzes on supported files.
|
||||||
if (hasNoExtension(FileName)):
|
if (hasNoExtension(FileName)):
|
||||||
continue
|
continue
|
||||||
if (isValidSingleInputFile(FileName) == False):
|
if (isValidSingleInputFile(FileName) == False):
|
||||||
print "Error: Invalid single input file %s." % (FullFileName,)
|
print "Error: Invalid single input file %s." % (FullFileName,)
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
# Build and call the analyzer command.
|
# Build and call the analyzer command.
|
||||||
OutputOption = "-o " + os.path.join(PlistPath, FileName) + ".plist "
|
OutputOption = "-o " + os.path.join(PlistPath, FileName) + ".plist "
|
||||||
Command = CmdPrefix + OutputOption + FileName
|
Command = CmdPrefix + OutputOption + FileName
|
||||||
LogFile = open(os.path.join(FailPath, FileName + ".stderr.txt"), "w+b")
|
LogFile = open(os.path.join(FailPath, FileName + ".stderr.txt"), "w+b")
|
||||||
try:
|
try:
|
||||||
if Verbose == 1:
|
if Verbose == 1:
|
||||||
print " Executing: %s" % (Command,)
|
print " Executing: %s" % (Command,)
|
||||||
check_call(Command, cwd = Dir, stderr=LogFile,
|
check_call(Command, cwd = Dir, stderr=LogFile,
|
||||||
stdout=LogFile,
|
stdout=LogFile,
|
||||||
shell=True)
|
shell=True)
|
||||||
except CalledProcessError, e:
|
except CalledProcessError, e:
|
||||||
print "Error: Analyzes of %s failed. See %s for details." \
|
print "Error: Analyzes of %s failed. See %s for details." \
|
||||||
"Error code %d." % \
|
"Error code %d." % \
|
||||||
(FullFileName, LogFile.name, e.returncode)
|
(FullFileName, LogFile.name, e.returncode)
|
||||||
Failed = True
|
Failed = True
|
||||||
finally:
|
finally:
|
||||||
LogFile.close()
|
LogFile.close()
|
||||||
|
|
||||||
# If command did not fail, erase the log file.
|
# If command did not fail, erase the log file.
|
||||||
if Failed == False:
|
if Failed == False:
|
||||||
os.remove(LogFile.name);
|
os.remove(LogFile.name);
|
||||||
|
|
||||||
def buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild):
|
def buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild):
|
||||||
TBegin = time.time()
|
TBegin = time.time()
|
||||||
|
|
||||||
BuildLogPath = os.path.join(SBOutputDir, LogFolderName, BuildLogName)
|
BuildLogPath = os.path.join(SBOutputDir, LogFolderName, BuildLogName)
|
||||||
print "Log file: %s" % (BuildLogPath,)
|
print "Log file: %s" % (BuildLogPath,)
|
||||||
print "Output directory: %s" %(SBOutputDir, )
|
print "Output directory: %s" %(SBOutputDir, )
|
||||||
|
|
||||||
# Clean up the log file.
|
# Clean up the log file.
|
||||||
if (os.path.exists(BuildLogPath)) :
|
if (os.path.exists(BuildLogPath)) :
|
||||||
RmCommand = "rm " + BuildLogPath
|
RmCommand = "rm " + BuildLogPath
|
||||||
if Verbose == 1:
|
if Verbose == 1:
|
||||||
print " Executing: %s" % (RmCommand,)
|
print " Executing: %s" % (RmCommand,)
|
||||||
check_call(RmCommand, shell=True)
|
check_call(RmCommand, shell=True)
|
||||||
|
|
||||||
# Clean up scan build results.
|
# Clean up scan build results.
|
||||||
if (os.path.exists(SBOutputDir)) :
|
if (os.path.exists(SBOutputDir)) :
|
||||||
RmCommand = "rm -r " + SBOutputDir
|
RmCommand = "rm -r " + SBOutputDir
|
||||||
if Verbose == 1:
|
if Verbose == 1:
|
||||||
print " Executing: %s" % (RmCommand,)
|
print " Executing: %s" % (RmCommand,)
|
||||||
check_call(RmCommand, shell=True)
|
check_call(RmCommand, shell=True)
|
||||||
assert(not os.path.exists(SBOutputDir))
|
assert(not os.path.exists(SBOutputDir))
|
||||||
os.makedirs(os.path.join(SBOutputDir, LogFolderName))
|
os.makedirs(os.path.join(SBOutputDir, LogFolderName))
|
||||||
|
|
||||||
# Open the log file.
|
# Open the log file.
|
||||||
PBuildLogFile = open(BuildLogPath, "wb+")
|
PBuildLogFile = open(BuildLogPath, "wb+")
|
||||||
|
|
||||||
# Build and analyze the project.
|
# Build and analyze the project.
|
||||||
try:
|
try:
|
||||||
runCleanupScript(Dir, PBuildLogFile)
|
runCleanupScript(Dir, PBuildLogFile)
|
||||||
|
|
||||||
if (ProjectBuildMode == 1):
|
if (ProjectBuildMode == 1):
|
||||||
runScanBuild(Dir, SBOutputDir, PBuildLogFile)
|
runScanBuild(Dir, SBOutputDir, PBuildLogFile)
|
||||||
else:
|
else:
|
||||||
runAnalyzePreprocessed(Dir, SBOutputDir, ProjectBuildMode)
|
runAnalyzePreprocessed(Dir, SBOutputDir, ProjectBuildMode)
|
||||||
|
|
||||||
if IsReferenceBuild :
|
if IsReferenceBuild :
|
||||||
runCleanupScript(Dir, PBuildLogFile)
|
runCleanupScript(Dir, PBuildLogFile)
|
||||||
|
|
||||||
|
@ -354,28 +354,28 @@ def buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild):
|
||||||
else SourceFile for SourceFile in Data['files']]
|
else SourceFile for SourceFile in Data['files']]
|
||||||
Data['files'] = Paths
|
Data['files'] = Paths
|
||||||
plistlib.writePlist(Data, Plist)
|
plistlib.writePlist(Data, Plist)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
PBuildLogFile.close()
|
PBuildLogFile.close()
|
||||||
|
|
||||||
print "Build complete (time: %.2f). See the log for more details: %s" % \
|
print "Build complete (time: %.2f). See the log for more details: %s" % \
|
||||||
((time.time()-TBegin), BuildLogPath)
|
((time.time()-TBegin), BuildLogPath)
|
||||||
|
|
||||||
# A plist file is created for each call to the analyzer(each source file).
|
# A plist file is created for each call to the analyzer(each source file).
|
||||||
# We are only interested on the once that have bug reports, so delete the rest.
|
# We are only interested on the once that have bug reports, so delete the rest.
|
||||||
def CleanUpEmptyPlists(SBOutputDir):
|
def CleanUpEmptyPlists(SBOutputDir):
|
||||||
for F in glob.glob(SBOutputDir + "/*/*.plist"):
|
for F in glob.glob(SBOutputDir + "/*/*.plist"):
|
||||||
P = os.path.join(SBOutputDir, F)
|
P = os.path.join(SBOutputDir, F)
|
||||||
|
|
||||||
Data = plistlib.readPlist(P)
|
Data = plistlib.readPlist(P)
|
||||||
# Delete empty reports.
|
# Delete empty reports.
|
||||||
if not Data['files']:
|
if not Data['files']:
|
||||||
os.remove(P)
|
os.remove(P)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Given the scan-build output directory, checks if the build failed
|
# Given the scan-build output directory, checks if the build failed
|
||||||
# (by searching for the failures directories). If there are failures, it
|
# (by searching for the failures directories). If there are failures, it
|
||||||
# creates a summary file in the output directory.
|
# creates a summary file in the output directory.
|
||||||
def checkBuild(SBOutputDir):
|
def checkBuild(SBOutputDir):
|
||||||
# Check if there are failures.
|
# Check if there are failures.
|
||||||
Failures = glob.glob(SBOutputDir + "/*/failures/*.stderr.txt")
|
Failures = glob.glob(SBOutputDir + "/*/failures/*.stderr.txt")
|
||||||
|
@ -386,37 +386,37 @@ def checkBuild(SBOutputDir):
|
||||||
print "Number of bug reports (non-empty plist files) produced: %d" %\
|
print "Number of bug reports (non-empty plist files) produced: %d" %\
|
||||||
len(Plists)
|
len(Plists)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
# Create summary file to display when the build fails.
|
# Create summary file to display when the build fails.
|
||||||
SummaryPath = os.path.join(SBOutputDir, LogFolderName, FailuresSummaryFileName)
|
SummaryPath = os.path.join(SBOutputDir, LogFolderName, FailuresSummaryFileName)
|
||||||
if (Verbose > 0):
|
if (Verbose > 0):
|
||||||
print " Creating the failures summary file %s" % (SummaryPath,)
|
print " Creating the failures summary file %s" % (SummaryPath,)
|
||||||
|
|
||||||
SummaryLog = open(SummaryPath, "w+")
|
SummaryLog = open(SummaryPath, "w+")
|
||||||
try:
|
try:
|
||||||
SummaryLog.write("Total of %d failures discovered.\n" % (TotalFailed,))
|
SummaryLog.write("Total of %d failures discovered.\n" % (TotalFailed,))
|
||||||
if TotalFailed > NumOfFailuresInSummary:
|
if TotalFailed > NumOfFailuresInSummary:
|
||||||
SummaryLog.write("See the first %d below.\n"
|
SummaryLog.write("See the first %d below.\n"
|
||||||
% (NumOfFailuresInSummary,))
|
% (NumOfFailuresInSummary,))
|
||||||
# TODO: Add a line "See the results folder for more."
|
# TODO: Add a line "See the results folder for more."
|
||||||
|
|
||||||
FailuresCopied = NumOfFailuresInSummary
|
FailuresCopied = NumOfFailuresInSummary
|
||||||
Idx = 0
|
Idx = 0
|
||||||
for FailLogPathI in Failures:
|
for FailLogPathI in Failures:
|
||||||
if Idx >= NumOfFailuresInSummary:
|
if Idx >= NumOfFailuresInSummary:
|
||||||
break;
|
break;
|
||||||
Idx += 1
|
Idx += 1
|
||||||
SummaryLog.write("\n-- Error #%d -----------\n" % (Idx,));
|
SummaryLog.write("\n-- Error #%d -----------\n" % (Idx,));
|
||||||
FailLogI = open(FailLogPathI, "r");
|
FailLogI = open(FailLogPathI, "r");
|
||||||
try:
|
try:
|
||||||
shutil.copyfileobj(FailLogI, SummaryLog);
|
shutil.copyfileobj(FailLogI, SummaryLog);
|
||||||
finally:
|
finally:
|
||||||
FailLogI.close()
|
FailLogI.close()
|
||||||
finally:
|
finally:
|
||||||
SummaryLog.close()
|
SummaryLog.close()
|
||||||
|
|
||||||
print "Error: analysis failed. See ", SummaryPath
|
print "Error: analysis failed. See ", SummaryPath
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
# Auxiliary object to discard stdout.
|
# Auxiliary object to discard stdout.
|
||||||
class Discarder(object):
|
class Discarder(object):
|
||||||
|
@ -428,44 +428,44 @@ class Discarder(object):
|
||||||
# 0 - success if there are no crashes or analyzer failure.
|
# 0 - success if there are no crashes or analyzer failure.
|
||||||
# 1 - success if there are no difference in the number of reported bugs.
|
# 1 - success if there are no difference in the number of reported bugs.
|
||||||
# 2 - success if all the bug reports are identical.
|
# 2 - success if all the bug reports are identical.
|
||||||
def runCmpResults(Dir, Strictness = 0):
|
def runCmpResults(Dir, Strictness = 0):
|
||||||
TBegin = time.time()
|
TBegin = time.time()
|
||||||
|
|
||||||
RefDir = os.path.join(Dir, SBOutputDirReferencePrefix + SBOutputDirName)
|
RefDir = os.path.join(Dir, SBOutputDirReferencePrefix + SBOutputDirName)
|
||||||
NewDir = os.path.join(Dir, SBOutputDirName)
|
NewDir = os.path.join(Dir, SBOutputDirName)
|
||||||
|
|
||||||
# We have to go one level down the directory tree.
|
# We have to go one level down the directory tree.
|
||||||
RefList = glob.glob(RefDir + "/*")
|
RefList = glob.glob(RefDir + "/*")
|
||||||
NewList = glob.glob(NewDir + "/*")
|
NewList = glob.glob(NewDir + "/*")
|
||||||
|
|
||||||
# Log folders are also located in the results dir, so ignore them.
|
# Log folders are also located in the results dir, so ignore them.
|
||||||
RefLogDir = os.path.join(RefDir, LogFolderName)
|
RefLogDir = os.path.join(RefDir, LogFolderName)
|
||||||
if RefLogDir in RefList:
|
if RefLogDir in RefList:
|
||||||
RefList.remove(RefLogDir)
|
RefList.remove(RefLogDir)
|
||||||
NewList.remove(os.path.join(NewDir, LogFolderName))
|
NewList.remove(os.path.join(NewDir, LogFolderName))
|
||||||
|
|
||||||
if len(RefList) == 0 or len(NewList) == 0:
|
if len(RefList) == 0 or len(NewList) == 0:
|
||||||
return False
|
return False
|
||||||
assert(len(RefList) == len(NewList))
|
assert(len(RefList) == len(NewList))
|
||||||
|
|
||||||
# There might be more then one folder underneath - one per each scan-build
|
# There might be more then one folder underneath - one per each scan-build
|
||||||
# command (Ex: one for configure and one for make).
|
# command (Ex: one for configure and one for make).
|
||||||
if (len(RefList) > 1):
|
if (len(RefList) > 1):
|
||||||
# Assume that the corresponding folders have the same names.
|
# Assume that the corresponding folders have the same names.
|
||||||
RefList.sort()
|
RefList.sort()
|
||||||
NewList.sort()
|
NewList.sort()
|
||||||
|
|
||||||
# Iterate and find the differences.
|
# Iterate and find the differences.
|
||||||
NumDiffs = 0
|
NumDiffs = 0
|
||||||
PairList = zip(RefList, NewList)
|
PairList = zip(RefList, NewList)
|
||||||
for P in PairList:
|
for P in PairList:
|
||||||
RefDir = P[0]
|
RefDir = P[0]
|
||||||
NewDir = P[1]
|
NewDir = P[1]
|
||||||
|
|
||||||
assert(RefDir != NewDir)
|
assert(RefDir != NewDir)
|
||||||
if Verbose == 1:
|
if Verbose == 1:
|
||||||
print " Comparing Results: %s %s" % (RefDir, NewDir)
|
print " Comparing Results: %s %s" % (RefDir, NewDir)
|
||||||
|
|
||||||
DiffsPath = os.path.join(NewDir, DiffsSummaryFileName)
|
DiffsPath = os.path.join(NewDir, DiffsSummaryFileName)
|
||||||
Opts = CmpRuns.CmpOptions(DiffsPath, "", Dir)
|
Opts = CmpRuns.CmpOptions(DiffsPath, "", Dir)
|
||||||
# Discard everything coming out of stdout (CmpRun produces a lot of them).
|
# Discard everything coming out of stdout (CmpRun produces a lot of them).
|
||||||
|
@ -480,70 +480,70 @@ def runCmpResults(Dir, Strictness = 0):
|
||||||
(NumDiffs, DiffsPath,)
|
(NumDiffs, DiffsPath,)
|
||||||
if Strictness >= 2 and NumDiffs > 0:
|
if Strictness >= 2 and NumDiffs > 0:
|
||||||
print "Error: Diffs found in strict mode (2)."
|
print "Error: Diffs found in strict mode (2)."
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
elif Strictness >= 1 and ReportsInRef != ReportsInNew:
|
elif Strictness >= 1 and ReportsInRef != ReportsInNew:
|
||||||
print "Error: The number of results are different in strict mode (1)."
|
print "Error: The number of results are different in strict mode (1)."
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
print "Diagnostic comparison complete (time: %.2f)." % (time.time()-TBegin)
|
print "Diagnostic comparison complete (time: %.2f)." % (time.time()-TBegin)
|
||||||
return (NumDiffs > 0)
|
return (NumDiffs > 0)
|
||||||
|
|
||||||
def updateSVN(Mode, ProjectsMap):
|
def updateSVN(Mode, ProjectsMap):
|
||||||
try:
|
try:
|
||||||
ProjectsMap.seek(0)
|
ProjectsMap.seek(0)
|
||||||
for I in csv.reader(ProjectsMap):
|
for I in csv.reader(ProjectsMap):
|
||||||
ProjName = I[0]
|
ProjName = I[0]
|
||||||
Path = os.path.join(ProjName, getSBOutputDirName(True))
|
Path = os.path.join(ProjName, getSBOutputDirName(True))
|
||||||
|
|
||||||
if Mode == "delete":
|
if Mode == "delete":
|
||||||
Command = "svn delete %s" % (Path,)
|
Command = "svn delete %s" % (Path,)
|
||||||
else:
|
else:
|
||||||
Command = "svn add %s" % (Path,)
|
Command = "svn add %s" % (Path,)
|
||||||
|
|
||||||
if Verbose == 1:
|
if Verbose == 1:
|
||||||
print " Executing: %s" % (Command,)
|
print " Executing: %s" % (Command,)
|
||||||
check_call(Command, shell=True)
|
check_call(Command, shell=True)
|
||||||
|
|
||||||
if Mode == "delete":
|
if Mode == "delete":
|
||||||
CommitCommand = "svn commit -m \"[analyzer tests] Remove " \
|
CommitCommand = "svn commit -m \"[analyzer tests] Remove " \
|
||||||
"reference results.\""
|
"reference results.\""
|
||||||
else:
|
else:
|
||||||
CommitCommand = "svn commit -m \"[analyzer tests] Add new " \
|
CommitCommand = "svn commit -m \"[analyzer tests] Add new " \
|
||||||
"reference results.\""
|
"reference results.\""
|
||||||
if Verbose == 1:
|
if Verbose == 1:
|
||||||
print " Executing: %s" % (CommitCommand,)
|
print " Executing: %s" % (CommitCommand,)
|
||||||
check_call(CommitCommand, shell=True)
|
check_call(CommitCommand, shell=True)
|
||||||
except:
|
except:
|
||||||
print "Error: SVN update failed."
|
print "Error: SVN update failed."
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
def testProject(ID, ProjectBuildMode, IsReferenceBuild=False, Dir=None, Strictness = 0):
|
def testProject(ID, ProjectBuildMode, IsReferenceBuild=False, Dir=None, Strictness = 0):
|
||||||
print " \n\n--- Building project %s" % (ID,)
|
print " \n\n--- Building project %s" % (ID,)
|
||||||
|
|
||||||
TBegin = time.time()
|
TBegin = time.time()
|
||||||
|
|
||||||
if Dir is None :
|
if Dir is None :
|
||||||
Dir = getProjectDir(ID)
|
Dir = getProjectDir(ID)
|
||||||
if Verbose == 1:
|
if Verbose == 1:
|
||||||
print " Build directory: %s." % (Dir,)
|
print " Build directory: %s." % (Dir,)
|
||||||
|
|
||||||
# Set the build results directory.
|
# Set the build results directory.
|
||||||
RelOutputDir = getSBOutputDirName(IsReferenceBuild)
|
RelOutputDir = getSBOutputDirName(IsReferenceBuild)
|
||||||
SBOutputDir = os.path.join(Dir, RelOutputDir)
|
SBOutputDir = os.path.join(Dir, RelOutputDir)
|
||||||
|
|
||||||
buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild)
|
buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild)
|
||||||
|
|
||||||
checkBuild(SBOutputDir)
|
checkBuild(SBOutputDir)
|
||||||
|
|
||||||
if IsReferenceBuild == False:
|
if IsReferenceBuild == False:
|
||||||
runCmpResults(Dir, Strictness)
|
runCmpResults(Dir, Strictness)
|
||||||
|
|
||||||
print "Completed tests for project %s (time: %.2f)." % \
|
print "Completed tests for project %s (time: %.2f)." % \
|
||||||
(ID, (time.time()-TBegin))
|
(ID, (time.time()-TBegin))
|
||||||
|
|
||||||
def testAll(IsReferenceBuild = False, UpdateSVN = False, Strictness = 0):
|
def testAll(IsReferenceBuild = False, UpdateSVN = False, Strictness = 0):
|
||||||
PMapFile = open(getProjectMapPath(), "rb")
|
PMapFile = open(getProjectMapPath(), "rb")
|
||||||
try:
|
try:
|
||||||
# Validate the input.
|
# Validate the input.
|
||||||
for I in csv.reader(PMapFile):
|
for I in csv.reader(PMapFile):
|
||||||
if (len(I) != 2) :
|
if (len(I) != 2) :
|
||||||
|
@ -552,16 +552,16 @@ def testAll(IsReferenceBuild = False, UpdateSVN = False, Strictness = 0):
|
||||||
if (not ((I[1] == "0") | (I[1] == "1") | (I[1] == "2"))):
|
if (not ((I[1] == "0") | (I[1] == "1") | (I[1] == "2"))):
|
||||||
print "Error: Second entry in the ProjectMapFile should be 0" \
|
print "Error: Second entry in the ProjectMapFile should be 0" \
|
||||||
" (single file), 1 (project), or 2(single file c++11)."
|
" (single file), 1 (project), or 2(single file c++11)."
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
# When we are regenerating the reference results, we might need to
|
# When we are regenerating the reference results, we might need to
|
||||||
# update svn. Remove reference results from SVN.
|
# update svn. Remove reference results from SVN.
|
||||||
if UpdateSVN == True:
|
if UpdateSVN == True:
|
||||||
assert(IsReferenceBuild == True);
|
assert(IsReferenceBuild == True);
|
||||||
updateSVN("delete", PMapFile);
|
updateSVN("delete", PMapFile);
|
||||||
|
|
||||||
# Test the projects.
|
# Test the projects.
|
||||||
PMapFile.seek(0)
|
PMapFile.seek(0)
|
||||||
for I in csv.reader(PMapFile):
|
for I in csv.reader(PMapFile):
|
||||||
testProject(I[0], int(I[1]), IsReferenceBuild, None, Strictness)
|
testProject(I[0], int(I[1]), IsReferenceBuild, None, Strictness)
|
||||||
|
|
||||||
|
@ -571,10 +571,10 @@ def testAll(IsReferenceBuild = False, UpdateSVN = False, Strictness = 0):
|
||||||
|
|
||||||
except:
|
except:
|
||||||
print "Error occurred. Premature termination."
|
print "Error occurred. Premature termination."
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
PMapFile.close()
|
PMapFile.close()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Parse command line arguments.
|
# Parse command line arguments.
|
||||||
Parser = argparse.ArgumentParser(description='Test the Clang Static Analyzer.')
|
Parser = argparse.ArgumentParser(description='Test the Clang Static Analyzer.')
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"""
|
"""
|
||||||
Script to Summarize statistics in the scan-build output.
|
Script to Summarize statistics in the scan-build output.
|
||||||
|
|
||||||
Statistics are enabled by passing '-internal-stats' option to scan-build
|
Statistics are enabled by passing '-internal-stats' option to scan-build
|
||||||
(or '-analyzer-stats' to the analyzer).
|
(or '-analyzer-stats' to the analyzer).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -69,7 +69,7 @@ if __name__ == '__main__':
|
||||||
if ((") Total" in line) and (Mode == 1)) :
|
if ((") Total" in line) and (Mode == 1)) :
|
||||||
s = line.split()
|
s = line.split()
|
||||||
TotalTime = TotalTime + float(s[6])
|
TotalTime = TotalTime + float(s[6])
|
||||||
|
|
||||||
print "TU Count %d" % (Count)
|
print "TU Count %d" % (Count)
|
||||||
print "Time %f" % (Time)
|
print "Time %f" % (Time)
|
||||||
print "Warnings %d" % (Warnings)
|
print "Warnings %d" % (Warnings)
|
||||||
|
@ -81,4 +81,3 @@ if __name__ == '__main__':
|
||||||
print "MaxTime %f" % (MaxTime)
|
print "MaxTime %f" % (MaxTime)
|
||||||
print "TotalTime %f" % (TotalTime)
|
print "TotalTime %f" % (TotalTime)
|
||||||
print "Max CFG Size %d" % (MaxCFGSize)
|
print "Max CFG Size %d" % (MaxCFGSize)
|
||||||
|
|
|
@ -18,7 +18,7 @@ import sys
|
||||||
def Error(message):
|
def Error(message):
|
||||||
print >> sys.stderr, 'ubiviz: ' + message
|
print >> sys.stderr, 'ubiviz: ' + message
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def StreamData(filename):
|
def StreamData(filename):
|
||||||
file = open(filename)
|
file = open(filename)
|
||||||
for ln in file:
|
for ln in file:
|
||||||
|
@ -55,20 +55,19 @@ def Display(G, data):
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
Error('no input files')
|
Error('no input files')
|
||||||
|
|
||||||
server = xmlrpclib.Server('http://127.0.0.1:20738/RPC2')
|
server = xmlrpclib.Server('http://127.0.0.1:20738/RPC2')
|
||||||
G = server.ubigraph
|
G = server.ubigraph
|
||||||
|
|
||||||
for arg in args:
|
for arg in args:
|
||||||
G.clear()
|
G.clear()
|
||||||
for x in StreamData(arg):
|
for x in StreamData(arg):
|
||||||
Display(G,x)
|
Display(G,x)
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue