aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
commit706b4fc47bbc608932d3b491ae19a3b9cde9497b (patch)
tree4adf86a776049cbf7f69a1929c4babcbbef925eb /clang/lib/StaticAnalyzer
parent7cc9cf2bf09f069cb2dd947ead05d0b54301fb71 (diff)
Vendor import of llvm-project master e26a78e70, the last commit beforevendor/llvm-project/llvmorg-10-init-17466-ge26a78e7085
the llvmorg-11-init tag, from which release/10.x was branched.
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp10
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp355
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp121
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp68
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp196
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp557
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp234
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp95
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/Iterator.cpp227
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/Iterator.h175
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp (renamed from clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp)1211
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp273
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp24
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp295
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp425
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/Taint.cpp37
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/Taint.h38
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp25
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallEvent.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerManager.cpp22
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp101
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp44
-rw-r--r--clang/lib/StaticAnalyzer/Core/ProgramState.cpp10
-rw-r--r--clang/lib/StaticAnalyzer/Core/RegionStore.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp80
-rw-r--r--clang/lib/StaticAnalyzer/Core/Store.cpp14
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp1
42 files changed, 3093 insertions, 1616 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
index d0def6918932..2ef50a727ece 100644
--- a/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
@@ -40,6 +40,7 @@ class AnalysisOrderChecker
check::EndFunction,
check::NewAllocator,
check::Bind,
+ check::PointerEscape,
check::RegionChanges,
check::LiveSymbols> {
@@ -165,6 +166,15 @@ public:
llvm::errs() << "RegionChanges\n";
return State;
}
+
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
+ if (isCallbackEnabled(State, "PointerEscape"))
+ llvm::errs() << "PointerEscape\n";
+ return State;
+ }
};
} // end anonymous namespace
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 503c451670b8..21c4bbc60264 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -28,6 +28,7 @@ using namespace clang;
using namespace ento;
namespace {
+enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 };
class CStringChecker : public Checker< eval::Call,
check::PreStmt<DeclStmt>,
check::LiveSymbols,
@@ -129,11 +130,8 @@ public:
void evalStrncpy(CheckerContext &C, const CallExpr *CE) const;
void evalStpcpy(CheckerContext &C, const CallExpr *CE) const;
void evalStrlcpy(CheckerContext &C, const CallExpr *CE) const;
- void evalStrcpyCommon(CheckerContext &C,
- const CallExpr *CE,
- bool returnEnd,
- bool isBounded,
- bool isAppending,
+ void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool ReturnEnd,
+ bool IsBounded, ConcatFnKind appendK,
bool returnPtr = true) const;
void evalStrcat(CheckerContext &C, const CallExpr *CE) const;
@@ -146,8 +144,8 @@ public:
void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const;
void evalStrcmpCommon(CheckerContext &C,
const CallExpr *CE,
- bool isBounded = false,
- bool ignoreCase = false) const;
+ bool IsBounded = false,
+ bool IgnoreCase = false) const;
void evalStrsep(CheckerContext &C, const CallExpr *CE) const;
@@ -292,9 +290,9 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
SmallString<80> buf;
llvm::raw_svector_ostream OS(buf);
assert(CurrentFunctionDescription);
- OS << "Null pointer argument in call to " << CurrentFunctionDescription
- << ' ' << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg)
- << " parameter";
+ OS << "Null pointer passed as " << IdxOfArg
+ << llvm::getOrdinalSuffix(IdxOfArg) << " argument to "
+ << CurrentFunctionDescription;
emitNullArgBug(C, stateNull, S, OS.str());
}
@@ -1008,12 +1006,9 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
const MemRegion *MR) {
- const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR);
-
switch (MR->getKind()) {
case MemRegion::FunctionCodeRegionKind: {
- const NamedDecl *FD = cast<FunctionCodeRegion>(MR)->getDecl();
- if (FD)
+ if (const auto *FD = cast<FunctionCodeRegion>(MR)->getDecl())
os << "the address of the function '" << *FD << '\'';
else
os << "the address of a function";
@@ -1027,16 +1022,20 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
return true;
case MemRegion::CXXThisRegionKind:
case MemRegion::CXXTempObjectRegionKind:
- os << "a C++ temp object of type " << TVR->getValueType().getAsString();
+ os << "a C++ temp object of type "
+ << cast<TypedValueRegion>(MR)->getValueType().getAsString();
return true;
case MemRegion::VarRegionKind:
- os << "a variable of type" << TVR->getValueType().getAsString();
+ os << "a variable of type"
+ << cast<TypedValueRegion>(MR)->getValueType().getAsString();
return true;
case MemRegion::FieldRegionKind:
- os << "a field of type " << TVR->getValueType().getAsString();
+ os << "a field of type "
+ << cast<TypedValueRegion>(MR)->getValueType().getAsString();
return true;
case MemRegion::ObjCIvarRegionKind:
- os << "an instance variable of type " << TVR->getValueType().getAsString();
+ os << "an instance variable of type "
+ << cast<TypedValueRegion>(MR)->getValueType().getAsString();
return true;
default:
return false;
@@ -1315,9 +1314,9 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef StSameBuf, StNotSameBuf;
std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
- // If the two arguments might be the same buffer, we know the result is 0,
+ // If the two arguments are the same buffer, we know the result is 0,
// and we only need to check one size.
- if (StSameBuf) {
+ if (StSameBuf && !StNotSameBuf) {
state = StSameBuf;
state = CheckBufferAccess(C, state, Size, Left);
if (state) {
@@ -1325,20 +1324,19 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
svalBuilder.makeZeroVal(CE->getType()));
C.addTransition(state);
}
+ return;
}
- // If the two arguments might be different buffers, we have to check the
- // size of both of them.
- if (StNotSameBuf) {
- state = StNotSameBuf;
- state = CheckBufferAccess(C, state, Size, Left, Right);
- if (state) {
- // The return value is the comparison result, which we don't know.
- SVal CmpV = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
- C.blockCount());
- state = state->BindExpr(CE, LCtx, CmpV);
- C.addTransition(state);
- }
+ // If the two arguments might be different buffers, we have to check
+ // the size of both of them.
+ assert(StNotSameBuf);
+ state = CheckBufferAccess(C, state, Size, Left, Right);
+ if (state) {
+ // The return value is the comparison result, which we don't know.
+ SVal CmpV =
+ svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ state = state->BindExpr(CE, LCtx, CmpV);
+ C.addTransition(state);
}
}
}
@@ -1477,69 +1475,71 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {
// char *strcpy(char *restrict dst, const char *restrict src);
evalStrcpyCommon(C, CE,
- /* returnEnd = */ false,
- /* isBounded = */ false,
- /* isAppending = */ false);
+ /* ReturnEnd = */ false,
+ /* IsBounded = */ false,
+ /* appendK = */ ConcatFnKind::none);
}
void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {
// char *strncpy(char *restrict dst, const char *restrict src, size_t n);
evalStrcpyCommon(C, CE,
- /* returnEnd = */ false,
- /* isBounded = */ true,
- /* isAppending = */ false);
+ /* ReturnEnd = */ false,
+ /* IsBounded = */ true,
+ /* appendK = */ ConcatFnKind::none);
}
void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
// char *stpcpy(char *restrict dst, const char *restrict src);
evalStrcpyCommon(C, CE,
- /* returnEnd = */ true,
- /* isBounded = */ false,
- /* isAppending = */ false);
+ /* ReturnEnd = */ true,
+ /* IsBounded = */ false,
+ /* appendK = */ ConcatFnKind::none);
}
void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const {
- // char *strlcpy(char *dst, const char *src, size_t n);
+ // size_t strlcpy(char *dest, const char *src, size_t size);
evalStrcpyCommon(C, CE,
- /* returnEnd = */ true,
- /* isBounded = */ true,
- /* isAppending = */ false,
+ /* ReturnEnd = */ true,
+ /* IsBounded = */ true,
+ /* appendK = */ ConcatFnKind::none,
/* returnPtr = */ false);
}
void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
- //char *strcat(char *restrict s1, const char *restrict s2);
+ // char *strcat(char *restrict s1, const char *restrict s2);
evalStrcpyCommon(C, CE,
- /* returnEnd = */ false,
- /* isBounded = */ false,
- /* isAppending = */ true);
+ /* ReturnEnd = */ false,
+ /* IsBounded = */ false,
+ /* appendK = */ ConcatFnKind::strcat);
}
void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
//char *strncat(char *restrict s1, const char *restrict s2, size_t n);
evalStrcpyCommon(C, CE,
- /* returnEnd = */ false,
- /* isBounded = */ true,
- /* isAppending = */ true);
+ /* ReturnEnd = */ false,
+ /* IsBounded = */ true,
+ /* appendK = */ ConcatFnKind::strcat);
}
void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const {
- // FIXME: strlcat() uses a different rule for bound checking, i.e. 'n' means
- // a different thing as compared to strncat(). This currently causes
- // false positives in the alpha string bound checker.
-
- //char *strlcat(char *s1, const char *s2, size_t n);
+ // size_t strlcat(char *dst, const char *src, size_t size);
+ // It will append at most size - strlen(dst) - 1 bytes,
+ // NULL-terminating the result.
evalStrcpyCommon(C, CE,
- /* returnEnd = */ false,
- /* isBounded = */ true,
- /* isAppending = */ true,
+ /* ReturnEnd = */ false,
+ /* IsBounded = */ true,
+ /* appendK = */ ConcatFnKind::strlcat,
/* returnPtr = */ false);
}
void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
- bool returnEnd, bool isBounded,
- bool isAppending, bool returnPtr) const {
- CurrentFunctionDescription = "string copy function";
+ bool ReturnEnd, bool IsBounded,
+ ConcatFnKind appendK,
+ bool returnPtr) const {
+ if (appendK == ConcatFnKind::none)
+ CurrentFunctionDescription = "string copy function";
+ else
+ CurrentFunctionDescription = "string concatenation function";
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
@@ -1560,6 +1560,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// Get the string length of the source.
SVal strLength = getCStringLength(C, state, srcExpr, srcVal);
+ Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
+
+ // Get the string length of the destination buffer.
+ SVal dstStrLength = getCStringLength(C, state, Dst, DstVal);
+ Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
// If the source isn't a valid C string, give up.
if (strLength.isUndef())
@@ -1576,13 +1581,14 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
SVal maxLastElementIndex = UnknownVal();
const char *boundWarning = nullptr;
- state = CheckOverlap(C, state, isBounded ? CE->getArg(2) : CE->getArg(1), Dst, srcExpr);
+ state = CheckOverlap(C, state, IsBounded ? CE->getArg(2) : CE->getArg(1), Dst,
+ srcExpr);
if (!state)
return;
// If the function is strncpy, strncat, etc... it is bounded.
- if (isBounded) {
+ if (IsBounded) {
// Get the max number of characters to copy.
const Expr *lenExpr = CE->getArg(2);
SVal lenVal = state->getSVal(lenExpr, LCtx);
@@ -1590,57 +1596,100 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// Protect against misdeclared strncpy().
lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr->getType());
- Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();
// If we know both values, we might be able to figure out how much
// we're copying.
if (strLengthNL && lenValNL) {
- ProgramStateRef stateSourceTooLong, stateSourceNotTooLong;
+ switch (appendK) {
+ case ConcatFnKind::none:
+ case ConcatFnKind::strcat: {
+ ProgramStateRef stateSourceTooLong, stateSourceNotTooLong;
+ // Check if the max number to copy is less than the length of the src.
+ // If the bound is equal to the source length, strncpy won't null-
+ // terminate the result!
+ std::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume(
+ svalBuilder
+ .evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy)
+ .castAs<DefinedOrUnknownSVal>());
+
+ if (stateSourceTooLong && !stateSourceNotTooLong) {
+ // Max number to copy is less than the length of the src, so the
+ // actual strLength copied is the max number arg.
+ state = stateSourceTooLong;
+ amountCopied = lenVal;
+
+ } else if (!stateSourceTooLong && stateSourceNotTooLong) {
+ // The source buffer entirely fits in the bound.
+ state = stateSourceNotTooLong;
+ amountCopied = strLength;
+ }
+ break;
+ }
+ case ConcatFnKind::strlcat:
+ if (!dstStrLengthNL)
+ return;
- // Check if the max number to copy is less than the length of the src.
- // If the bound is equal to the source length, strncpy won't null-
- // terminate the result!
- std::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume(
- svalBuilder.evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy)
- .castAs<DefinedOrUnknownSVal>());
+ // amountCopied = min (size - dstLen - 1 , srcLen)
+ SVal freeSpace = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL,
+ *dstStrLengthNL, sizeTy);
+ if (!freeSpace.getAs<NonLoc>())
+ return;
+ freeSpace =
+ svalBuilder.evalBinOp(state, BO_Sub, freeSpace,
+ svalBuilder.makeIntVal(1, sizeTy), sizeTy);
+ Optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>();
+
+ // While unlikely, it is possible that the subtraction is
+ // too complex to compute, let's check whether it succeeded.
+ if (!freeSpaceNL)
+ return;
+ SVal hasEnoughSpace = svalBuilder.evalBinOpNN(
+ state, BO_LE, *strLengthNL, *freeSpaceNL, cmpTy);
+
+ ProgramStateRef TrueState, FalseState;
+ std::tie(TrueState, FalseState) =
+ state->assume(hasEnoughSpace.castAs<DefinedOrUnknownSVal>());
+
+ // srcStrLength <= size - dstStrLength -1
+ if (TrueState && !FalseState) {
+ amountCopied = strLength;
+ }
- if (stateSourceTooLong && !stateSourceNotTooLong) {
- // Max number to copy is less than the length of the src, so the actual
- // strLength copied is the max number arg.
- state = stateSourceTooLong;
- amountCopied = lenVal;
+ // srcStrLength > size - dstStrLength -1
+ if (!TrueState && FalseState) {
+ amountCopied = freeSpace;
+ }
- } else if (!stateSourceTooLong && stateSourceNotTooLong) {
- // The source buffer entirely fits in the bound.
- state = stateSourceNotTooLong;
- amountCopied = strLength;
+ if (TrueState && FalseState)
+ amountCopied = UnknownVal();
+ break;
}
}
-
// We still want to know if the bound is known to be too large.
if (lenValNL) {
- if (isAppending) {
+ switch (appendK) {
+ case ConcatFnKind::strcat:
// For strncat, the check is strlen(dst) + lenVal < sizeof(dst)
// Get the string length of the destination. If the destination is
// memory that can't have a string length, we shouldn't be copying
// into it anyway.
- SVal dstStrLength = getCStringLength(C, state, Dst, DstVal);
if (dstStrLength.isUndef())
return;
- if (Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>()) {
- maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Add,
- *lenValNL,
- *dstStrLengthNL,
- sizeTy);
+ if (dstStrLengthNL) {
+ maxLastElementIndex = svalBuilder.evalBinOpNN(
+ state, BO_Add, *lenValNL, *dstStrLengthNL, sizeTy);
+
boundWarning = "Size argument is greater than the free space in the "
"destination buffer";
}
-
- } else {
- // For strncpy, this is just checking that lenVal <= sizeof(dst)
+ break;
+ case ConcatFnKind::none:
+ case ConcatFnKind::strlcat:
+ // For strncpy and strlcat, this is just checking
+ // that lenVal <= sizeof(dst).
// (Yes, strncpy and strncat differ in how they treat termination.
// strncat ALWAYS terminates, but strncpy doesn't.)
@@ -1649,14 +1698,22 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// as the last element accessed, so n == 0 is problematic.
ProgramStateRef StateZeroSize, StateNonZeroSize;
std::tie(StateZeroSize, StateNonZeroSize) =
- assumeZero(C, state, *lenValNL, sizeTy);
+ assumeZero(C, state, *lenValNL, sizeTy);
// If the size is known to be zero, we're done.
if (StateZeroSize && !StateNonZeroSize) {
if (returnPtr) {
StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal);
} else {
- StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, *lenValNL);
+ if (appendK == ConcatFnKind::none) {
+ // strlcpy returns strlen(src)
+ StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, strLength);
+ } else {
+ // strlcat returns strlen(src) + strlen(dst)
+ SVal retSize = svalBuilder.evalBinOp(
+ state, BO_Add, strLength, dstStrLength, sizeTy);
+ StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, retSize);
+ }
}
C.addTransition(StateZeroSize);
return;
@@ -1666,50 +1723,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// We don't record the non-zero assumption here because we can't
// be sure. We won't warn on a possible zero.
NonLoc one = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
- maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL,
- one, sizeTy);
+ maxLastElementIndex =
+ svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, one, sizeTy);
boundWarning = "Size argument is greater than the length of the "
"destination buffer";
+ break;
}
}
-
- // If we couldn't pin down the copy length, at least bound it.
- // FIXME: We should actually run this code path for append as well, but
- // right now it creates problems with constraints (since we can end up
- // trying to pass constraints from symbol to symbol).
- if (amountCopied.isUnknown() && !isAppending) {
- // Try to get a "hypothetical" string length symbol, which we can later
- // set as a real value if that turns out to be the case.
- amountCopied = getCStringLength(C, state, lenExpr, srcVal, true);
- assert(!amountCopied.isUndef());
-
- if (Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>()) {
- if (lenValNL) {
- // amountCopied <= lenVal
- SVal copiedLessThanBound = svalBuilder.evalBinOpNN(state, BO_LE,
- *amountCopiedNL,
- *lenValNL,
- cmpTy);
- state = state->assume(
- copiedLessThanBound.castAs<DefinedOrUnknownSVal>(), true);
- if (!state)
- return;
- }
-
- if (strLengthNL) {
- // amountCopied <= strlen(source)
- SVal copiedLessThanSrc = svalBuilder.evalBinOpNN(state, BO_LE,
- *amountCopiedNL,
- *strLengthNL,
- cmpTy);
- state = state->assume(
- copiedLessThanSrc.castAs<DefinedOrUnknownSVal>(), true);
- if (!state)
- return;
- }
- }
- }
-
} else {
// The function isn't bounded. The amount copied should match the length
// of the source buffer.
@@ -1722,28 +1742,37 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// buffer. (It may not actually be the strlen if the destination buffer
// is not terminated.)
SVal finalStrLength = UnknownVal();
+ SVal strlRetVal = UnknownVal();
+
+ if (appendK == ConcatFnKind::none && !returnPtr) {
+ // strlcpy returns the sizeof(src)
+ strlRetVal = strLength;
+ }
// If this is an appending function (strcat, strncat...) then set the
// string length to strlen(src) + strlen(dst) since the buffer will
// ultimately contain both.
- if (isAppending) {
+ if (appendK != ConcatFnKind::none) {
// Get the string length of the destination. If the destination is memory
// that can't have a string length, we shouldn't be copying into it anyway.
- SVal dstStrLength = getCStringLength(C, state, Dst, DstVal);
if (dstStrLength.isUndef())
return;
- Optional<NonLoc> srcStrLengthNL = amountCopied.getAs<NonLoc>();
- Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
+ if (appendK == ConcatFnKind::strlcat && dstStrLengthNL && strLengthNL) {
+ strlRetVal = svalBuilder.evalBinOpNN(state, BO_Add, *strLengthNL,
+ *dstStrLengthNL, sizeTy);
+ }
+
+ Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>();
// If we know both string lengths, we might know the final string length.
- if (srcStrLengthNL && dstStrLengthNL) {
+ if (amountCopiedNL && dstStrLengthNL) {
// Make sure the two lengths together don't overflow a size_t.
- state = checkAdditionOverflow(C, state, *srcStrLengthNL, *dstStrLengthNL);
+ state = checkAdditionOverflow(C, state, *amountCopiedNL, *dstStrLengthNL);
if (!state)
return;
- finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *srcStrLengthNL,
+ finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *amountCopiedNL,
*dstStrLengthNL, sizeTy);
}
@@ -1756,19 +1785,19 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
assert(!finalStrLength.isUndef());
if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) {
- if (srcStrLengthNL) {
+ if (amountCopiedNL && appendK == ConcatFnKind::none) {
+ // we overwrite dst string with the src
// finalStrLength >= srcStrLength
- SVal sourceInResult = svalBuilder.evalBinOpNN(state, BO_GE,
- *finalStrLengthNL,
- *srcStrLengthNL,
- cmpTy);
+ SVal sourceInResult = svalBuilder.evalBinOpNN(
+ state, BO_GE, *finalStrLengthNL, *amountCopiedNL, cmpTy);
state = state->assume(sourceInResult.castAs<DefinedOrUnknownSVal>(),
true);
if (!state)
return;
}
- if (dstStrLengthNL) {
+ if (dstStrLengthNL && appendK != ConcatFnKind::none) {
+ // we extend the dst string with the src
// finalStrLength >= dstStrLength
SVal destInResult = svalBuilder.evalBinOpNN(state, BO_GE,
*finalStrLengthNL,
@@ -1793,9 +1822,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
if (returnPtr) {
// The final result of the function will either be a pointer past the last
// copied element, or a pointer to the start of the destination buffer.
- Result = (returnEnd ? UnknownVal() : DstVal);
+ Result = (ReturnEnd ? UnknownVal() : DstVal);
} else {
- Result = finalStrLength;
+ if (appendK == ConcatFnKind::strlcat || appendK == ConcatFnKind::none)
+ //strlcpy, strlcat
+ Result = strlRetVal;
+ else
+ Result = finalStrLength;
}
assert(state);
@@ -1834,7 +1867,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
}
// If this is a stpcpy-style copy, the last element is the return value.
- if (returnPtr && returnEnd)
+ if (returnPtr && ReturnEnd)
Result = lastElement;
}
@@ -1854,7 +1887,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
nullptr);
// Set the C string length of the destination, if we know it.
- if (isBounded && !isAppending) {
+ if (IsBounded && (appendK == ConcatFnKind::none)) {
// strncpy is annoying in that it doesn't guarantee to null-terminate
// the result string. If the original string didn't fit entirely inside
// the bound (including the null-terminator), we don't know how long the
@@ -1870,7 +1903,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
if (returnPtr) {
// If this is a stpcpy-style copy, but we were unable to check for a buffer
// overflow, we still need a result. Conjure a return value.
- if (returnEnd && Result.isUnknown()) {
+ if (ReturnEnd && Result.isUnknown()) {
Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
}
}
@@ -1881,28 +1914,28 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const {
//int strcmp(const char *s1, const char *s2);
- evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ false);
+ evalStrcmpCommon(C, CE, /* IsBounded = */ false, /* IgnoreCase = */ false);
}
void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const {
//int strncmp(const char *s1, const char *s2, size_t n);
- evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false);
+ evalStrcmpCommon(C, CE, /* IsBounded = */ true, /* IgnoreCase = */ false);
}
void CStringChecker::evalStrcasecmp(CheckerContext &C,
const CallExpr *CE) const {
//int strcasecmp(const char *s1, const char *s2);
- evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true);
+ evalStrcmpCommon(C, CE, /* IsBounded = */ false, /* IgnoreCase = */ true);
}
void CStringChecker::evalStrncasecmp(CheckerContext &C,
const CallExpr *CE) const {
//int strncasecmp(const char *s1, const char *s2, size_t n);
- evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ true);
+ evalStrcmpCommon(C, CE, /* IsBounded = */ true, /* IgnoreCase = */ true);
}
void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
- bool isBounded, bool ignoreCase) const {
+ bool IsBounded, bool IgnoreCase) const {
CurrentFunctionDescription = "string comparison function";
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
@@ -1972,7 +2005,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
StringRef s1StrRef = s1StrLiteral->getString();
StringRef s2StrRef = s2StrLiteral->getString();
- if (isBounded) {
+ if (IsBounded) {
// Get the max number of characters to compare.
const Expr *lenExpr = CE->getArg(2);
SVal lenVal = state->getSVal(lenExpr, LCtx);
@@ -2000,7 +2033,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
s2StrRef = s2StrRef.substr(0, s2Term);
// Use StringRef's comparison methods to compute the actual result.
- int compareRes = ignoreCase ? s1StrRef.compare_lower(s2StrRef)
+ int compareRes = IgnoreCase ? s1StrRef.compare_lower(s2StrRef)
: s1StrRef.compare(s2StrRef);
// The strcmp function returns an integer greater than, equal to, or less
@@ -2180,7 +2213,7 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy);
ProgramStateRef State = C.getState();
-
+
// See if the size argument is zero.
SVal SizeVal = C.getSVal(Size);
QualType SizeTy = Size->getType();
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
new file mode 100644
index 000000000000..48fee4a0ffb7
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
@@ -0,0 +1,121 @@
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
+public:
+ void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+
+private:
+ // Returns the size of the target in a placement new expression.
+ // E.g. in "new (&s) long" it returns the size of `long`.
+ SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, ProgramStateRef State,
+ CheckerContext &C) const;
+ // Returns the size of the place in a placement new expression.
+ // E.g. in "new (&s) long" it returns the size of `s`.
+ SVal getExtentSizeOfPlace(const Expr *NE, ProgramStateRef State,
+ CheckerContext &C) const;
+ BugType BT{this, "Insufficient storage for placement new",
+ categories::MemoryError};
+};
+} // namespace
+
+SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place,
+ ProgramStateRef State,
+ CheckerContext &C) const {
+ const MemRegion *MRegion = C.getSVal(Place).getAsRegion();
+ if (!MRegion)
+ return UnknownVal();
+ RegionOffset Offset = MRegion->getAsOffset();
+ if (Offset.hasSymbolicOffset())
+ return UnknownVal();
+ const MemRegion *BaseRegion = MRegion->getBaseRegion();
+ if (!BaseRegion)
+ return UnknownVal();
+
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex(
+ Offset.getOffset() / C.getASTContext().getCharWidth());
+ DefinedOrUnknownSVal ExtentInBytes =
+ BaseRegion->castAs<SubRegion>()->getExtent(SvalBuilder);
+
+ return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub,
+ ExtentInBytes, OffsetInBytes,
+ SvalBuilder.getArrayIndexType());
+}
+
+SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
+ ProgramStateRef State,
+ CheckerContext &C) const {
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ QualType ElementType = NE->getAllocatedType();
+ ASTContext &AstContext = C.getASTContext();
+ CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
+ if (NE->isArray()) {
+ const Expr *SizeExpr = *NE->getArraySize();
+ SVal ElementCount = C.getSVal(SizeExpr);
+ if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
+ // size in Bytes = ElementCountNL * TypeSize
+ return SvalBuilder.evalBinOp(
+ State, BO_Mul, *ElementCountNL,
+ SvalBuilder.makeArrayIndex(TypeSize.getQuantity()),
+ SvalBuilder.getArrayIndexType());
+ }
+ } else {
+ // Create a concrete int whose size in bits and signedness is equal to
+ // ArrayIndexType.
+ llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())
+ .getQuantity() *
+ C.getASTContext().getCharWidth(),
+ TypeSize.getQuantity());
+ return SvalBuilder.makeArrayIndex(I.getZExtValue());
+ }
+ return UnknownVal();
+}
+
+void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
+ CheckerContext &C) const {
+ // Check only the default placement new.
+ if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
+ return;
+ if (NE->getNumPlacementArgs() == 0)
+ return;
+
+ ProgramStateRef State = C.getState();
+ SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, State, C);
+ const Expr *Place = NE->getPlacementArg(0);
+ SVal SizeOfPlace = getExtentSizeOfPlace(Place, State, C);
+ const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
+ if (!SizeOfTargetCI)
+ return;
+ const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
+ if (!SizeOfPlaceCI)
+ return;
+
+ if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ std::string Msg =
+ llvm::formatv("Storage provided to placement new is only {0} bytes, "
+ "whereas the allocated type requires {1} bytes",
+ SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue());
+
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+ bugreporter::trackExpressionValue(N, Place, *R);
+ C.emitReport(std::move(R));
+ return;
+ }
+ }
+}
+
+void ento::registerPlacementNewChecker(CheckerManager &mgr) {
+ mgr.registerChecker<PlacementNewChecker>();
+}
+
+bool ento::shouldRegisterPlacementNewChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 260a2896e78c..d9ffa562c0aa 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -49,6 +49,7 @@ struct ChecksFilter {
DefaultBool check_vfork;
DefaultBool check_FloatLoopCounter;
DefaultBool check_UncheckedReturn;
+ DefaultBool check_decodeValueOfObjCType;
CheckerNameRef checkName_bcmp;
CheckerNameRef checkName_bcopy;
@@ -63,6 +64,7 @@ struct ChecksFilter {
CheckerNameRef checkName_vfork;
CheckerNameRef checkName_FloatLoopCounter;
CheckerNameRef checkName_UncheckedReturn;
+ CheckerNameRef checkName_decodeValueOfObjCType;
};
class WalkAST : public StmtVisitor<WalkAST> {
@@ -83,6 +85,7 @@ public:
// Statement visitor methods.
void VisitCallExpr(CallExpr *CE);
+ void VisitObjCMessageExpr(ObjCMessageExpr *CE);
void VisitForStmt(ForStmt *S);
void VisitCompoundStmt (CompoundStmt *S);
void VisitStmt(Stmt *S) { VisitChildren(S); }
@@ -93,6 +96,7 @@ public:
bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
+ typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);
// Checker-specific methods.
void checkLoopConditionForFloat(const ForStmt *FS);
@@ -110,6 +114,7 @@ public:
void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
+ void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME);
void checkUncheckedReturnValue(CallExpr *CE);
};
} // end anonymous namespace
@@ -182,6 +187,20 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
VisitChildren(CE);
}
+void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
+ MsgCheck evalFunction =
+ llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())
+ .Case("decodeValueOfObjCType:at:",
+ &WalkAST::checkMsg_decodeValueOfObjCType)
+ .Default(nullptr);
+
+ if (evalFunction)
+ (this->*evalFunction)(ME);
+
+ // Recurse and check children.
+ VisitChildren(ME);
+}
+
void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
for (Stmt *Child : S->children())
if (Child) {
@@ -924,6 +943,54 @@ void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
}
//===----------------------------------------------------------------------===//
+// Check: '-decodeValueOfObjCType:at:' should not be used.
+// It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
+// likelihood of buffer overflows.
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
+ if (!filter.check_decodeValueOfObjCType)
+ return;
+
+ // Check availability of the secure alternative:
+ // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
+ // FIXME: We probably shouldn't register the check if it's not available.
+ const TargetInfo &TI = AC->getASTContext().getTargetInfo();
+ const llvm::Triple &T = TI.getTriple();
+ const VersionTuple &VT = TI.getPlatformMinVersion();
+ switch (T.getOS()) {
+ case llvm::Triple::IOS:
+ if (VT < VersionTuple(11, 0))
+ return;
+ break;
+ case llvm::Triple::MacOSX:
+ if (VT < VersionTuple(10, 13))
+ return;
+ break;
+ case llvm::Triple::WatchOS:
+ if (VT < VersionTuple(4, 0))
+ return;
+ break;
+ case llvm::Triple::TvOS:
+ if (VT < VersionTuple(11, 0))
+ return;
+ break;
+ default:
+ return;
+ }
+
+ PathDiagnosticLocation MELoc =
+ PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(
+ AC->getDecl(), filter.checkName_decodeValueOfObjCType,
+ "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",
+ "Deprecated method '-decodeValueOfObjCType:at:' is insecure "
+ "as it can lead to potential buffer overflows. Use the safer "
+ "'-decodeValueOfObjCType:at:size:' method.",
+ MELoc, ME->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
// Check: Should check whether privileges are dropped successfully.
// Originally: <rdar://problem/6337132>
//===----------------------------------------------------------------------===//
@@ -1035,3 +1102,4 @@ REGISTER_CHECKER(vfork)
REGISTER_CHECKER(FloatLoopCounter)
REGISTER_CHECKER(UncheckedReturn)
REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
+REGISTER_CHECKER(decodeValueOfObjCType)
diff --git a/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
new file mode 100644
index 000000000000..4717fef96341
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
@@ -0,0 +1,196 @@
+//===-- DebugIteratorModeling.cpp ---------------------------------*- C++ -*--//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for debugging iterator modeling.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+#include "Iterator.h"
+
+using namespace clang;
+using namespace ento;
+using namespace iterator;
+
+namespace {
+
+class DebugIteratorModeling
+ : public Checker<eval::Call> {
+
+ std::unique_ptr<BugType> DebugMsgBugType;
+
+ template <typename Getter>
+ void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
+ Getter get) const;
+ void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
+ template <typename Getter>
+ void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
+ Getter get, SVal Default) const;
+ void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
+ ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
+
+ typedef void (DebugIteratorModeling::*FnCheck)(const CallExpr *,
+ CheckerContext &) const;
+
+ CallDescriptionMap<FnCheck> Callbacks = {
+ {{0, "clang_analyzer_container_begin", 1},
+ &DebugIteratorModeling::analyzerContainerBegin},
+ {{0, "clang_analyzer_container_end", 1},
+ &DebugIteratorModeling::analyzerContainerEnd},
+ {{0, "clang_analyzer_iterator_position", 1},
+ &DebugIteratorModeling::analyzerIteratorPosition},
+ {{0, "clang_analyzer_iterator_container", 1},
+ &DebugIteratorModeling::analyzerIteratorContainer},
+ {{0, "clang_analyzer_iterator_validity", 1},
+ &DebugIteratorModeling::analyzerIteratorValidity},
+ };
+
+public:
+ DebugIteratorModeling();
+
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+};
+
+} //namespace
+
+DebugIteratorModeling::DebugIteratorModeling() {
+ DebugMsgBugType.reset(
+ new BugType(this, "Checking analyzer assumptions", "debug",
+ /*SuppressOnSink=*/true));
+}
+
+bool DebugIteratorModeling::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
+
+ const FnCheck *Handler = Callbacks.lookup(Call);
+ if (!Handler)
+ return false;
+
+ (this->**Handler)(CE, C);
+ return true;
+}
+
+template <typename Getter>
+void DebugIteratorModeling::analyzerContainerDataField(const CallExpr *CE,
+ CheckerContext &C,
+ Getter get) const {
+ if (CE->getNumArgs() == 0) {
+ reportDebugMsg("Missing container argument", C);
+ return;
+ }
+
+ auto State = C.getState();
+ const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
+ if (Cont) {
+ const auto *Data = getContainerData(State, Cont);
+ if (Data) {
+ SymbolRef Field = get(Data);
+ if (Field) {
+ State = State->BindExpr(CE, C.getLocationContext(),
+ nonloc::SymbolVal(Field));
+ C.addTransition(State);
+ return;
+ }
+ }
+ }
+
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ State = State->BindExpr(CE, C.getLocationContext(),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
+}
+
+void DebugIteratorModeling::analyzerContainerBegin(const CallExpr *CE,
+ CheckerContext &C) const {
+ analyzerContainerDataField(CE, C, [](const ContainerData *D) {
+ return D->getBegin();
+ });
+}
+
+void DebugIteratorModeling::analyzerContainerEnd(const CallExpr *CE,
+ CheckerContext &C) const {
+ analyzerContainerDataField(CE, C, [](const ContainerData *D) {
+ return D->getEnd();
+ });
+}
+
+template <typename Getter>
+void DebugIteratorModeling::analyzerIteratorDataField(const CallExpr *CE,
+ CheckerContext &C,
+ Getter get,
+ SVal Default) const {
+ if (CE->getNumArgs() == 0) {
+ reportDebugMsg("Missing iterator argument", C);
+ return;
+ }
+
+ auto State = C.getState();
+ SVal V = C.getSVal(CE->getArg(0));
+ const auto *Pos = getIteratorPosition(State, V);
+ if (Pos) {
+ State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
+ } else {
+ State = State->BindExpr(CE, C.getLocationContext(), Default);
+ }
+ C.addTransition(State);
+}
+
+void DebugIteratorModeling::analyzerIteratorPosition(const CallExpr *CE,
+ CheckerContext &C) const {
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
+ return nonloc::SymbolVal(P->getOffset());
+ }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
+}
+
+void DebugIteratorModeling::analyzerIteratorContainer(const CallExpr *CE,
+ CheckerContext &C) const {
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
+ return loc::MemRegionVal(P->getContainer());
+ }, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
+}
+
+void DebugIteratorModeling::analyzerIteratorValidity(const CallExpr *CE,
+ CheckerContext &C) const {
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
+ return
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
+ }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
+}
+
+ExplodedNode *DebugIteratorModeling::reportDebugMsg(llvm::StringRef Msg,
+ CheckerContext &C) const {
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ return nullptr;
+
+ auto &BR = C.getBugReporter();
+ BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
+ Msg, N));
+ return N;
+}
+
+void ento::registerDebugIteratorModeling(CheckerManager &mgr) {
+ mgr.registerChecker<DebugIteratorModeling>();
+}
+
+bool ento::shouldRegisterDebugIteratorModeling(const LangOptions &LO) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index e3de0b4f4a7f..46100cd1dace 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -107,7 +107,7 @@ static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
static bool suppressReport(const Expr *E) {
// Do not report dereferences on memory in non-default address spaces.
- return E->getType().getQualifiers().hasAddressSpace();
+ return E->getType().hasAddressSpace();
}
static bool isDeclRefExprToReference(const Expr *E) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
index 0058f3d3881f..0c46447e1985 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
@@ -144,6 +144,8 @@ void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,
continue;
const Stmt *Body = M->getBody();
+ if (M->isSynthesizedAccessorStub())
+ continue;
assert(Body);
MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
diff --git a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
new file mode 100644
index 000000000000..3c04983df443
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
@@ -0,0 +1,557 @@
+//=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- C++ -*--=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker checks if the handle of Fuchsia is properly used according to
+// following rules.
+// - If a handle is acquired, it should be released before execution
+// ends.
+// - If a handle is released, it should not be released again.
+// - If a handle is released, it should not be used for other purposes
+// such as I/O.
+//
+// In this checker, each tracked handle is associated with a state. When the
+// handle variable is passed to different function calls or syscalls, its state
+// changes. The state changes can be generally represented by following ASCII
+// Art:
+//
+//
+// +-+---------v-+ +------------+
+// acquire_func succeeded | | Escape | |
+// +-----------------> Allocated +---------> Escaped <--+
+// | | | | | |
+// | +-----+------++ +------------+ |
+// | | | |
+// | release_func | +--+ |
+// | | | handle +--------+ |
+// | | | dies | | |
+// | +----v-----+ +---------> Leaked | |
+// | | | |(REPORT)| |
+// +----------+--+ | Released | Escape +--------+ |
+// | | | +---------------------------+
+// | Not tracked <--+ +----+---+-+
+// | | | | | As argument by value
+// +------+------+ | release_func | +------+ in function call
+// | | | | or by reference in
+// | | | | use_func call
+// +---------+ +----v-----+ | +-----------+
+// acquire_func failed | Double | +-----> Use after |
+// | released | | released |
+// | (REPORT) | | (REPORT) |
+// +----------+ +-----------+
+//
+// acquire_func represents the functions or syscalls that may acquire a handle.
+// release_func represents the functions or syscalls that may release a handle.
+// use_func represents the functions or syscall that requires an open handle.
+//
+// If a tracked handle dies in "Released" or "Not Tracked" state, we assume it
+// is properly used. Otherwise a bug and will be reported.
+//
+// Note that, the analyzer does not always know for sure if a function failed
+// or succeeded. In those cases we use the state MaybeAllocated.
+// Thus, the diagramm above captures the intent, not implementation details.
+//
+// Due to the fact that the number of handle related syscalls in Fuchsia
+// is large, we adopt the annotation attributes to descript syscalls'
+// operations(acquire/release/use) on handles instead of hardcoding
+// everything in the checker.
+//
+// We use following annotation attributes for handle related syscalls or
+// functions:
+// 1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired
+// 2. __attribute__((release_handle("Fuchsia"))) |handle will be released
+// 3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to
+// escaped state, it also needs to be open.
+//
+// For example, an annotated syscall:
+// zx_status_t zx_channel_create(
+// uint32_t options,
+// zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) ,
+// zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia"))));
+// denotes a syscall which will acquire two handles and save them to 'out0' and
+// 'out1' when succeeded.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Type.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+static const StringRef HandleTypeName = "zx_handle_t";
+static const StringRef ErrorTypeName = "zx_status_t";
+
+class HandleState {
+private:
+ enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K;
+ SymbolRef ErrorSym;
+ HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
+
+public:
+ bool operator==(const HandleState &Other) const {
+ return K == Other.K && ErrorSym == Other.ErrorSym;
+ }
+ bool isAllocated() const { return K == Kind::Allocated; }
+ bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
+ bool isReleased() const { return K == Kind::Released; }
+ bool isEscaped() const { return K == Kind::Escaped; }
+
+ static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
+ return HandleState(Kind::MaybeAllocated, ErrorSym);
+ }
+ static HandleState getAllocated(ProgramStateRef State, HandleState S) {
+ assert(S.maybeAllocated());
+ assert(State->getConstraintManager()
+ .isNull(State, S.getErrorSym())
+ .isConstrained());
+ return HandleState(Kind::Allocated, nullptr);
+ }
+ static HandleState getReleased() {
+ return HandleState(Kind::Released, nullptr);
+ }
+ static HandleState getEscaped() {
+ return HandleState(Kind::Escaped, nullptr);
+ }
+
+ SymbolRef getErrorSym() const { return ErrorSym; }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(static_cast<int>(K));
+ ID.AddPointer(ErrorSym);
+ }
+
+ LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
+ switch (K) {
+#define CASE(ID) \
+ case ID: \
+ OS << #ID; \
+ break;
+ CASE(Kind::MaybeAllocated)
+ CASE(Kind::Allocated)
+ CASE(Kind::Released)
+ CASE(Kind::Escaped)
+ }
+ }
+
+ LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
+};
+
+template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
+ return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
+}
+
+class FuchsiaHandleChecker
+ : public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
+ check::PointerEscape, eval::Assume> {
+ BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error",
+ /*SuppressOnSink=*/true};
+ BugType DoubleReleaseBugType{this, "Fuchsia handle double release",
+ "Fuchsia Handle Error"};
+ BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",
+ "Fuchsia Handle Error"};
+
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
+ bool Assumption) const;
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+
+ ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
+ CheckerContext &C, ExplodedNode *Pred) const;
+
+ void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
+ CheckerContext &C) const;
+
+ void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
+ CheckerContext &C) const;
+
+ void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C,
+ const SourceRange *Range, const BugType &Type,
+ StringRef Msg) const;
+
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
+};
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState)
+
+static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
+ CheckerContext &Ctx) {
+ ProgramStateRef State = N->getState();
+ // When bug type is handle leak, exploded node N does not have state info for
+ // leaking handle. Get the predecessor of N instead.
+ if (!State->get<HStateMap>(Sym))
+ N = N->getFirstPred();
+
+ const ExplodedNode *Pred = N;
+ while (N) {
+ State = N->getState();
+ if (!State->get<HStateMap>(Sym)) {
+ const HandleState *HState = Pred->getState()->get<HStateMap>(Sym);
+ if (HState && (HState->isAllocated() || HState->maybeAllocated()))
+ return N;
+ }
+ Pred = N;
+ N = N->getFirstPred();
+ }
+ return nullptr;
+}
+
+/// Returns the symbols extracted from the argument or null if it cannot be
+/// found.
+static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg,
+ ProgramStateRef State) {
+ int PtrToHandleLevel = 0;
+ while (QT->isAnyPointerType() || QT->isReferenceType()) {
+ ++PtrToHandleLevel;
+ QT = QT->getPointeeType();
+ }
+ if (const auto *HandleType = QT->getAs<TypedefType>()) {
+ if (HandleType->getDecl()->getName() != HandleTypeName)
+ return nullptr;
+ if (PtrToHandleLevel > 1) {
+ // Not supported yet.
+ return nullptr;
+ }
+
+ if (PtrToHandleLevel == 0) {
+ return Arg.getAsSymbol();
+ } else {
+ assert(PtrToHandleLevel == 1);
+ if (Optional<Loc> ArgLoc = Arg.getAs<Loc>())
+ return State->getSVal(*ArgLoc).getAsSymbol();
+ }
+ }
+ return nullptr;
+}
+
+void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FuncDecl) {
+ // Unknown call, escape by value handles. They are not covered by
+ // PointerEscape callback.
+ for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
+ if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol())
+ State = State->set<HStateMap>(Handle, HandleState::getEscaped());
+ }
+ C.addTransition(State);
+ return;
+ }
+
+ for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
+ if (Arg >= FuncDecl->getNumParams())
+ break;
+ const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
+ SymbolRef Handle =
+ getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
+ if (!Handle)
+ continue;
+
+ // Handled in checkPostCall.
+ if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
+ hasFuchsiaAttr<AcquireHandleAttr>(PVD))
+ continue;
+
+ const HandleState *HState = State->get<HStateMap>(Handle);
+ if (!HState || HState->isEscaped())
+ continue;
+
+ if (hasFuchsiaAttr<UseHandleAttr>(PVD) || PVD->getType()->isIntegerType()) {
+ if (HState->isReleased()) {
+ reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
+ return;
+ }
+ }
+ if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
+ PVD->getType()->isIntegerType()) {
+ // Working around integer by-value escapes.
+ State = State->set<HStateMap>(Handle, HandleState::getEscaped());
+ }
+ }
+ C.addTransition(State);
+}
+
+void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FuncDecl)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ std::vector<std::function<std::string(BugReport & BR)>> Notes;
+ SymbolRef ResultSymbol = nullptr;
+ if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>())
+ if (TypeDefTy->getDecl()->getName() == ErrorTypeName)
+ ResultSymbol = Call.getReturnValue().getAsSymbol();
+
+ // Function returns an open handle.
+ if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
+ SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
+ State =
+ State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
+ }
+
+ for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
+ if (Arg >= FuncDecl->getNumParams())
+ break;
+ const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
+ SymbolRef Handle =
+ getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
+ if (!Handle)
+ continue;
+
+ const HandleState *HState = State->get<HStateMap>(Handle);
+ if (HState && HState->isEscaped())
+ continue;
+ if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
+ if (HState && HState->isReleased()) {
+ reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
+ return;
+ } else {
+ Notes.push_back([Handle](BugReport &BR) {
+ auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
+ if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
+ return "Handle released here.";
+ } else
+ return "";
+ });
+ State = State->set<HStateMap>(Handle, HandleState::getReleased());
+ }
+ } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
+ Notes.push_back([Handle](BugReport &BR) {
+ auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
+ if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
+ return "Handle allocated here.";
+ } else
+ return "";
+ });
+ State = State->set<HStateMap>(
+ Handle, HandleState::getMaybeAllocated(ResultSymbol));
+ }
+ }
+ const NoteTag *T = nullptr;
+ if (!Notes.empty()) {
+ T = C.getNoteTag(
+ [this, Notes{std::move(Notes)}](BugReport &BR) -> std::string {
+ if (&BR.getBugType() != &UseAfterReleaseBugType &&
+ &BR.getBugType() != &LeakBugType &&
+ &BR.getBugType() != &DoubleReleaseBugType)
+ return "";
+ for (auto &Note : Notes) {
+ std::string Text = Note(BR);
+ if (!Text.empty())
+ return Text;
+ }
+ return "";
+ });
+ }
+ C.addTransition(State, T);
+}
+
+void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SmallVector<SymbolRef, 2> LeakedSyms;
+ HStateMapTy TrackedHandles = State->get<HStateMap>();
+ for (auto &CurItem : TrackedHandles) {
+ if (!SymReaper.isDead(CurItem.first))
+ continue;
+ if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
+ LeakedSyms.push_back(CurItem.first);
+ State = State->remove<HStateMap>(CurItem.first);
+ }
+
+ ExplodedNode *N = C.getPredecessor();
+ if (!LeakedSyms.empty())
+ N = reportLeaks(LeakedSyms, C, N);
+
+ C.addTransition(State, N);
+}
+
+// Acquiring a handle is not always successful. In Fuchsia most functions
+// return a status code that determines the status of the handle.
+// When we split the path based on this status code we know that on one
+// path we do have the handle and on the other path the acquire failed.
+// This method helps avoiding false positive leak warnings on paths where
+// the function failed.
+// Moreover, when a handle is known to be zero (the invalid handle),
+// we no longer can follow the symbol on the path, becaue the constant
+// zero will be used instead of the symbol. We also do not need to release
+// an invalid handle, so we remove the corresponding symbol from the state.
+ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State,
+ SVal Cond,
+ bool Assumption) const {
+ // TODO: add notes about successes/fails for APIs.
+ ConstraintManager &Cmr = State->getConstraintManager();
+ HStateMapTy TrackedHandles = State->get<HStateMap>();
+ for (auto &CurItem : TrackedHandles) {
+ ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first);
+ if (HandleVal.isConstrainedTrue()) {
+ // The handle is invalid. We can no longer follow the symbol on this path.
+ State = State->remove<HStateMap>(CurItem.first);
+ }
+ SymbolRef ErrorSym = CurItem.second.getErrorSym();
+ if (!ErrorSym)
+ continue;
+ ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym);
+ if (ErrorVal.isConstrainedTrue()) {
+ // Allocation succeeded.
+ if (CurItem.second.maybeAllocated())
+ State = State->set<HStateMap>(
+ CurItem.first, HandleState::getAllocated(State, CurItem.second));
+ } else if (ErrorVal.isConstrainedFalse()) {
+ // Allocation failed.
+ if (CurItem.second.maybeAllocated())
+ State = State->remove<HStateMap>(CurItem.first);
+ }
+ }
+ return State;
+}
+
+ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
+ ProgramStateRef State, const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind) const {
+ const FunctionDecl *FuncDecl =
+ Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl()) : nullptr;
+
+ llvm::DenseSet<SymbolRef> UnEscaped;
+ // Not all calls should escape our symbols.
+ if (FuncDecl &&
+ (Kind == PSK_DirectEscapeOnCall || Kind == PSK_IndirectEscapeOnCall ||
+ Kind == PSK_EscapeOutParameters)) {
+ for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg) {
+ if (Arg >= FuncDecl->getNumParams())
+ break;
+ const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
+ SymbolRef Handle =
+ getFuchsiaHandleSymbol(PVD->getType(), Call->getArgSVal(Arg), State);
+ if (!Handle)
+ continue;
+ if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
+ hasFuchsiaAttr<ReleaseHandleAttr>(PVD))
+ UnEscaped.insert(Handle);
+ }
+ }
+
+ // For out params, we have to deal with derived symbols. See
+ // MacOSKeychainAPIChecker for details.
+ for (auto I : State->get<HStateMap>()) {
+ if (Escaped.count(I.first) && !UnEscaped.count(I.first))
+ State = State->set<HStateMap>(I.first, HandleState::getEscaped());
+ if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) {
+ auto ParentSym = SD->getParentSymbol();
+ if (Escaped.count(ParentSym))
+ State = State->set<HStateMap>(I.first, HandleState::getEscaped());
+ }
+ }
+
+ return State;
+}
+
+ExplodedNode *
+FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles,
+ CheckerContext &C, ExplodedNode *Pred) const {
+ ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred);
+ for (SymbolRef LeakedHandle : LeakedHandles) {
+ reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType,
+ "Potential leak of handle");
+ }
+ return ErrNode;
+}
+
+void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
+ const SourceRange &Range,
+ CheckerContext &C) const {
+ ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
+ reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType,
+ "Releasing a previously released handle");
+}
+
+void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
+ const SourceRange &Range,
+ CheckerContext &C) const {
+ ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
+ reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType,
+ "Using a previously released handle");
+}
+
+void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
+ CheckerContext &C,
+ const SourceRange *Range,
+ const BugType &Type, StringRef Msg) const {
+ if (!ErrorNode)
+ return;
+
+ std::unique_ptr<PathSensitiveBugReport> R;
+ if (Type.isSuppressOnSink()) {
+ const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
+ if (AcquireNode) {
+ PathDiagnosticLocation LocUsedForUniqueing =
+ PathDiagnosticLocation::createBegin(
+ AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
+ AcquireNode->getLocationContext());
+
+ R = std::make_unique<PathSensitiveBugReport>(
+ Type, Msg, ErrorNode, LocUsedForUniqueing,
+ AcquireNode->getLocationContext()->getDecl());
+ }
+ }
+ if (!R)
+ R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode);
+ if (Range)
+ R->addRange(*Range);
+ R->markInteresting(Sym);
+ C.emitReport(std::move(R));
+}
+
+void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
+ mgr.registerChecker<FuchsiaHandleChecker>();
+}
+
+bool ento::shouldRegisterFuchsiaHandleChecker(const LangOptions &LO) {
+ return true;
+}
+
+void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+
+ HStateMapTy StateMap = State->get<HStateMap>();
+
+ if (!StateMap.isEmpty()) {
+ Out << Sep << "FuchsiaHandleChecker :" << NL;
+ for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
+ ++I) {
+ I.getKey()->dumpToStream(Out);
+ Out << " : ";
+ I.getData().dump(Out);
+ Out << NL;
+ }
+ }
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index d442b26b3959..302d5bb1bea8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -24,9 +24,10 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include "llvm/ADT/StringMap.h"
#include "llvm/Support/YAMLTraits.h"
+#include <algorithm>
#include <limits>
+#include <unordered_map>
#include <utility>
using namespace clang;
@@ -56,10 +57,11 @@ public:
/// Used to parse the configuration file.
struct TaintConfiguration {
- using NameArgsPair = std::pair<std::string, ArgVector>;
+ using NameScopeArgs = std::tuple<std::string, std::string, ArgVector>;
struct Propagation {
std::string Name;
+ std::string Scope;
ArgVector SrcArgs;
SignedArgVector DstArgs;
VariadicType VarType;
@@ -67,8 +69,8 @@ public:
};
std::vector<Propagation> Propagations;
- std::vector<NameArgsPair> Filters;
- std::vector<NameArgsPair> Sinks;
+ std::vector<NameScopeArgs> Filters;
+ std::vector<NameScopeArgs> Sinks;
TaintConfiguration() = default;
TaintConfiguration(const TaintConfiguration &) = default;
@@ -97,14 +99,52 @@ private:
BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data"));
}
+ struct FunctionData {
+ FunctionData() = delete;
+ FunctionData(const FunctionData &) = default;
+ FunctionData(FunctionData &&) = default;
+ FunctionData &operator=(const FunctionData &) = delete;
+ FunctionData &operator=(FunctionData &&) = delete;
+
+ static Optional<FunctionData> create(const CallExpr *CE,
+ const CheckerContext &C) {
+ const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!FDecl || (FDecl->getKind() != Decl::Function &&
+ FDecl->getKind() != Decl::CXXMethod))
+ return None;
+
+ StringRef Name = C.getCalleeName(FDecl);
+ std::string FullName = FDecl->getQualifiedNameAsString();
+ if (Name.empty() || FullName.empty())
+ return None;
+
+ return FunctionData{FDecl, Name, FullName};
+ }
+
+ bool isInScope(StringRef Scope) const {
+ return StringRef(FullName).startswith(Scope);
+ }
+
+ const FunctionDecl *const FDecl;
+ const StringRef Name;
+ const std::string FullName;
+ };
+
/// Catch taint related bugs. Check if tainted data is passed to a
- /// system call etc.
- bool checkPre(const CallExpr *CE, CheckerContext &C) const;
+ /// system call etc. Returns true on matching.
+ bool checkPre(const CallExpr *CE, const FunctionData &FData,
+ CheckerContext &C) const;
+
+ /// Add taint sources on a pre-visit. Returns true on matching.
+ bool addSourcesPre(const CallExpr *CE, const FunctionData &FData,
+ CheckerContext &C) const;
- /// Add taint sources on a pre-visit.
- void addSourcesPre(const CallExpr *CE, CheckerContext &C) const;
+ /// Mark filter's arguments not tainted on a pre-visit. Returns true on
+ /// matching.
+ bool addFiltersPre(const CallExpr *CE, const FunctionData &FData,
+ CheckerContext &C) const;
- /// Propagate taint generated at pre-visit.
+ /// Propagate taint generated at pre-visit. Returns true on matching.
bool propagateFromPre(const CallExpr *CE, CheckerContext &C) const;
/// Check if the region the expression evaluates to is the standard input,
@@ -142,7 +182,7 @@ private:
/// Check if tainted data is used as a custom sink's parameter.
static constexpr llvm::StringLiteral MsgCustomSink =
"Untrusted data is passed to a user-defined sink";
- bool checkCustomSinks(const CallExpr *CE, StringRef Name,
+ bool checkCustomSinks(const CallExpr *CE, const FunctionData &FData,
CheckerContext &C) const;
/// Generate a report if the expression is tainted or points to tainted data.
@@ -150,8 +190,17 @@ private:
CheckerContext &C) const;
struct TaintPropagationRule;
- using NameRuleMap = llvm::StringMap<TaintPropagationRule>;
- using NameArgMap = llvm::StringMap<ArgVector>;
+ template <typename T>
+ using ConfigDataMap =
+ std::unordered_multimap<std::string, std::pair<std::string, T>>;
+ using NameRuleMap = ConfigDataMap<TaintPropagationRule>;
+ using NameArgMap = ConfigDataMap<ArgVector>;
+
+ /// Find a function with the given name and scope. Returns the first match
+ /// or the end of the map.
+ template <typename T>
+ static auto findFunctionInConfig(const ConfigDataMap<T> &Map,
+ const FunctionData &FData);
/// A struct used to specify taint propagation rules for a function.
///
@@ -193,8 +242,7 @@ private:
/// Get the propagation rule for a given function.
static TaintPropagationRule
getTaintPropagationRule(const NameRuleMap &CustomPropagations,
- const FunctionDecl *FDecl, StringRef Name,
- CheckerContext &C);
+ const FunctionData &FData, CheckerContext &C);
void addSrcArg(unsigned A) { SrcArgs.push_back(A); }
void addDstArg(unsigned A) { DstArgs.push_back(A); }
@@ -229,14 +277,15 @@ private:
CheckerContext &C);
};
- /// Defines a map between the propagation function's name and
- /// TaintPropagationRule.
+ /// Defines a map between the propagation function's name, scope
+ /// and TaintPropagationRule.
NameRuleMap CustomPropagations;
- /// Defines a map between the filter function's name and filtering args.
+ /// Defines a map between the filter function's name, scope and filtering
+ /// args.
NameArgMap CustomFilters;
- /// Defines a map between the sink function's name and sinking args.
+ /// Defines a map between the sink function's name, scope and sinking args.
NameArgMap CustomSinks;
};
@@ -253,7 +302,7 @@ constexpr llvm::StringLiteral GenericTaintChecker::MsgCustomSink;
using TaintConfig = GenericTaintChecker::TaintConfiguration;
LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::Propagation)
-LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::NameArgsPair)
+LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::NameScopeArgs)
namespace llvm {
namespace yaml {
@@ -268,6 +317,7 @@ template <> struct MappingTraits<TaintConfig> {
template <> struct MappingTraits<TaintConfig::Propagation> {
static void mapping(IO &IO, TaintConfig::Propagation &Propagation) {
IO.mapRequired("Name", Propagation.Name);
+ IO.mapOptional("Scope", Propagation.Scope);
IO.mapOptional("SrcArgs", Propagation.SrcArgs);
IO.mapOptional("DstArgs", Propagation.DstArgs);
IO.mapOptional("VariadicType", Propagation.VarType,
@@ -285,10 +335,11 @@ template <> struct ScalarEnumerationTraits<GenericTaintChecker::VariadicType> {
}
};
-template <> struct MappingTraits<TaintConfig::NameArgsPair> {
- static void mapping(IO &IO, TaintConfig::NameArgsPair &NameArg) {
- IO.mapRequired("Name", NameArg.first);
- IO.mapRequired("Args", NameArg.second);
+template <> struct MappingTraits<TaintConfig::NameScopeArgs> {
+ static void mapping(IO &IO, TaintConfig::NameScopeArgs &NSA) {
+ IO.mapRequired("Name", std::get<0>(NSA));
+ IO.mapOptional("Scope", std::get<1>(NSA));
+ IO.mapRequired("Args", std::get<2>(NSA));
}
};
} // namespace yaml
@@ -321,31 +372,51 @@ void GenericTaintChecker::parseConfiguration(CheckerManager &Mgr,
const std::string &Option,
TaintConfiguration &&Config) {
for (auto &P : Config.Propagations) {
- GenericTaintChecker::CustomPropagations.try_emplace(
- P.Name, std::move(P.SrcArgs),
- convertToArgVector(Mgr, Option, P.DstArgs), P.VarType, P.VarIndex);
+ GenericTaintChecker::CustomPropagations.emplace(
+ P.Name,
+ std::make_pair(P.Scope, TaintPropagationRule{
+ std::move(P.SrcArgs),
+ convertToArgVector(Mgr, Option, P.DstArgs),
+ P.VarType, P.VarIndex}));
}
for (auto &F : Config.Filters) {
- GenericTaintChecker::CustomFilters.try_emplace(F.first,
- std::move(F.second));
+ GenericTaintChecker::CustomFilters.emplace(
+ std::get<0>(F),
+ std::make_pair(std::move(std::get<1>(F)), std::move(std::get<2>(F))));
}
for (auto &S : Config.Sinks) {
- GenericTaintChecker::CustomSinks.try_emplace(S.first, std::move(S.second));
+ GenericTaintChecker::CustomSinks.emplace(
+ std::get<0>(S),
+ std::make_pair(std::move(std::get<1>(S)), std::move(std::get<2>(S))));
}
}
+template <typename T>
+auto GenericTaintChecker::findFunctionInConfig(const ConfigDataMap<T> &Map,
+ const FunctionData &FData) {
+ auto Range = Map.equal_range(FData.Name);
+ auto It =
+ std::find_if(Range.first, Range.second, [&FData](const auto &Entry) {
+ const auto &Value = Entry.second;
+ StringRef Scope = Value.first;
+ return Scope.empty() || FData.isInScope(Scope);
+ });
+ return It != Range.second ? It : Map.end();
+}
+
GenericTaintChecker::TaintPropagationRule
GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
- const NameRuleMap &CustomPropagations, const FunctionDecl *FDecl,
- StringRef Name, CheckerContext &C) {
+ const NameRuleMap &CustomPropagations, const FunctionData &FData,
+ CheckerContext &C) {
// TODO: Currently, we might lose precision here: we always mark a return
// value as tainted even if it's just a pointer, pointing to tainted data.
// Check for exact name match for functions without builtin substitutes.
+ // Use qualified name, because these are C functions without namespace.
TaintPropagationRule Rule =
- llvm::StringSwitch<TaintPropagationRule>(Name)
+ llvm::StringSwitch<TaintPropagationRule>(FData.FullName)
// Source functions
// TODO: Add support for vfscanf & family.
.Case("fdopen", TaintPropagationRule({}, {ReturnValueIndex}))
@@ -390,6 +461,7 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
// Check if it's one of the memory setting/copying functions.
// This check is specialized but faster then calling isCLibraryFunction.
+ const FunctionDecl *FDecl = FData.FDecl;
unsigned BId = 0;
if ((BId = FDecl->getMemoryFunctionKind()))
switch (BId) {
@@ -433,23 +505,32 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
// or smart memory copy:
// - memccpy - copying until hitting a special character.
- auto It = CustomPropagations.find(Name);
- if (It != CustomPropagations.end())
- return It->getValue();
+ auto It = findFunctionInConfig(CustomPropagations, FData);
+ if (It != CustomPropagations.end()) {
+ const auto &Value = It->second;
+ return Value.second;
+ }
return TaintPropagationRule();
}
void GenericTaintChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const {
+ Optional<FunctionData> FData = FunctionData::create(CE, C);
+ if (!FData)
+ return;
+
// Check for taintedness related errors first: system call, uncontrolled
// format string, tainted buffer size.
- if (checkPre(CE, C))
+ if (checkPre(CE, *FData, C))
return;
// Marks the function's arguments and/or return value tainted if it present in
// the list.
- addSourcesPre(CE, C);
+ if (addSourcesPre(CE, *FData, C))
+ return;
+
+ addFiltersPre(CE, *FData, C);
}
void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
@@ -464,31 +545,47 @@ void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State,
printTaint(State, Out, NL, Sep);
}
-void GenericTaintChecker::addSourcesPre(const CallExpr *CE,
+bool GenericTaintChecker::addSourcesPre(const CallExpr *CE,
+ const FunctionData &FData,
CheckerContext &C) const {
- ProgramStateRef State = nullptr;
- const FunctionDecl *FDecl = C.getCalleeDecl(CE);
- if (!FDecl || FDecl->getKind() != Decl::Function)
- return;
-
- StringRef Name = C.getCalleeName(FDecl);
- if (Name.empty())
- return;
-
// First, try generating a propagation rule for this function.
TaintPropagationRule Rule = TaintPropagationRule::getTaintPropagationRule(
- this->CustomPropagations, FDecl, Name, C);
+ this->CustomPropagations, FData, C);
if (!Rule.isNull()) {
- State = Rule.process(CE, C);
- if (!State)
- return;
- C.addTransition(State);
- return;
+ ProgramStateRef State = Rule.process(CE, C);
+ if (State) {
+ C.addTransition(State);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GenericTaintChecker::addFiltersPre(const CallExpr *CE,
+ const FunctionData &FData,
+ CheckerContext &C) const {
+ auto It = findFunctionInConfig(CustomFilters, FData);
+ if (It == CustomFilters.end())
+ return false;
+
+ ProgramStateRef State = C.getState();
+ const auto &Value = It->second;
+ const ArgVector &Args = Value.second;
+ for (unsigned ArgNum : Args) {
+ if (ArgNum >= CE->getNumArgs())
+ continue;
+
+ const Expr *Arg = CE->getArg(ArgNum);
+ Optional<SVal> V = getPointedToSVal(C, Arg);
+ if (V)
+ State = removeTaint(State, *V);
}
- if (!State)
- return;
- C.addTransition(State);
+ if (State != C.getState()) {
+ C.addTransition(State);
+ return true;
+ }
+ return false;
}
bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
@@ -530,26 +627,19 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
}
bool GenericTaintChecker::checkPre(const CallExpr *CE,
+ const FunctionData &FData,
CheckerContext &C) const {
if (checkUncontrolledFormatString(CE, C))
return true;
- const FunctionDecl *FDecl = C.getCalleeDecl(CE);
- if (!FDecl || FDecl->getKind() != Decl::Function)
- return false;
-
- StringRef Name = C.getCalleeName(FDecl);
- if (Name.empty())
- return false;
-
- if (checkSystemCall(CE, Name, C))
+ if (checkSystemCall(CE, FData.Name, C))
return true;
- if (checkTaintedBufferSize(CE, FDecl, C))
+ if (checkTaintedBufferSize(CE, FData.FDecl, C))
return true;
- if (checkCustomSinks(CE, Name, C))
+ if (checkCustomSinks(CE, FData, C))
return true;
return false;
@@ -568,7 +658,7 @@ Optional<SVal> GenericTaintChecker::getPointedToSVal(CheckerContext &C,
QualType ArgTy = Arg->getType().getCanonicalType();
if (!ArgTy->isPointerType())
- return None;
+ return State->getSVal(*AddrLoc);
QualType ValTy = ArgTy->getPointeeType();
@@ -821,13 +911,15 @@ bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE,
generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C);
}
-bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE, StringRef Name,
+bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE,
+ const FunctionData &FData,
CheckerContext &C) const {
- auto It = CustomSinks.find(Name);
+ auto It = findFunctionInConfig(CustomSinks, FData);
if (It == CustomSinks.end())
return false;
- const GenericTaintChecker::ArgVector &Args = It->getValue();
+ const auto &Value = It->second;
+ const GenericTaintChecker::ArgVector &Args = Value.second;
for (unsigned ArgNum : Args) {
if (ArgNum >= CE->getNumArgs())
continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
index b0d101c88517..dd89c53478e8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -62,7 +62,7 @@ public:
// lookup by region.
bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
RawPtrMapTy Map = State->get<RawPtrMap>();
- for (const auto Entry : Map) {
+ for (const auto &Entry : Map) {
if (Entry.second.contains(Sym))
return true;
}
@@ -236,7 +236,7 @@ void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
ProgramStateRef State = C.getState();
PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
RawPtrMapTy RPM = State->get<RawPtrMap>();
- for (const auto Entry : RPM) {
+ for (const auto &Entry : RPM) {
if (!SymReaper.isLiveRegion(Entry.first)) {
// Due to incomplete destructor support, some dead regions might
// remain in the program state map. Clean them up.
@@ -266,7 +266,7 @@ std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {
const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) {
RawPtrMapTy Map = State->get<RawPtrMap>();
- for (const auto Entry : Map) {
+ for (const auto &Entry : Map) {
if (Entry.second.contains(Sym)) {
return Entry.first;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
new file mode 100644
index 000000000000..d1a9a7df071d
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
@@ -0,0 +1,95 @@
+//===-- InvalidatedIteratorChecker.cpp ----------------------------*- C++ -*--//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for access of invalidated iterators.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+
+#include "Iterator.h"
+
+using namespace clang;
+using namespace ento;
+using namespace iterator;
+
+namespace {
+
+class InvalidatedIteratorChecker
+ : public Checker<check::PreCall> {
+
+ std::unique_ptr<BugType> InvalidatedBugType;
+
+ void verifyAccess(CheckerContext &C, const SVal &Val) const;
+ void reportBug(const StringRef &Message, const SVal &Val,
+ CheckerContext &C, ExplodedNode *ErrNode) const;
+public:
+ InvalidatedIteratorChecker();
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+};
+
+} //namespace
+
+InvalidatedIteratorChecker::InvalidatedIteratorChecker() {
+ InvalidatedBugType.reset(
+ new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
+}
+
+void InvalidatedIteratorChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // Check for access of invalidated position
+ const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!Func)
+ return;
+
+ if (Func->isOverloadedOperator() &&
+ isAccessOperator(Func->getOverloadedOperator())) {
+ // Check for any kind of access of invalidated iterator positions
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ verifyAccess(C, InstCall->getCXXThisVal());
+ } else {
+ verifyAccess(C, Call.getArgSVal(0));
+ }
+ }
+}
+
+void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Val);
+ if (Pos && !Pos->isValid()) {
+ auto *N = C.generateErrorNode(State);
+ if (!N) {
+ return;
+ }
+ reportBug("Invalidated iterator accessed.", Val, C, N);
+ }
+}
+
+void InvalidatedIteratorChecker::reportBug(const StringRef &Message,
+ const SVal &Val, CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType,
+ Message, ErrNode);
+ R->markInteresting(Val);
+ C.emitReport(std::move(R));
+}
+
+void ento::registerInvalidatedIteratorChecker(CheckerManager &mgr) {
+ mgr.registerChecker<InvalidatedIteratorChecker>();
+}
+
+bool ento::shouldRegisterInvalidatedIteratorChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp b/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
new file mode 100644
index 000000000000..6bca5515724c
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
@@ -0,0 +1,227 @@
+//=== Iterator.cpp - Common functions for iterator checkers. -------*- C++ -*-//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines common functions to be used by the itertor checkers .
+//
+//===----------------------------------------------------------------------===//
+
+#include "Iterator.h"
+
+namespace clang {
+namespace ento {
+namespace iterator {
+
+bool isIteratorType(const QualType &Type) {
+ if (Type->isPointerType())
+ return true;
+
+ const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+ return isIterator(CRD);
+}
+
+bool isIterator(const CXXRecordDecl *CRD) {
+ if (!CRD)
+ return false;
+
+ const auto Name = CRD->getName();
+ if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
+ Name.endswith_lower("it")))
+ return false;
+
+ bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
+ HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
+ for (const auto *Method : CRD->methods()) {
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
+ if (Ctor->isCopyConstructor()) {
+ HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
+ }
+ continue;
+ }
+ if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
+ HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
+ continue;
+ }
+ if (Method->isCopyAssignmentOperator()) {
+ HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
+ continue;
+ }
+ if (!Method->isOverloadedOperator())
+ continue;
+ const auto OPK = Method->getOverloadedOperator();
+ if (OPK == OO_PlusPlus) {
+ HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
+ HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
+ continue;
+ }
+ if (OPK == OO_Star) {
+ HasDerefOp = (Method->getNumParams() == 0);
+ continue;
+ }
+ }
+
+ return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
+ HasPostIncrOp && HasDerefOp;
+}
+
+bool isComparisonOperator(OverloadedOperatorKind OK) {
+ return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
+ OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
+}
+
+bool isInsertCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
+ return false;
+ if (!isIteratorType(Func->getParamDecl(0)->getType()))
+ return false;
+ return IdInfo->getName() == "insert";
+}
+
+bool isEmplaceCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 2)
+ return false;
+ if (!isIteratorType(Func->getParamDecl(0)->getType()))
+ return false;
+ return IdInfo->getName() == "emplace";
+}
+
+bool isEraseCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
+ return false;
+ if (!isIteratorType(Func->getParamDecl(0)->getType()))
+ return false;
+ if (Func->getNumParams() == 2 &&
+ !isIteratorType(Func->getParamDecl(1)->getType()))
+ return false;
+ return IdInfo->getName() == "erase";
+}
+
+bool isEraseAfterCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
+ return false;
+ if (!isIteratorType(Func->getParamDecl(0)->getType()))
+ return false;
+ if (Func->getNumParams() == 2 &&
+ !isIteratorType(Func->getParamDecl(1)->getType()))
+ return false;
+ return IdInfo->getName() == "erase_after";
+}
+
+bool isAccessOperator(OverloadedOperatorKind OK) {
+ return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
+ isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
+}
+
+bool isDereferenceOperator(OverloadedOperatorKind OK) {
+ return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
+ OK == OO_Subscript;
+}
+
+bool isIncrementOperator(OverloadedOperatorKind OK) {
+ return OK == OO_PlusPlus;
+}
+
+bool isDecrementOperator(OverloadedOperatorKind OK) {
+ return OK == OO_MinusMinus;
+}
+
+bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
+ return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
+ OK == OO_MinusEqual;
+}
+
+const ContainerData *getContainerData(ProgramStateRef State,
+ const MemRegion *Cont) {
+ return State->get<ContainerMap>(Cont);
+}
+
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+ const SVal &Val) {
+ if (auto Reg = Val.getAsRegion()) {
+ Reg = Reg->getMostDerivedObjectRegion();
+ return State->get<IteratorRegionMap>(Reg);
+ } else if (const auto Sym = Val.getAsSymbol()) {
+ return State->get<IteratorSymbolMap>(Sym);
+ } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+ return State->get<IteratorRegionMap>(LCVal->getRegion());
+ }
+ return nullptr;
+}
+
+ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
+ const IteratorPosition &Pos) {
+ if (auto Reg = Val.getAsRegion()) {
+ Reg = Reg->getMostDerivedObjectRegion();
+ return State->set<IteratorRegionMap>(Reg, Pos);
+ } else if (const auto Sym = Val.getAsSymbol()) {
+ return State->set<IteratorSymbolMap>(Sym, Pos);
+ } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+ return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
+ }
+ return nullptr;
+}
+
+ProgramStateRef advancePosition(ProgramStateRef State, const SVal &Iter,
+ OverloadedOperatorKind Op,
+ const SVal &Distance) {
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (!Pos)
+ return nullptr;
+
+ auto &SymMgr = State->getStateManager().getSymbolManager();
+ auto &SVB = State->getStateManager().getSValBuilder();
+
+ assert ((Op == OO_Plus || Op == OO_PlusEqual ||
+ Op == OO_Minus || Op == OO_MinusEqual) &&
+ "Advance operator must be one of +, -, += and -=.");
+ auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
+ if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) {
+ // For concrete integers we can calculate the new position
+ const auto NewPos =
+ Pos->setTo(SVB.evalBinOp(State, BinOp,
+ nonloc::SymbolVal(Pos->getOffset()),
+ *IntDist, SymMgr.getType(Pos->getOffset()))
+ .getAsSymbol());
+ return setIteratorPosition(State, Iter, NewPos);
+ }
+
+ return nullptr;
+}
+
+bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
+ BinaryOperator::Opcode Opc) {
+ return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
+}
+
+bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
+ BinaryOperator::Opcode Opc) {
+ auto &SVB = State->getStateManager().getSValBuilder();
+
+ const auto comparison =
+ SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
+
+ assert(comparison.getAs<DefinedSVal>() &&
+ "Symbol comparison must be a `DefinedSVal`");
+
+ return !State->assume(comparison.castAs<DefinedSVal>(), false);
+}
+
+} // namespace iterator
+} // namespace ento
+} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Checkers/Iterator.h b/clang/lib/StaticAnalyzer/Checkers/Iterator.h
new file mode 100644
index 000000000000..c10d86691693
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/Iterator.h
@@ -0,0 +1,175 @@
+//=== Iterator.h - Common functions for iterator checkers. ---------*- C++ -*-//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines common functions to be used by the itertor checkers .
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ITERATOR_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ITERATOR_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+
+namespace clang {
+namespace ento {
+namespace iterator {
+
+// Abstract position of an iterator. This helps to handle all three kinds
+// of operators in a common way by using a symbolic position.
+struct IteratorPosition {
+private:
+
+ // Container the iterator belongs to
+ const MemRegion *Cont;
+
+ // Whether iterator is valid
+ const bool Valid;
+
+ // Abstract offset
+ const SymbolRef Offset;
+
+ IteratorPosition(const MemRegion *C, bool V, SymbolRef Of)
+ : Cont(C), Valid(V), Offset(Of) {}
+
+public:
+ const MemRegion *getContainer() const { return Cont; }
+ bool isValid() const { return Valid; }
+ SymbolRef getOffset() const { return Offset; }
+
+ IteratorPosition invalidate() const {
+ return IteratorPosition(Cont, false, Offset);
+ }
+
+ static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
+ return IteratorPosition(C, true, Of);
+ }
+
+ IteratorPosition setTo(SymbolRef NewOf) const {
+ return IteratorPosition(Cont, Valid, NewOf);
+ }
+
+ IteratorPosition reAssign(const MemRegion *NewCont) const {
+ return IteratorPosition(NewCont, Valid, Offset);
+ }
+
+ bool operator==(const IteratorPosition &X) const {
+ return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
+ }
+
+ bool operator!=(const IteratorPosition &X) const {
+ return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddPointer(Cont);
+ ID.AddInteger(Valid);
+ ID.Add(Offset);
+ }
+};
+
+// Structure to record the symbolic begin and end position of a container
+struct ContainerData {
+private:
+ const SymbolRef Begin, End;
+
+ ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
+
+public:
+ static ContainerData fromBegin(SymbolRef B) {
+ return ContainerData(B, nullptr);
+ }
+
+ static ContainerData fromEnd(SymbolRef E) {
+ return ContainerData(nullptr, E);
+ }
+
+ SymbolRef getBegin() const { return Begin; }
+ SymbolRef getEnd() const { return End; }
+
+ ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
+
+ ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
+
+ bool operator==(const ContainerData &X) const {
+ return Begin == X.Begin && End == X.End;
+ }
+
+ bool operator!=(const ContainerData &X) const {
+ return Begin != X.Begin || End != X.End;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.Add(Begin);
+ ID.Add(End);
+ }
+};
+
+class IteratorSymbolMap {};
+class IteratorRegionMap {};
+class ContainerMap {};
+
+using IteratorSymbolMapTy = CLANG_ENTO_PROGRAMSTATE_MAP(SymbolRef, IteratorPosition);
+using IteratorRegionMapTy = CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, IteratorPosition);
+using ContainerMapTy = CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, ContainerData);
+
+} // namespace iterator
+
+template<>
+struct ProgramStateTrait<iterator::IteratorSymbolMap>
+ : public ProgramStatePartialTrait<iterator::IteratorSymbolMapTy> {
+ static void *GDMIndex() { static int Index; return &Index; }
+};
+
+template<>
+struct ProgramStateTrait<iterator::IteratorRegionMap>
+ : public ProgramStatePartialTrait<iterator::IteratorRegionMapTy> {
+ static void *GDMIndex() { static int Index; return &Index; }
+};
+
+template<>
+struct ProgramStateTrait<iterator::ContainerMap>
+ : public ProgramStatePartialTrait<iterator::ContainerMapTy> {
+ static void *GDMIndex() { static int Index; return &Index; }
+};
+
+namespace iterator {
+
+bool isIteratorType(const QualType &Type);
+bool isIterator(const CXXRecordDecl *CRD);
+bool isComparisonOperator(OverloadedOperatorKind OK);
+bool isInsertCall(const FunctionDecl *Func);
+bool isEraseCall(const FunctionDecl *Func);
+bool isEraseAfterCall(const FunctionDecl *Func);
+bool isEmplaceCall(const FunctionDecl *Func);
+bool isAccessOperator(OverloadedOperatorKind OK);
+bool isDereferenceOperator(OverloadedOperatorKind OK);
+bool isIncrementOperator(OverloadedOperatorKind OK);
+bool isDecrementOperator(OverloadedOperatorKind OK);
+bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
+const ContainerData *getContainerData(ProgramStateRef State,
+ const MemRegion *Cont);
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+ const SVal &Val);
+ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
+ const IteratorPosition &Pos);
+ProgramStateRef advancePosition(ProgramStateRef State,
+ const SVal &Iter,
+ OverloadedOperatorKind Op,
+ const SVal &Distance);
+bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
+ BinaryOperator::Opcode Opc);
+bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
+ BinaryOperator::Opcode Opc);
+
+} // namespace iterator
+} // namespace ento
+} // namespace clang
+
+#endif
diff --git a/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
index 97ace68569ef..eb962a2ffd9e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
@@ -1,4 +1,4 @@
-//===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
+//===-- IteratorModeling.cpp --------------------------------------*- C++ -*--//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -73,127 +73,33 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include "Iterator.h"
+
#include <utility>
using namespace clang;
using namespace ento;
+using namespace iterator;
namespace {
-// Abstract position of an iterator. This helps to handle all three kinds
-// of operators in a common way by using a symbolic position.
-struct IteratorPosition {
-private:
-
- // Container the iterator belongs to
- const MemRegion *Cont;
-
- // Whether iterator is valid
- const bool Valid;
-
- // Abstract offset
- const SymbolRef Offset;
-
- IteratorPosition(const MemRegion *C, bool V, SymbolRef Of)
- : Cont(C), Valid(V), Offset(Of) {}
-
-public:
- const MemRegion *getContainer() const { return Cont; }
- bool isValid() const { return Valid; }
- SymbolRef getOffset() const { return Offset; }
-
- IteratorPosition invalidate() const {
- return IteratorPosition(Cont, false, Offset);
- }
-
- static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
- return IteratorPosition(C, true, Of);
- }
-
- IteratorPosition setTo(SymbolRef NewOf) const {
- return IteratorPosition(Cont, Valid, NewOf);
- }
-
- IteratorPosition reAssign(const MemRegion *NewCont) const {
- return IteratorPosition(NewCont, Valid, Offset);
- }
-
- bool operator==(const IteratorPosition &X) const {
- return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
- }
-
- bool operator!=(const IteratorPosition &X) const {
- return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
- }
-
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddPointer(Cont);
- ID.AddInteger(Valid);
- ID.Add(Offset);
- }
-};
-
-// Structure to record the symbolic begin and end position of a container
-struct ContainerData {
-private:
- const SymbolRef Begin, End;
-
- ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
-
-public:
- static ContainerData fromBegin(SymbolRef B) {
- return ContainerData(B, nullptr);
- }
-
- static ContainerData fromEnd(SymbolRef E) {
- return ContainerData(nullptr, E);
- }
-
- SymbolRef getBegin() const { return Begin; }
- SymbolRef getEnd() const { return End; }
+class IteratorModeling
+ : public Checker<check::PostCall, check::PostStmt<MaterializeTemporaryExpr>,
+ check::Bind, check::LiveSymbols, check::DeadSymbols> {
- ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
-
- ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
-
- bool operator==(const ContainerData &X) const {
- return Begin == X.Begin && End == X.End;
- }
-
- bool operator!=(const ContainerData &X) const {
- return Begin != X.Begin || End != X.End;
- }
-
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.Add(Begin);
- ID.Add(End);
- }
-};
-
-class IteratorChecker
- : public Checker<check::PreCall, check::PostCall,
- check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
- check::LiveSymbols, check::DeadSymbols> {
-
- std::unique_ptr<BugType> OutOfRangeBugType;
- std::unique_ptr<BugType> MismatchedBugType;
- std::unique_ptr<BugType> InvalidatedBugType;
-
- void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
+ void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
const SVal &LVal, const SVal &RVal,
OverloadedOperatorKind Op) const;
void processComparison(CheckerContext &C, ProgramStateRef State,
SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
OverloadedOperatorKind Op) const;
- void verifyAccess(CheckerContext &C, const SVal &Val) const;
- void verifyDereference(CheckerContext &C, const SVal &Val) const;
void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
bool Postfix) const;
void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
bool Postfix) const;
- void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
- const SVal &RetVal, const SVal &LHS,
- const SVal &RHS) const;
+ void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
+ OverloadedOperatorKind Op, const SVal &RetVal,
+ const SVal &LHS, const SVal &RHS) const;
void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
const SVal &Cont) const;
void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
@@ -215,42 +121,12 @@ class IteratorChecker
void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
const SVal &Iter2) const;
- void verifyIncrement(CheckerContext &C, const SVal &Iter) const;
- void verifyDecrement(CheckerContext &C, const SVal &Iter) const;
- void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
- const SVal &LHS, const SVal &RHS) const;
- void verifyMatch(CheckerContext &C, const SVal &Iter,
- const MemRegion *Cont) const;
- void verifyMatch(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const;
- IteratorPosition advancePosition(CheckerContext &C, OverloadedOperatorKind Op,
- const IteratorPosition &Pos,
- const SVal &Distance) const;
- void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
- CheckerContext &C, ExplodedNode *ErrNode) const;
- void reportMismatchedBug(const StringRef &Message, const SVal &Val1,
- const SVal &Val2, CheckerContext &C,
- ExplodedNode *ErrNode) const;
- void reportMismatchedBug(const StringRef &Message, const SVal &Val,
- const MemRegion *Reg, CheckerContext &C,
- ExplodedNode *ErrNode) const;
- void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
- CheckerContext &C, ExplodedNode *ErrNode) const;
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
public:
- IteratorChecker();
+ IteratorModeling() {}
- enum CheckKind {
- CK_IteratorRangeChecker,
- CK_MismatchedIteratorChecker,
- CK_InvalidatedIteratorChecker,
- CK_NumCheckKinds
- };
-
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
- CheckerNameRef CheckNames[CK_NumCheckKinds];
-
- void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
@@ -260,19 +136,7 @@ public:
void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
};
-} // namespace
-
-REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
-REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
- IteratorPosition)
-
-REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
-
-namespace {
-bool isIteratorType(const QualType &Type);
-bool isIterator(const CXXRecordDecl *CRD);
-bool isComparisonOperator(OverloadedOperatorKind OK);
bool isBeginCall(const FunctionDecl *Func);
bool isEndCall(const FunctionDecl *Func);
bool isAssignCall(const FunctionDecl *Func);
@@ -283,17 +147,8 @@ bool isPopBackCall(const FunctionDecl *Func);
bool isPushFrontCall(const FunctionDecl *Func);
bool isEmplaceFrontCall(const FunctionDecl *Func);
bool isPopFrontCall(const FunctionDecl *Func);
-bool isInsertCall(const FunctionDecl *Func);
-bool isEraseCall(const FunctionDecl *Func);
-bool isEraseAfterCall(const FunctionDecl *Func);
-bool isEmplaceCall(const FunctionDecl *Func);
bool isAssignmentOperator(OverloadedOperatorKind OK);
bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
-bool isAccessOperator(OverloadedOperatorKind OK);
-bool isDereferenceOperator(OverloadedOperatorKind OK);
-bool isIncrementOperator(OverloadedOperatorKind OK);
-bool isDecrementOperator(OverloadedOperatorKind OK);
-bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
@@ -307,10 +162,8 @@ ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
const Expr *E, QualType T,
const LocationContext *LCtx,
unsigned BlockCount);
-const IteratorPosition *getIteratorPosition(ProgramStateRef State,
- const SVal &Val);
-ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
- const IteratorPosition &Pos);
+ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
+ const ContainerData &CData);
ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
long Scale);
@@ -341,222 +194,16 @@ ProgramStateRef rebaseSymbolInIteratorPositionsIf(
SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
SymbolRef Sym2, bool Equal);
-const ContainerData *getContainerData(ProgramStateRef State,
- const MemRegion *Cont);
-ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
- const ContainerData &CData);
+SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
+ SymbolRef OldSym, SymbolRef NewSym);
bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
bool isBoundThroughLazyCompoundVal(const Environment &Env,
const MemRegion *Reg);
-bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
-bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
-bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
-bool isZero(ProgramStateRef State, const NonLoc &Val);
-} // namespace
-IteratorChecker::IteratorChecker() {
- OutOfRangeBugType.reset(
- new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
- MismatchedBugType.reset(
- new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
- /*SuppressOnSink=*/true));
- InvalidatedBugType.reset(
- new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
-}
-
-void IteratorChecker::checkPreCall(const CallEvent &Call,
- CheckerContext &C) const {
- // Check for out of range access or access of invalidated position and
- // iterator mismatches
- const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!Func)
- return;
-
- if (Func->isOverloadedOperator()) {
- if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
- isAccessOperator(Func->getOverloadedOperator())) {
- // Check for any kind of access of invalidated iterator positions
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyAccess(C, InstCall->getCXXThisVal());
- } else {
- verifyAccess(C, Call.getArgSVal(0));
- }
- }
- if (ChecksEnabled[CK_IteratorRangeChecker]) {
- if (isIncrementOperator(Func->getOverloadedOperator())) {
- // Check for out-of-range incrementions
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyIncrement(C, InstCall->getCXXThisVal());
- } else {
- if (Call.getNumArgs() >= 1) {
- verifyIncrement(C, Call.getArgSVal(0));
- }
- }
- } else if (isDecrementOperator(Func->getOverloadedOperator())) {
- // Check for out-of-range decrementions
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyDecrement(C, InstCall->getCXXThisVal());
- } else {
- if (Call.getNumArgs() >= 1) {
- verifyDecrement(C, Call.getArgSVal(0));
- }
- }
- } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- // Check for out-of-range incrementions and decrementions
- if (Call.getNumArgs() >= 1 &&
- Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
- verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- InstCall->getCXXThisVal(),
- Call.getArgSVal(0));
- }
- } else {
- if (Call.getNumArgs() >= 2 &&
- Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
- verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- Call.getArgSVal(0), Call.getArgSVal(1));
- }
- }
- } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
- // Check for dereference of out-of-range iterators
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyDereference(C, InstCall->getCXXThisVal());
- } else {
- verifyDereference(C, Call.getArgSVal(0));
- }
- }
- } else if (ChecksEnabled[CK_MismatchedIteratorChecker] &&
- isComparisonOperator(Func->getOverloadedOperator())) {
- // Check for comparisons of iterators of different containers
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (Call.getNumArgs() < 1)
- return;
-
- if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
- !isIteratorType(Call.getArgExpr(0)->getType()))
- return;
-
- verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
- } else {
- if (Call.getNumArgs() < 2)
- return;
-
- if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
- !isIteratorType(Call.getArgExpr(1)->getType()))
- return;
-
- verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
- }
- }
- } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (!ChecksEnabled[CK_MismatchedIteratorChecker])
- return;
-
- const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
- if (!ContReg)
- return;
- // Check for erase, insert and emplace using iterator of another container
- if (isEraseCall(Func) || isEraseAfterCall(Func)) {
- verifyMatch(C, Call.getArgSVal(0),
- InstCall->getCXXThisVal().getAsRegion());
- if (Call.getNumArgs() == 2) {
- verifyMatch(C, Call.getArgSVal(1),
- InstCall->getCXXThisVal().getAsRegion());
- }
- } else if (isInsertCall(Func)) {
- verifyMatch(C, Call.getArgSVal(0),
- InstCall->getCXXThisVal().getAsRegion());
- if (Call.getNumArgs() == 3 &&
- isIteratorType(Call.getArgExpr(1)->getType()) &&
- isIteratorType(Call.getArgExpr(2)->getType())) {
- verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
- }
- } else if (isEmplaceCall(Func)) {
- verifyMatch(C, Call.getArgSVal(0),
- InstCall->getCXXThisVal().getAsRegion());
- }
- } else if (isa<CXXConstructorCall>(&Call)) {
- // Check match of first-last iterator pair in a constructor of a container
- if (Call.getNumArgs() < 2)
- return;
-
- const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
- if (Ctr->getNumParams() < 2)
- return;
-
- if (Ctr->getParamDecl(0)->getName() != "first" ||
- Ctr->getParamDecl(1)->getName() != "last")
- return;
-
- if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
- !isIteratorType(Call.getArgExpr(1)->getType()))
- return;
-
- verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
- } else {
- // The main purpose of iterators is to abstract away from different
- // containers and provide a (maybe limited) uniform access to them.
- // This implies that any correctly written template function that
- // works on multiple containers using iterators takes different
- // template parameters for different containers. So we can safely
- // assume that passing iterators of different containers as arguments
- // whose type replaces the same template parameter is a bug.
- //
- // Example:
- // template<typename I1, typename I2>
- // void f(I1 first1, I1 last1, I2 first2, I2 last2);
- //
- // In this case the first two arguments to f() must be iterators must belong
- // to the same container and the last to also to the same container but
- // not necessarily to the same as the first two.
-
- if (!ChecksEnabled[CK_MismatchedIteratorChecker])
- return;
-
- const auto *Templ = Func->getPrimaryTemplate();
- if (!Templ)
- return;
-
- const auto *TParams = Templ->getTemplateParameters();
- const auto *TArgs = Func->getTemplateSpecializationArgs();
-
- // Iterate over all the template parameters
- for (size_t I = 0; I < TParams->size(); ++I) {
- const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
- if (!TPDecl)
- continue;
-
- if (TPDecl->isParameterPack())
- continue;
-
- const auto TAType = TArgs->get(I).getAsType();
- if (!isIteratorType(TAType))
- continue;
-
- SVal LHS = UndefinedVal();
-
- // For every template parameter which is an iterator type in the
- // instantiation look for all functions' parameters' type by it and
- // check whether they belong to the same container
- for (auto J = 0U; J < Func->getNumParams(); ++J) {
- const auto *Param = Func->getParamDecl(J);
- const auto *ParamType =
- Param->getType()->getAs<SubstTemplateTypeParmType>();
- if (!ParamType ||
- ParamType->getReplacedParameter()->getDecl() != TPDecl)
- continue;
- if (LHS.isUndef()) {
- LHS = Call.getArgSVal(J);
- } else {
- verifyMatch(C, LHS, Call.getArgSVal(J));
- }
- }
- }
- }
-}
+} // namespace
-void IteratorChecker::checkPostCall(const CallEvent &Call,
- CheckerContext &C) const {
+void IteratorModeling::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
// Record new iterator positions and iterator position changes
const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!Func)
@@ -590,10 +237,14 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
Call.getArgSVal(1), Op);
return;
} else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
+
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
if (Call.getNumArgs() >= 1 &&
Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
- handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ handleRandomIncrOrDecr(C, OrigExpr, Func->getOverloadedOperator(),
Call.getReturnValue(),
InstCall->getCXXThisVal(), Call.getArgSVal(0));
return;
@@ -601,7 +252,7 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
} else {
if (Call.getNumArgs() >= 2 &&
Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
- handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ handleRandomIncrOrDecr(C, OrigExpr, Func->getOverloadedOperator(),
Call.getReturnValue(), Call.getArgSVal(0),
Call.getArgSVal(1));
return;
@@ -746,8 +397,8 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
}
}
-void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
- CheckerContext &C) const {
+void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
+ CheckerContext &C) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Val);
if (Pos) {
@@ -762,24 +413,23 @@ void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
}
}
-void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
- CheckerContext &C) const {
+void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
+ CheckerContext &C) const {
/* Transfer iterator state to temporary objects */
auto State = C.getState();
- const auto *Pos =
- getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
+ const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
if (!Pos)
return;
State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
C.addTransition(State);
}
-void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
- SymbolReaper &SR) const {
+void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
+ SymbolReaper &SR) const {
// Keep symbolic expressions of iterator positions, container begins and ends
// alive
auto RegionMap = State->get<IteratorRegionMap>();
- for (const auto Reg : RegionMap) {
+ for (const auto &Reg : RegionMap) {
const auto Offset = Reg.second.getOffset();
for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
if (isa<SymbolData>(*i))
@@ -787,7 +437,7 @@ void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
}
auto SymbolMap = State->get<IteratorSymbolMap>();
- for (const auto Sym : SymbolMap) {
+ for (const auto &Sym : SymbolMap) {
const auto Offset = Sym.second.getOffset();
for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
if (isa<SymbolData>(*i))
@@ -795,7 +445,7 @@ void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
}
auto ContMap = State->get<ContainerMap>();
- for (const auto Cont : ContMap) {
+ for (const auto &Cont : ContMap) {
const auto CData = Cont.second;
if (CData.getBegin()) {
SR.markLive(CData.getBegin());
@@ -810,13 +460,13 @@ void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
}
}
-void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
- CheckerContext &C) const {
+void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
// Cleanup
auto State = C.getState();
auto RegionMap = State->get<IteratorRegionMap>();
- for (const auto Reg : RegionMap) {
+ for (const auto &Reg : RegionMap) {
if (!SR.isLiveRegion(Reg.first)) {
// The region behind the `LazyCompoundVal` is often cleaned up before
// the `LazyCompoundVal` itself. If there are iterator positions keyed
@@ -828,14 +478,14 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
}
auto SymbolMap = State->get<IteratorSymbolMap>();
- for (const auto Sym : SymbolMap) {
+ for (const auto &Sym : SymbolMap) {
if (!SR.isLive(Sym.first)) {
State = State->remove<IteratorSymbolMap>(Sym.first);
}
}
auto ContMap = State->get<ContainerMap>();
- for (const auto Cont : ContMap) {
+ for (const auto &Cont : ContMap) {
if (!SR.isLiveRegion(Cont.first)) {
// We must keep the container data while it has live iterators to be able
// to compare them to the begin and the end of the container.
@@ -848,8 +498,8 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
C.addTransition(State);
}
-void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE,
- const SVal &RetVal, const SVal &LVal,
+void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
+ SVal RetVal, const SVal &LVal,
const SVal &RVal,
OverloadedOperatorKind Op) const {
// Record the operands and the operator of the comparison for the next
@@ -888,13 +538,23 @@ void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE,
RPos = getIteratorPosition(State, RVal);
}
+ // We cannot make assumpotions on `UnknownVal`. Let us conjure a symbol
+ // instead.
+ if (RetVal.isUnknown()) {
+ auto &SymMgr = C.getSymbolManager();
+ auto *LCtx = C.getLocationContext();
+ RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
+ CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
+ State = State->BindExpr(CE, LCtx, RetVal);
+ }
+
processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
}
-void IteratorChecker::processComparison(CheckerContext &C,
- ProgramStateRef State, SymbolRef Sym1,
- SymbolRef Sym2, const SVal &RetVal,
- OverloadedOperatorKind Op) const {
+void IteratorModeling::processComparison(CheckerContext &C,
+ ProgramStateRef State, SymbolRef Sym1,
+ SymbolRef Sym2, const SVal &RetVal,
+ OverloadedOperatorKind Op) const {
if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
if ((State = relateSymbols(State, Sym1, Sym2,
(Op == OO_EqualEqual) ==
@@ -909,7 +569,7 @@ void IteratorChecker::processComparison(CheckerContext &C,
const auto ConditionVal = RetVal.getAs<DefinedSVal>();
if (!ConditionVal)
return;
-
+
if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
StateTrue = StateTrue->assume(*ConditionVal, true);
C.addTransition(StateTrue);
@@ -921,75 +581,68 @@ void IteratorChecker::processComparison(CheckerContext &C,
}
}
-void IteratorChecker::verifyDereference(CheckerContext &C,
- const SVal &Val) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Val);
- if (Pos && isPastTheEnd(State, *Pos)) {
- auto *N = C.generateErrorNode(State);
- if (!N)
- return;
- reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N);
- return;
- }
-}
-
-void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Val);
- if (Pos && !Pos->isValid()) {
- auto *N = C.generateErrorNode(State);
- if (!N) {
- return;
- }
- reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
- }
-}
-
-void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
- const SVal &Iter, bool Postfix) const {
+void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
+ const SVal &Iter, bool Postfix) const {
// Increment the symbolic expressions which represents the position of the
// iterator
auto State = C.getState();
+ auto &BVF = C.getSymbolManager().getBasicVals();
+
const auto *Pos = getIteratorPosition(State, Iter);
- if (Pos) {
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- const auto NewPos =
- advancePosition(C, OO_Plus, *Pos,
+ if (!Pos)
+ return;
+
+ auto NewState =
+ advancePosition(State, Iter, OO_Plus,
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
- State = setIteratorPosition(State, Iter, NewPos);
- State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
- C.addTransition(State);
- }
+ assert(NewState &&
+ "Advancing position by concrete int should always be successful");
+
+ const auto *NewPos = getIteratorPosition(NewState, Iter);
+ assert(NewPos &&
+ "Iterator should have position after successful advancement");
+
+ State = setIteratorPosition(State, Iter, *NewPos);
+ State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
+ C.addTransition(State);
}
-void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
- const SVal &Iter, bool Postfix) const {
+void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
+ const SVal &Iter, bool Postfix) const {
// Decrement the symbolic expressions which represents the position of the
// iterator
auto State = C.getState();
+ auto &BVF = C.getSymbolManager().getBasicVals();
+
const auto *Pos = getIteratorPosition(State, Iter);
- if (Pos) {
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- const auto NewPos =
- advancePosition(C, OO_Minus, *Pos,
+ if (!Pos)
+ return;
+
+ auto NewState =
+ advancePosition(State, Iter, OO_Minus,
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
- State = setIteratorPosition(State, Iter, NewPos);
- State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
- C.addTransition(State);
- }
+ assert(NewState &&
+ "Advancing position by concrete int should always be successful");
+
+ const auto *NewPos = getIteratorPosition(NewState, Iter);
+ assert(NewPos &&
+ "Iterator should have position after successful advancement");
+
+ State = setIteratorPosition(State, Iter, *NewPos);
+ State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
+ C.addTransition(State);
}
-void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
- OverloadedOperatorKind Op,
- const SVal &RetVal,
- const SVal &LHS,
- const SVal &RHS) const {
+void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C,
+ const Expr *CE,
+ OverloadedOperatorKind Op,
+ const SVal &RetVal,
+ const SVal &LHS,
+ const SVal &RHS) const {
// Increment or decrement the symbolic expressions which represents the
// position of the iterator
auto State = C.getState();
+
const auto *Pos = getIteratorPosition(State, LHS);
if (!Pos)
return;
@@ -1001,142 +654,23 @@ void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
}
auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
- State =
- setIteratorPosition(State, TgtVal, advancePosition(C, Op, *Pos, *value));
- C.addTransition(State);
-}
-
-void IteratorChecker::verifyIncrement(CheckerContext &C,
- const SVal &Iter) const {
- auto &BVF = C.getSValBuilder().getBasicValueFactory();
- verifyRandomIncrOrDecr(C, OO_Plus, Iter,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
-}
-
-void IteratorChecker::verifyDecrement(CheckerContext &C,
- const SVal &Iter) const {
- auto &BVF = C.getSValBuilder().getBasicValueFactory();
- verifyRandomIncrOrDecr(C, OO_Minus, Iter,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
-}
-void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
- OverloadedOperatorKind Op,
- const SVal &LHS,
- const SVal &RHS) const {
- auto State = C.getState();
-
- // If the iterator is initially inside its range, then the operation is valid
- const auto *Pos = getIteratorPosition(State, LHS);
- if (!Pos)
- return;
-
- auto Value = RHS;
- if (auto ValAsLoc = RHS.getAs<Loc>()) {
- Value = State->getRawSVal(*ValAsLoc);
- }
-
- if (Value.isUnknown())
- return;
-
- // Incremention or decremention by 0 is never a bug.
- if (isZero(State, Value.castAs<NonLoc>()))
- return;
-
- // The result may be the past-end iterator of the container, but any other
- // out of range position is undefined behaviour
- if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) {
- auto *N = C.generateErrorNode(State);
- if (!N)
- return;
- reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS,
- C, N);
- }
- if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) {
- auto *N = C.generateErrorNode(State);
- if (!N)
- return;
- reportOutOfRangeBug("Iterator incremented behind the past-the-end "
- "iterator.", LHS, C, N);
- }
-}
-
-void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
- const MemRegion *Cont) const {
- // Verify match between a container and the container of an iterator
- Cont = Cont->getMostDerivedObjectRegion();
+ auto NewState =
+ advancePosition(State, LHS, Op, *value);
+ if (NewState) {
+ const auto *NewPos = getIteratorPosition(NewState, LHS);
+ assert(NewPos &&
+ "Iterator should have position after successful advancement");
- if (const auto *ContSym = Cont->getSymbolicBase()) {
- if (isa<SymbolConjured>(ContSym->getSymbol()))
- return;
- }
-
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (!Pos)
- return;
-
- const auto *IterCont = Pos->getContainer();
-
- // Skip symbolic regions based on conjured symbols. Two conjured symbols
- // may or may not be the same. For example, the same function can return
- // the same or a different container but we get different conjured symbols
- // for each call. This may cause false positives so omit them from the check.
- if (const auto *ContSym = IterCont->getSymbolicBase()) {
- if (isa<SymbolConjured>(ContSym->getSymbol()))
- return;
- }
-
- if (IterCont != Cont) {
- auto *N = C.generateNonFatalErrorNode(State);
- if (!N) {
- return;
- }
- reportMismatchedBug("Container accessed using foreign iterator argument.",
- Iter, Cont, C, N);
- }
-}
-
-void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const {
- // Verify match between the containers of two iterators
- auto State = C.getState();
- const auto *Pos1 = getIteratorPosition(State, Iter1);
- if (!Pos1)
- return;
-
- const auto *IterCont1 = Pos1->getContainer();
-
- // Skip symbolic regions based on conjured symbols. Two conjured symbols
- // may or may not be the same. For example, the same function can return
- // the same or a different container but we get different conjured symbols
- // for each call. This may cause false positives so omit them from the check.
- if (const auto *ContSym = IterCont1->getSymbolicBase()) {
- if (isa<SymbolConjured>(ContSym->getSymbol()))
- return;
- }
-
- const auto *Pos2 = getIteratorPosition(State, Iter2);
- if (!Pos2)
- return;
-
- const auto *IterCont2 = Pos2->getContainer();
- if (const auto *ContSym = IterCont2->getSymbolicBase()) {
- if (isa<SymbolConjured>(ContSym->getSymbol()))
- return;
- }
-
- if (IterCont1 != IterCont2) {
- auto *N = C.generateNonFatalErrorNode(State);
- if (!N)
- return;
- reportMismatchedBug("Iterators of different containers used where the "
- "same container is expected.", Iter1, Iter2, C, N);
+ State = setIteratorPosition(NewState, TgtVal, *NewPos);
+ C.addTransition(State);
+ } else {
+ assignToContainer(C, CE, TgtVal, Pos->getContainer());
}
}
-void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
- const SVal &RetVal, const SVal &Cont) const {
+void IteratorModeling::handleBegin(CheckerContext &C, const Expr *CE,
+ const SVal &RetVal, const SVal &Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -1157,8 +691,8 @@ void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
C.addTransition(State);
}
-void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
- const SVal &RetVal, const SVal &Cont) const {
+void IteratorModeling::handleEnd(CheckerContext &C, const Expr *CE,
+ const SVal &RetVal, const SVal &Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -1179,9 +713,9 @@ void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
C.addTransition(State);
}
-void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
- const SVal &RetVal,
- const MemRegion *Cont) const {
+void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
+ const SVal &RetVal,
+ const MemRegion *Cont) const {
Cont = Cont->getMostDerivedObjectRegion();
auto State = C.getState();
@@ -1194,8 +728,8 @@ void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
C.addTransition(State);
}
-void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
- const Expr *CE, const SVal &OldCont) const {
+void IteratorModeling::handleAssign(CheckerContext &C, const SVal &Cont,
+ const Expr *CE, const SVal &OldCont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -1270,7 +804,7 @@ void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
C.addTransition(State);
}
-void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
+void IteratorModeling::handleClear(CheckerContext &C, const SVal &Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -1296,8 +830,8 @@ void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
C.addTransition(State);
}
-void IteratorChecker::handlePushBack(CheckerContext &C,
- const SVal &Cont) const {
+void IteratorModeling::handlePushBack(CheckerContext &C,
+ const SVal &Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -1334,7 +868,8 @@ void IteratorChecker::handlePushBack(CheckerContext &C,
C.addTransition(State);
}
-void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const {
+void IteratorModeling::handlePopBack(CheckerContext &C,
+ const SVal &Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -1371,8 +906,8 @@ void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const {
}
}
-void IteratorChecker::handlePushFront(CheckerContext &C,
- const SVal &Cont) const {
+void IteratorModeling::handlePushFront(CheckerContext &C,
+ const SVal &Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -1404,8 +939,8 @@ void IteratorChecker::handlePushFront(CheckerContext &C,
}
}
-void IteratorChecker::handlePopFront(CheckerContext &C,
- const SVal &Cont) const {
+void IteratorModeling::handlePopFront(CheckerContext &C,
+ const SVal &Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -1438,7 +973,7 @@ void IteratorChecker::handlePopFront(CheckerContext &C,
}
}
-void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
+void IteratorModeling::handleInsert(CheckerContext &C, const SVal &Iter) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Iter);
if (!Pos)
@@ -1463,7 +998,7 @@ void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
}
}
-void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
+void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Iter);
if (!Pos)
@@ -1491,8 +1026,8 @@ void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
C.addTransition(State);
}
-void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const {
+void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter1,
+ const SVal &Iter2) const {
auto State = C.getState();
const auto *Pos1 = getIteratorPosition(State, Iter1);
const auto *Pos2 = getIteratorPosition(State, Iter2);
@@ -1523,8 +1058,8 @@ void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
C.addTransition(State);
}
-void IteratorChecker::handleEraseAfter(CheckerContext &C,
- const SVal &Iter) const {
+void IteratorModeling::handleEraseAfter(CheckerContext &C,
+ const SVal &Iter) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Iter);
if (!Pos)
@@ -1544,8 +1079,8 @@ void IteratorChecker::handleEraseAfter(CheckerContext &C,
C.addTransition(State);
}
-void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const {
+void IteratorModeling::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
+ const SVal &Iter2) const {
auto State = C.getState();
const auto *Pos1 = getIteratorPosition(State, Iter1);
const auto *Pos2 = getIteratorPosition(State, Iter2);
@@ -1558,145 +1093,62 @@ void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
C.addTransition(State);
}
-IteratorPosition IteratorChecker::advancePosition(CheckerContext &C,
- OverloadedOperatorKind Op,
- const IteratorPosition &Pos,
- const SVal &Distance) const {
- auto State = C.getState();
- auto &SymMgr = C.getSymbolManager();
- auto &SVB = C.getSValBuilder();
+void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
- assert ((Op == OO_Plus || Op == OO_PlusEqual ||
- Op == OO_Minus || Op == OO_MinusEqual) &&
- "Advance operator must be one of +, -, += and -=.");
- auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
- if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) {
- // For concrete integers we can calculate the new position
- return Pos.setTo(SVB.evalBinOp(State, BinOp,
- nonloc::SymbolVal(Pos.getOffset()), *IntDist,
- SymMgr.getType(Pos.getOffset()))
- .getAsSymbol());
- } else {
- // For other symbols create a new symbol to keep expressions simple
- const auto &LCtx = C.getLocationContext();
- const auto NewPosSym = SymMgr.conjureSymbol(nullptr, LCtx,
- SymMgr.getType(Pos.getOffset()),
- C.blockCount());
- State = assumeNoOverflow(State, NewPosSym, 4);
- return Pos.setTo(NewPosSym);
+ auto ContMap = State->get<ContainerMap>();
+
+ if (!ContMap.isEmpty()) {
+ Out << Sep << "Container Data :" << NL;
+ for (const auto &Cont : ContMap) {
+ Cont.first->dumpToStream(Out);
+ Out << " : [ ";
+ const auto CData = Cont.second;
+ if (CData.getBegin())
+ CData.getBegin()->dumpToStream(Out);
+ else
+ Out << "<Unknown>";
+ Out << " .. ";
+ if (CData.getEnd())
+ CData.getEnd()->dumpToStream(Out);
+ else
+ Out << "<Unknown>";
+ Out << " ]" << NL;
+ }
}
-}
-void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
- const SVal &Val, CheckerContext &C,
- ExplodedNode *ErrNode) const {
- auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
- ErrNode);
- R->markInteresting(Val);
- C.emitReport(std::move(R));
-}
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ auto RegionMap = State->get<IteratorRegionMap>();
-void IteratorChecker::reportMismatchedBug(const StringRef &Message,
- const SVal &Val1, const SVal &Val2,
- CheckerContext &C,
- ExplodedNode *ErrNode) const {
- auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
- ErrNode);
- R->markInteresting(Val1);
- R->markInteresting(Val2);
- C.emitReport(std::move(R));
-}
+ if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
+ Out << Sep << "Iterator Positions :" << NL;
+ for (const auto &Sym : SymbolMap) {
+ Sym.first->dumpToStream(Out);
+ Out << " : ";
+ const auto Pos = Sym.second;
+ Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
+ Pos.getContainer()->dumpToStream(Out);
+ Out<<" ; Offset == ";
+ Pos.getOffset()->dumpToStream(Out);
+ }
-void IteratorChecker::reportMismatchedBug(const StringRef &Message,
- const SVal &Val, const MemRegion *Reg,
- CheckerContext &C,
- ExplodedNode *ErrNode) const {
- auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
- ErrNode);
- R->markInteresting(Val);
- R->markInteresting(Reg);
- C.emitReport(std::move(R));
+ for (const auto &Reg : RegionMap) {
+ Reg.first->dumpToStream(Out);
+ Out << " : ";
+ const auto Pos = Reg.second;
+ Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
+ Pos.getContainer()->dumpToStream(Out);
+ Out<<" ; Offset == ";
+ Pos.getOffset()->dumpToStream(Out);
+ }
+ }
}
-void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
- const SVal &Val, CheckerContext &C,
- ExplodedNode *ErrNode) const {
- auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType,
- Message, ErrNode);
- R->markInteresting(Val);
- C.emitReport(std::move(R));
-}
namespace {
-bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
-bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
-bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
-bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
- BinaryOperator::Opcode Opc);
-bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
- BinaryOperator::Opcode Opc);
const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
const MemRegion *Reg);
-SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
- SymbolRef OldSym, SymbolRef NewSym);
-
-bool isIteratorType(const QualType &Type) {
- if (Type->isPointerType())
- return true;
-
- const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
- return isIterator(CRD);
-}
-
-bool isIterator(const CXXRecordDecl *CRD) {
- if (!CRD)
- return false;
-
- const auto Name = CRD->getName();
- if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
- Name.endswith_lower("it")))
- return false;
-
- bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
- HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
- for (const auto *Method : CRD->methods()) {
- if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
- if (Ctor->isCopyConstructor()) {
- HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
- }
- continue;
- }
- if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
- HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
- continue;
- }
- if (Method->isCopyAssignmentOperator()) {
- HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
- continue;
- }
- if (!Method->isOverloadedOperator())
- continue;
- const auto OPK = Method->getOverloadedOperator();
- if (OPK == OO_PlusPlus) {
- HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
- HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
- continue;
- }
- if (OPK == OO_Star) {
- HasDerefOp = (Method->getNumParams() == 0);
- continue;
- }
- }
-
- return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
- HasPostIncrOp && HasDerefOp;
-}
-
-bool isComparisonOperator(OverloadedOperatorKind OK) {
- return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
- OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
-}
bool isBeginCall(const FunctionDecl *Func) {
const auto *IdInfo = Func->getIdentifier();
@@ -1784,85 +1236,12 @@ bool isPopFrontCall(const FunctionDecl *Func) {
return IdInfo->getName() == "pop_front";
}
-bool isInsertCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
- return false;
- if (!isIteratorType(Func->getParamDecl(0)->getType()))
- return false;
- return IdInfo->getName() == "insert";
-}
-
-bool isEmplaceCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() < 2)
- return false;
- if (!isIteratorType(Func->getParamDecl(0)->getType()))
- return false;
- return IdInfo->getName() == "emplace";
-}
-
-bool isEraseCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
- return false;
- if (!isIteratorType(Func->getParamDecl(0)->getType()))
- return false;
- if (Func->getNumParams() == 2 &&
- !isIteratorType(Func->getParamDecl(1)->getType()))
- return false;
- return IdInfo->getName() == "erase";
-}
-
-bool isEraseAfterCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
- return false;
- if (!isIteratorType(Func->getParamDecl(0)->getType()))
- return false;
- if (Func->getNumParams() == 2 &&
- !isIteratorType(Func->getParamDecl(1)->getType()))
- return false;
- return IdInfo->getName() == "erase_after";
-}
-
bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
}
-bool isAccessOperator(OverloadedOperatorKind OK) {
- return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
- isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
-}
-
-bool isDereferenceOperator(OverloadedOperatorKind OK) {
- return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
- OK == OO_Subscript;
-}
-
-bool isIncrementOperator(OverloadedOperatorKind OK) {
- return OK == OO_PlusPlus;
-}
-
-bool isDecrementOperator(OverloadedOperatorKind OK) {
- return OK == OO_MinusMinus;
-}
-
-bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
- return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
- OK == OO_MinusEqual;
-}
-
bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
const auto *CRD = getCXXRecordDecl(State, Reg);
if (!CRD)
@@ -1985,52 +1364,62 @@ ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
return setContainerData(State, Cont, CData);
}
-const ContainerData *getContainerData(ProgramStateRef State,
- const MemRegion *Cont) {
- return State->get<ContainerMap>(Cont);
-}
-
ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
const ContainerData &CData) {
return State->set<ContainerMap>(Cont, CData);
}
-const IteratorPosition *getIteratorPosition(ProgramStateRef State,
- const SVal &Val) {
+ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
if (auto Reg = Val.getAsRegion()) {
Reg = Reg->getMostDerivedObjectRegion();
- return State->get<IteratorRegionMap>(Reg);
+ return State->remove<IteratorRegionMap>(Reg);
} else if (const auto Sym = Val.getAsSymbol()) {
- return State->get<IteratorSymbolMap>(Sym);
+ return State->remove<IteratorSymbolMap>(Sym);
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
- return State->get<IteratorRegionMap>(LCVal->getRegion());
+ return State->remove<IteratorRegionMap>(LCVal->getRegion());
}
return nullptr;
}
-ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
- const IteratorPosition &Pos) {
- if (auto Reg = Val.getAsRegion()) {
- Reg = Reg->getMostDerivedObjectRegion();
- return State->set<IteratorRegionMap>(Reg, Pos);
- } else if (const auto Sym = Val.getAsSymbol()) {
- return State->set<IteratorSymbolMap>(Sym, Pos);
- } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
- return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
+// This function tells the analyzer's engine that symbols produced by our
+// checker, most notably iterator positions, are relatively small.
+// A distance between items in the container should not be very large.
+// By assuming that it is within around 1/8 of the address space,
+// we can help the analyzer perform operations on these symbols
+// without being afraid of integer overflows.
+// FIXME: Should we provide it as an API, so that all checkers could use it?
+ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
+ long Scale) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ BasicValueFactory &BV = SVB.getBasicValueFactory();
+
+ QualType T = Sym->getType();
+ assert(T->isSignedIntegerOrEnumerationType());
+ APSIntType AT = BV.getAPSIntType(T);
+
+ ProgramStateRef NewState = State;
+
+ llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
+ SVal IsCappedFromAbove =
+ SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
+ nonloc::ConcreteInt(Max), SVB.getConditionType());
+ if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
+ NewState = NewState->assume(*DV, true);
+ if (!NewState)
+ return State;
}
- return nullptr;
-}
-ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
- if (auto Reg = Val.getAsRegion()) {
- Reg = Reg->getMostDerivedObjectRegion();
- return State->remove<IteratorRegionMap>(Reg);
- } else if (const auto Sym = Val.getAsSymbol()) {
- return State->remove<IteratorSymbolMap>(Sym);
- } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
- return State->remove<IteratorRegionMap>(LCVal->getRegion());
+ llvm::APSInt Min = -Max;
+ SVal IsCappedFromBelow =
+ SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
+ nonloc::ConcreteInt(Min), SVB.getConditionType());
+ if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
+ NewState = NewState->assume(*DV, true);
+ if (!NewState)
+ return State;
}
- return nullptr;
+
+ return NewState;
}
ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
@@ -2067,13 +1456,13 @@ ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
auto RegionMap = State->get<IteratorRegionMap>();
- for (const auto Reg : RegionMap) {
+ for (const auto &Reg : RegionMap) {
if (Reg.second.getContainer() == Cont)
return true;
}
auto SymbolMap = State->get<IteratorSymbolMap>();
- for (const auto Sym : SymbolMap) {
+ for (const auto &Sym : SymbolMap) {
if (Sym.second.getContainer() == Cont)
return true;
}
@@ -2083,7 +1472,7 @@ bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
bool isBoundThroughLazyCompoundVal(const Environment &Env,
const MemRegion *Reg) {
- for (const auto Binding: Env) {
+ for (const auto &Binding : Env) {
if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
if (LCVal->getRegion() == Reg)
return true;
@@ -2093,54 +1482,13 @@ bool isBoundThroughLazyCompoundVal(const Environment &Env,
return false;
}
-// This function tells the analyzer's engine that symbols produced by our
-// checker, most notably iterator positions, are relatively small.
-// A distance between items in the container should not be very large.
-// By assuming that it is within around 1/8 of the address space,
-// we can help the analyzer perform operations on these symbols
-// without being afraid of integer overflows.
-// FIXME: Should we provide it as an API, so that all checkers could use it?
-ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
- long Scale) {
- SValBuilder &SVB = State->getStateManager().getSValBuilder();
- BasicValueFactory &BV = SVB.getBasicValueFactory();
-
- QualType T = Sym->getType();
- assert(T->isSignedIntegerOrEnumerationType());
- APSIntType AT = BV.getAPSIntType(T);
-
- ProgramStateRef NewState = State;
-
- llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
- SVal IsCappedFromAbove =
- SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
- nonloc::ConcreteInt(Max), SVB.getConditionType());
- if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
- NewState = NewState->assume(*DV, true);
- if (!NewState)
- return State;
- }
-
- llvm::APSInt Min = -Max;
- SVal IsCappedFromBelow =
- SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
- nonloc::ConcreteInt(Min), SVB.getConditionType());
- if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
- NewState = NewState->assume(*DV, true);
- if (!NewState)
- return State;
- }
-
- return NewState;
-}
-
template <typename Condition, typename Process>
ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
Process Proc) {
auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
auto RegionMap = State->get<IteratorRegionMap>();
bool Changed = false;
- for (const auto Reg : RegionMap) {
+ for (const auto &Reg : RegionMap) {
if (Cond(Reg.second)) {
RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
Changed = true;
@@ -2153,7 +1501,7 @@ ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
auto SymbolMap = State->get<IteratorSymbolMap>();
Changed = false;
- for (const auto Sym : SymbolMap) {
+ for (const auto &Sym : SymbolMap) {
if (Cond(Sym.second)) {
SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
Changed = true;
@@ -2280,111 +1628,12 @@ SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
SymMgr.getType(OrigExpr)).getAsSymbol();
}
-bool isZero(ProgramStateRef State, const NonLoc &Val) {
- auto &BVF = State->getBasicVals();
- return compare(State, Val,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
- BO_EQ);
-}
-
-bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
- const auto *Cont = Pos.getContainer();
- const auto *CData = getContainerData(State, Cont);
- if (!CData)
- return false;
-
- const auto End = CData->getEnd();
- if (End) {
- if (isEqual(State, Pos.getOffset(), End)) {
- return true;
- }
- }
-
- return false;
-}
-
-bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
- const auto *Cont = Pos.getContainer();
- const auto *CData = getContainerData(State, Cont);
- if (!CData)
- return false;
-
- const auto Beg = CData->getBegin();
- if (Beg) {
- if (isLess(State, Pos.getOffset(), Beg)) {
- return true;
- }
- }
-
- return false;
-}
-
-bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
- const auto *Cont = Pos.getContainer();
- const auto *CData = getContainerData(State, Cont);
- if (!CData)
- return false;
-
- const auto End = CData->getEnd();
- if (End) {
- if (isGreater(State, Pos.getOffset(), End)) {
- return true;
- }
- }
-
- return false;
-}
-
-bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
- return compare(State, Sym1, Sym2, BO_LT);
-}
-
-bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
- return compare(State, Sym1, Sym2, BO_GT);
-}
-
-bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
- return compare(State, Sym1, Sym2, BO_EQ);
-}
-
-bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
- BinaryOperator::Opcode Opc) {
- return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
-}
-
-bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
- BinaryOperator::Opcode Opc) {
- auto &SVB = State->getStateManager().getSValBuilder();
-
- const auto comparison =
- SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
-
- assert(comparison.getAs<DefinedSVal>() &&
- "Symbol comparison must be a `DefinedSVal`");
-
- return !State->assume(comparison.castAs<DefinedSVal>(), false);
-}
-
} // namespace
void ento::registerIteratorModeling(CheckerManager &mgr) {
- mgr.registerChecker<IteratorChecker>();
+ mgr.registerChecker<IteratorModeling>();
}
bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
return true;
}
-
-#define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &Mgr) { \
- auto *checker = Mgr.getChecker<IteratorChecker>(); \
- checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
- checker->CheckNames[IteratorChecker::CK_##name] = \
- Mgr.getCurrentCheckerName(); \
- } \
- \
- bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
-
-REGISTER_CHECKER(IteratorRangeChecker)
-REGISTER_CHECKER(MismatchedIteratorChecker)
-REGISTER_CHECKER(InvalidatedIteratorChecker)
diff --git a/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
new file mode 100644
index 000000000000..bd8b84d464b6
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
@@ -0,0 +1,273 @@
+//===-- IteratorRangeChecker.cpp ----------------------------------*- C++ -*--//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for dereference of the past-the-end iterator and
+// out-of-range increments and decrements.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+
+#include "Iterator.h"
+
+using namespace clang;
+using namespace ento;
+using namespace iterator;
+
+namespace {
+
+class IteratorRangeChecker
+ : public Checker<check::PreCall> {
+
+ std::unique_ptr<BugType> OutOfRangeBugType;
+
+ void verifyDereference(CheckerContext &C, const SVal &Val) const;
+ void verifyIncrement(CheckerContext &C, const SVal &Iter) const;
+ void verifyDecrement(CheckerContext &C, const SVal &Iter) const;
+ void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
+ const SVal &LHS, const SVal &RHS) const;
+ void reportBug(const StringRef &Message, const SVal &Val,
+ CheckerContext &C, ExplodedNode *ErrNode) const;
+public:
+ IteratorRangeChecker();
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+};
+
+bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
+bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
+bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
+bool isZero(ProgramStateRef State, const NonLoc &Val);
+
+} //namespace
+
+IteratorRangeChecker::IteratorRangeChecker() {
+ OutOfRangeBugType.reset(
+ new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
+}
+
+void IteratorRangeChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // Check for out of range access
+ const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!Func)
+ return;
+
+ if (Func->isOverloadedOperator()) {
+ if (isIncrementOperator(Func->getOverloadedOperator())) {
+ // Check for out-of-range incrementions
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ verifyIncrement(C, InstCall->getCXXThisVal());
+ } else {
+ if (Call.getNumArgs() >= 1) {
+ verifyIncrement(C, Call.getArgSVal(0));
+ }
+ }
+ } else if (isDecrementOperator(Func->getOverloadedOperator())) {
+ // Check for out-of-range decrementions
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ verifyDecrement(C, InstCall->getCXXThisVal());
+ } else {
+ if (Call.getNumArgs() >= 1) {
+ verifyDecrement(C, Call.getArgSVal(0));
+ }
+ }
+ } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ // Check for out-of-range incrementions and decrementions
+ if (Call.getNumArgs() >= 1 &&
+ Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
+ verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ InstCall->getCXXThisVal(),
+ Call.getArgSVal(0));
+ }
+ } else {
+ if (Call.getNumArgs() >= 2 &&
+ Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
+ verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ Call.getArgSVal(0), Call.getArgSVal(1));
+ }
+ }
+ } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
+ // Check for dereference of out-of-range iterators
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ verifyDereference(C, InstCall->getCXXThisVal());
+ } else {
+ verifyDereference(C, Call.getArgSVal(0));
+ }
+ }
+ }
+}
+
+void IteratorRangeChecker::verifyDereference(CheckerContext &C,
+ const SVal &Val) const {
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Val);
+ if (Pos && isPastTheEnd(State, *Pos)) {
+ auto *N = C.generateErrorNode(State);
+ if (!N)
+ return;
+ reportBug("Past-the-end iterator dereferenced.", Val, C, N);
+ return;
+ }
+}
+
+void IteratorRangeChecker::verifyIncrement(CheckerContext &C,
+ const SVal &Iter) const {
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ verifyRandomIncrOrDecr(C, OO_Plus, Iter,
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
+}
+
+void IteratorRangeChecker::verifyDecrement(CheckerContext &C,
+ const SVal &Iter) const {
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ verifyRandomIncrOrDecr(C, OO_Minus, Iter,
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
+}
+
+void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C,
+ OverloadedOperatorKind Op,
+ const SVal &LHS,
+ const SVal &RHS) const {
+ auto State = C.getState();
+
+ auto Value = RHS;
+ if (auto ValAsLoc = RHS.getAs<Loc>()) {
+ Value = State->getRawSVal(*ValAsLoc);
+ }
+
+ if (Value.isUnknown())
+ return;
+
+ // Incremention or decremention by 0 is never a bug.
+ if (isZero(State, Value.castAs<NonLoc>()))
+ return;
+
+ // The result may be the past-end iterator of the container, but any other
+ // out of range position is undefined behaviour
+ auto StateAfter = advancePosition(State, LHS, Op, Value);
+ if (!StateAfter)
+ return;
+
+ const auto *PosAfter = getIteratorPosition(StateAfter, LHS);
+ assert(PosAfter &&
+ "Iterator should have position after successful advancement");
+ if (isAheadOfRange(State, *PosAfter)) {
+ auto *N = C.generateErrorNode(State);
+ if (!N)
+ return;
+ reportBug("Iterator decremented ahead of its valid range.", LHS,
+ C, N);
+ }
+ if (isBehindPastTheEnd(State, *PosAfter)) {
+ auto *N = C.generateErrorNode(State);
+ if (!N)
+ return;
+ reportBug("Iterator incremented behind the past-the-end "
+ "iterator.", LHS, C, N);
+ }
+}
+
+void IteratorRangeChecker::reportBug(const StringRef &Message,
+ const SVal &Val, CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
+ ErrNode);
+ R->markInteresting(Val);
+ C.emitReport(std::move(R));
+}
+
+namespace {
+
+bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
+bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
+bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
+
+bool isZero(ProgramStateRef State, const NonLoc &Val) {
+ auto &BVF = State->getBasicVals();
+ return compare(State, Val,
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
+ BO_EQ);
+}
+
+bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
+ const auto *Cont = Pos.getContainer();
+ const auto *CData = getContainerData(State, Cont);
+ if (!CData)
+ return false;
+
+ const auto End = CData->getEnd();
+ if (End) {
+ if (isEqual(State, Pos.getOffset(), End)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
+ const auto *Cont = Pos.getContainer();
+ const auto *CData = getContainerData(State, Cont);
+ if (!CData)
+ return false;
+
+ const auto Beg = CData->getBegin();
+ if (Beg) {
+ if (isLess(State, Pos.getOffset(), Beg)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
+ const auto *Cont = Pos.getContainer();
+ const auto *CData = getContainerData(State, Cont);
+ if (!CData)
+ return false;
+
+ const auto End = CData->getEnd();
+ if (End) {
+ if (isGreater(State, Pos.getOffset(), End)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
+ return compare(State, Sym1, Sym2, BO_LT);
+}
+
+bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
+ return compare(State, Sym1, Sym2, BO_GT);
+}
+
+bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
+ return compare(State, Sym1, Sym2, BO_EQ);
+}
+
+} // namespace
+
+void ento::registerIteratorRangeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<IteratorRangeChecker>();
+}
+
+bool ento::shouldRegisterIteratorRangeChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
index a81015b6e524..79de1844e745 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -1077,7 +1077,10 @@ void EmptyLocalizationContextChecker::checkASTDecl(
AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
const Stmt *Body = M->getBody();
- assert(Body);
+ if (!Body) {
+ assert(M->isSynthesizedAccessorStub());
+ continue;
+ }
MethodCrawler MC(M->getCanonicalDecl(), BR, this, Mgr, DCtx);
MC.VisitStmt(Body);
diff --git a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
index d8fd125f4003..d73e2eb92d42 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
@@ -21,6 +21,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/Attr.h"
#include "clang/Analysis/AnyCall.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
index d964a1668eaa..410721d8b6ff 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
@@ -14,8 +14,9 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index a82449951873..09306383d53f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1469,6 +1469,9 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
if (!*FreeWhenDone)
return;
+ if (Call.hasNonZeroCallbackArg())
+ return;
+
bool IsKnownToBeAllocatedMemory;
ProgramStateRef State =
FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(),
@@ -2525,19 +2528,18 @@ MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,
// Find the most recent expression bound to the symbol in the current
// context.
- if (!ReferenceRegion) {
- if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) {
- SVal Val = State->getSVal(MR);
- if (Val.getAsLocSymbol() == Sym) {
- const VarRegion* VR = MR->getBaseRegion()->getAs<VarRegion>();
- // Do not show local variables belonging to a function other than
- // where the error is reported.
- if (!VR ||
- (VR->getStackFrame() == LeakContext->getStackFrame()))
- ReferenceRegion = MR;
- }
+ if (!ReferenceRegion) {
+ if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) {
+ SVal Val = State->getSVal(MR);
+ if (Val.getAsLocSymbol() == Sym) {
+ const VarRegion *VR = MR->getBaseRegion()->getAs<VarRegion>();
+ // Do not show local variables belonging to a function other than
+ // where the error is reported.
+ if (!VR || (VR->getStackFrame() == LeakContext->getStackFrame()))
+ ReferenceRegion = MR;
}
}
+ }
// Allocation node, is the last node in the current or parent context in
// which the symbol was tracked.
diff --git a/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
new file mode 100644
index 000000000000..143910588959
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
@@ -0,0 +1,295 @@
+//===-- MismatchedIteratorChecker.cpp -----------------------------*- C++ -*--//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for mistakenly applying a foreign iterator on a container
+// and for using iterators of two different containers in a context where
+// iterators of the same container should be used.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+
+#include "Iterator.h"
+
+using namespace clang;
+using namespace ento;
+using namespace iterator;
+
+namespace {
+
+class MismatchedIteratorChecker
+ : public Checker<check::PreCall> {
+
+ std::unique_ptr<BugType> MismatchedBugType;
+
+ void verifyMatch(CheckerContext &C, const SVal &Iter,
+ const MemRegion *Cont) const;
+ void verifyMatch(CheckerContext &C, const SVal &Iter1,
+ const SVal &Iter2) const;
+ void reportBug(const StringRef &Message, const SVal &Val1,
+ const SVal &Val2, CheckerContext &C,
+ ExplodedNode *ErrNode) const;
+ void reportBug(const StringRef &Message, const SVal &Val,
+ const MemRegion *Reg, CheckerContext &C,
+ ExplodedNode *ErrNode) const;
+
+public:
+ MismatchedIteratorChecker();
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+};
+
+} // namespace
+
+MismatchedIteratorChecker::MismatchedIteratorChecker() {
+ MismatchedBugType.reset(
+ new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
+ /*SuppressOnSink=*/true));
+}
+
+void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // Check for iterator mismatches
+ const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!Func)
+ return;
+
+ if (Func->isOverloadedOperator() &&
+ isComparisonOperator(Func->getOverloadedOperator())) {
+ // Check for comparisons of iterators of different containers
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (Call.getNumArgs() < 1)
+ return;
+
+ if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
+ !isIteratorType(Call.getArgExpr(0)->getType()))
+ return;
+
+ verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ } else {
+ if (Call.getNumArgs() < 2)
+ return;
+
+ if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
+ !isIteratorType(Call.getArgExpr(1)->getType()))
+ return;
+
+ verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ }
+ } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
+ if (!ContReg)
+ return;
+ // Check for erase, insert and emplace using iterator of another container
+ if (isEraseCall(Func) || isEraseAfterCall(Func)) {
+ verifyMatch(C, Call.getArgSVal(0),
+ InstCall->getCXXThisVal().getAsRegion());
+ if (Call.getNumArgs() == 2) {
+ verifyMatch(C, Call.getArgSVal(1),
+ InstCall->getCXXThisVal().getAsRegion());
+ }
+ } else if (isInsertCall(Func)) {
+ verifyMatch(C, Call.getArgSVal(0),
+ InstCall->getCXXThisVal().getAsRegion());
+ if (Call.getNumArgs() == 3 &&
+ isIteratorType(Call.getArgExpr(1)->getType()) &&
+ isIteratorType(Call.getArgExpr(2)->getType())) {
+ verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
+ }
+ } else if (isEmplaceCall(Func)) {
+ verifyMatch(C, Call.getArgSVal(0),
+ InstCall->getCXXThisVal().getAsRegion());
+ }
+ } else if (isa<CXXConstructorCall>(&Call)) {
+ // Check match of first-last iterator pair in a constructor of a container
+ if (Call.getNumArgs() < 2)
+ return;
+
+ const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
+ if (Ctr->getNumParams() < 2)
+ return;
+
+ if (Ctr->getParamDecl(0)->getName() != "first" ||
+ Ctr->getParamDecl(1)->getName() != "last")
+ return;
+
+ if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
+ !isIteratorType(Call.getArgExpr(1)->getType()))
+ return;
+
+ verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ } else {
+ // The main purpose of iterators is to abstract away from different
+ // containers and provide a (maybe limited) uniform access to them.
+ // This implies that any correctly written template function that
+ // works on multiple containers using iterators takes different
+ // template parameters for different containers. So we can safely
+ // assume that passing iterators of different containers as arguments
+ // whose type replaces the same template parameter is a bug.
+ //
+ // Example:
+ // template<typename I1, typename I2>
+ // void f(I1 first1, I1 last1, I2 first2, I2 last2);
+ //
+ // In this case the first two arguments to f() must be iterators must belong
+ // to the same container and the last to also to the same container but
+ // not necessarily to the same as the first two.
+
+ const auto *Templ = Func->getPrimaryTemplate();
+ if (!Templ)
+ return;
+
+ const auto *TParams = Templ->getTemplateParameters();
+ const auto *TArgs = Func->getTemplateSpecializationArgs();
+
+ // Iterate over all the template parameters
+ for (size_t I = 0; I < TParams->size(); ++I) {
+ const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
+ if (!TPDecl)
+ continue;
+
+ if (TPDecl->isParameterPack())
+ continue;
+
+ const auto TAType = TArgs->get(I).getAsType();
+ if (!isIteratorType(TAType))
+ continue;
+
+ SVal LHS = UndefinedVal();
+
+ // For every template parameter which is an iterator type in the
+ // instantiation look for all functions' parameters' type by it and
+ // check whether they belong to the same container
+ for (auto J = 0U; J < Func->getNumParams(); ++J) {
+ const auto *Param = Func->getParamDecl(J);
+ const auto *ParamType =
+ Param->getType()->getAs<SubstTemplateTypeParmType>();
+ if (!ParamType ||
+ ParamType->getReplacedParameter()->getDecl() != TPDecl)
+ continue;
+ if (LHS.isUndef()) {
+ LHS = Call.getArgSVal(J);
+ } else {
+ verifyMatch(C, LHS, Call.getArgSVal(J));
+ }
+ }
+ }
+ }
+}
+
+void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
+ const MemRegion *Cont) const {
+ // Verify match between a container and the container of an iterator
+ Cont = Cont->getMostDerivedObjectRegion();
+
+ if (const auto *ContSym = Cont->getSymbolicBase()) {
+ if (isa<SymbolConjured>(ContSym->getSymbol()))
+ return;
+ }
+
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (!Pos)
+ return;
+
+ const auto *IterCont = Pos->getContainer();
+
+ // Skip symbolic regions based on conjured symbols. Two conjured symbols
+ // may or may not be the same. For example, the same function can return
+ // the same or a different container but we get different conjured symbols
+ // for each call. This may cause false positives so omit them from the check.
+ if (const auto *ContSym = IterCont->getSymbolicBase()) {
+ if (isa<SymbolConjured>(ContSym->getSymbol()))
+ return;
+ }
+
+ if (IterCont != Cont) {
+ auto *N = C.generateNonFatalErrorNode(State);
+ if (!N) {
+ return;
+ }
+ reportBug("Container accessed using foreign iterator argument.",
+ Iter, Cont, C, N);
+ }
+}
+
+void MismatchedIteratorChecker::verifyMatch(CheckerContext &C,
+ const SVal &Iter1,
+ const SVal &Iter2) const {
+ // Verify match between the containers of two iterators
+ auto State = C.getState();
+ const auto *Pos1 = getIteratorPosition(State, Iter1);
+ if (!Pos1)
+ return;
+
+ const auto *IterCont1 = Pos1->getContainer();
+
+ // Skip symbolic regions based on conjured symbols. Two conjured symbols
+ // may or may not be the same. For example, the same function can return
+ // the same or a different container but we get different conjured symbols
+ // for each call. This may cause false positives so omit them from the check.
+ if (const auto *ContSym = IterCont1->getSymbolicBase()) {
+ if (isa<SymbolConjured>(ContSym->getSymbol()))
+ return;
+ }
+
+ const auto *Pos2 = getIteratorPosition(State, Iter2);
+ if (!Pos2)
+ return;
+
+ const auto *IterCont2 = Pos2->getContainer();
+ if (const auto *ContSym = IterCont2->getSymbolicBase()) {
+ if (isa<SymbolConjured>(ContSym->getSymbol()))
+ return;
+ }
+
+ if (IterCont1 != IterCont2) {
+ auto *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ reportBug("Iterators of different containers used where the "
+ "same container is expected.", Iter1, Iter2, C, N);
+ }
+}
+
+void MismatchedIteratorChecker::reportBug(const StringRef &Message,
+ const SVal &Val1,
+ const SVal &Val2,
+ CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
+ ErrNode);
+ R->markInteresting(Val1);
+ R->markInteresting(Val2);
+ C.emitReport(std::move(R));
+}
+
+void MismatchedIteratorChecker::reportBug(const StringRef &Message,
+ const SVal &Val, const MemRegion *Reg,
+ CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
+ ErrNode);
+ R->markInteresting(Val);
+ R->markInteresting(Reg);
+ C.emitReport(std::move(R));
+}
+
+void ento::registerMismatchedIteratorChecker(CheckerManager &mgr) {
+ mgr.registerChecker<MismatchedIteratorChecker>();
+}
+
+bool ento::shouldRegisterMismatchedIteratorChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
index 1473c05d7e3f..40eb113e3f8e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/Attr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
@@ -685,7 +686,7 @@ void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
- for (TrackedRegionMapTy::value_type E : TrackedRegions) {
+ for (auto E : TrackedRegions) {
const MemRegion *Region = E.first;
bool IsRegDead = !SymReaper.isLiveRegion(Region);
diff --git a/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
index 43dbe57b8432..6efba433eed2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
@@ -36,6 +36,7 @@ class NonnullGlobalConstantsChecker : public Checker<check::Location> {
mutable IdentifierInfo *NSStringII = nullptr;
mutable IdentifierInfo *CFStringRefII = nullptr;
mutable IdentifierInfo *CFBooleanRefII = nullptr;
+ mutable IdentifierInfo *CFNullRefII = nullptr;
public:
NonnullGlobalConstantsChecker() {}
@@ -61,6 +62,7 @@ void NonnullGlobalConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const {
NSStringII = &Ctx.Idents.get("NSString");
CFStringRefII = &Ctx.Idents.get("CFStringRef");
CFBooleanRefII = &Ctx.Idents.get("CFBooleanRef");
+ CFNullRefII = &Ctx.Idents.get("CFNullRef");
}
/// Add an assumption that const string-like globals are non-null.
@@ -136,7 +138,7 @@ bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const {
T->getInterfaceDecl()->getIdentifier() == NSStringII;
} else if (auto *T = dyn_cast<TypedefType>(Ty)) {
IdentifierInfo* II = T->getDecl()->getIdentifier();
- return II == CFStringRefII || II == CFBooleanRefII;
+ return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII;
}
return false;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
index 4322ac207112..922048733c7c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -728,11 +728,6 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
}
continue;
}
- // No tracked nullability yet.
- if (ArgExprTypeLevelNullability != Nullability::Nullable)
- continue;
- State = State->set<NullabilityMap>(
- Region, NullabilityState(ArgExprTypeLevelNullability, ArgExpr));
}
if (State != OrigState)
C.addTransition(State);
diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index c254408351c8..47099f2afb6a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -19,84 +19,90 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include <functional>
using namespace clang;
using namespace ento;
+using namespace std::placeholders;
namespace {
struct StreamState {
enum Kind { Opened, Closed, OpenFailed, Escaped } K;
- const Stmt *S;
- StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
+ StreamState(Kind k) : K(k) {}
bool isOpened() const { return K == Opened; }
bool isClosed() const { return K == Closed; }
//bool isOpenFailed() const { return K == OpenFailed; }
//bool isEscaped() const { return K == Escaped; }
- bool operator==(const StreamState &X) const {
- return K == X.K && S == X.S;
- }
+ bool operator==(const StreamState &X) const { return K == X.K; }
- static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
- static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
- static StreamState getOpenFailed(const Stmt *s) {
- return StreamState(OpenFailed, s);
- }
- static StreamState getEscaped(const Stmt *s) {
- return StreamState(Escaped, s);
- }
+ static StreamState getOpened() { return StreamState(Opened); }
+ static StreamState getClosed() { return StreamState(Closed); }
+ static StreamState getOpenFailed() { return StreamState(OpenFailed); }
+ static StreamState getEscaped() { return StreamState(Escaped); }
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(K);
- ID.AddPointer(S);
}
};
class StreamChecker : public Checker<eval::Call,
check::DeadSymbols > {
- mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
- *II_fwrite,
- *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
- *II_clearerr, *II_feof, *II_ferror, *II_fileno;
mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
BT_doubleclose, BT_ResourceLeak;
public:
- StreamChecker()
- : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr),
- II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr),
- II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr),
- II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr),
- II_ferror(nullptr), II_fileno(nullptr) {}
-
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
private:
- void Fopen(CheckerContext &C, const CallExpr *CE) const;
- void Tmpfile(CheckerContext &C, const CallExpr *CE) const;
- void Fclose(CheckerContext &C, const CallExpr *CE) const;
- void Fread(CheckerContext &C, const CallExpr *CE) const;
- void Fwrite(CheckerContext &C, const CallExpr *CE) const;
- void Fseek(CheckerContext &C, const CallExpr *CE) const;
- void Ftell(CheckerContext &C, const CallExpr *CE) const;
- void Rewind(CheckerContext &C, const CallExpr *CE) const;
- void Fgetpos(CheckerContext &C, const CallExpr *CE) const;
- void Fsetpos(CheckerContext &C, const CallExpr *CE) const;
- void Clearerr(CheckerContext &C, const CallExpr *CE) const;
- void Feof(CheckerContext &C, const CallExpr *CE) const;
- void Ferror(CheckerContext &C, const CallExpr *CE) const;
- void Fileno(CheckerContext &C, const CallExpr *CE) const;
-
- void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
-
- ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state,
- CheckerContext &C) const;
- ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state,
- CheckerContext &C) const;
+ using FnCheck = std::function<void(const StreamChecker *, const CallEvent &,
+ CheckerContext &)>;
+
+ CallDescriptionMap<FnCheck> Callbacks = {
+ {{"fopen"}, &StreamChecker::evalFopen},
+ {{"freopen", 3}, &StreamChecker::evalFreopen},
+ {{"tmpfile"}, &StreamChecker::evalFopen},
+ {{"fclose", 1}, &StreamChecker::evalFclose},
+ {{"fread", 4},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)},
+ {{"fwrite", 4},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)},
+ {{"fseek", 3}, &StreamChecker::evalFseek},
+ {{"ftell", 1},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {{"rewind", 1},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {{"fgetpos", 2},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {{"fsetpos", 2},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {{"clearerr", 1},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {{"feof", 1},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {{"ferror", 1},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {{"fileno", 1},
+ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ };
+
+ void evalFopen(const CallEvent &Call, CheckerContext &C) const;
+ void evalFreopen(const CallEvent &Call, CheckerContext &C) const;
+ void evalFclose(const CallEvent &Call, CheckerContext &C) const;
+ void evalFseek(const CallEvent &Call, CheckerContext &C) const;
+
+ void checkArgNullStream(const CallEvent &Call, CheckerContext &C,
+ unsigned ArgI) const;
+ bool checkNullStream(SVal SV, CheckerContext &C,
+ ProgramStateRef &State) const;
+ void checkFseekWhence(SVal SV, CheckerContext &C,
+ ProgramStateRef &State) const;
+ bool checkDoubleClose(const CallEvent &Call, CheckerContext &C,
+ ProgramStateRef &State) const;
};
} // end anonymous namespace
@@ -109,115 +115,36 @@ bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
if (!FD || FD->getKind() != Decl::Function)
return false;
- const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
- if (!CE)
+ // Recognize "global C functions" with only integral or pointer arguments
+ // (and matching name) as stream functions.
+ if (!Call.isGlobalCFunction())
return false;
-
- ASTContext &Ctx = C.getASTContext();
- if (!II_fopen)
- II_fopen = &Ctx.Idents.get("fopen");
- if (!II_tmpfile)
- II_tmpfile = &Ctx.Idents.get("tmpfile");
- if (!II_fclose)
- II_fclose = &Ctx.Idents.get("fclose");
- if (!II_fread)
- II_fread = &Ctx.Idents.get("fread");
- if (!II_fwrite)
- II_fwrite = &Ctx.Idents.get("fwrite");
- if (!II_fseek)
- II_fseek = &Ctx.Idents.get("fseek");
- if (!II_ftell)
- II_ftell = &Ctx.Idents.get("ftell");
- if (!II_rewind)
- II_rewind = &Ctx.Idents.get("rewind");
- if (!II_fgetpos)
- II_fgetpos = &Ctx.Idents.get("fgetpos");
- if (!II_fsetpos)
- II_fsetpos = &Ctx.Idents.get("fsetpos");
- if (!II_clearerr)
- II_clearerr = &Ctx.Idents.get("clearerr");
- if (!II_feof)
- II_feof = &Ctx.Idents.get("feof");
- if (!II_ferror)
- II_ferror = &Ctx.Idents.get("ferror");
- if (!II_fileno)
- II_fileno = &Ctx.Idents.get("fileno");
-
- if (FD->getIdentifier() == II_fopen) {
- Fopen(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_tmpfile) {
- Tmpfile(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_fclose) {
- Fclose(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_fread) {
- Fread(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_fwrite) {
- Fwrite(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_fseek) {
- Fseek(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_ftell) {
- Ftell(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_rewind) {
- Rewind(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_fgetpos) {
- Fgetpos(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_fsetpos) {
- Fsetpos(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_clearerr) {
- Clearerr(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_feof) {
- Feof(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_ferror) {
- Ferror(C, CE);
- return true;
- }
- if (FD->getIdentifier() == II_fileno) {
- Fileno(C, CE);
- return true;
+ for (auto P : Call.parameters()) {
+ QualType T = P->getType();
+ if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
+ return false;
}
- return false;
-}
+ const FnCheck *Callback = Callbacks.lookup(Call);
+ if (!Callback)
+ return false;
-void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const {
- OpenFileAux(C, CE);
-}
+ (*Callback)(this, Call, C);
-void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const {
- OpenFileAux(C, CE);
+ return C.isDifferent();
}
-void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
+void StreamChecker::evalFopen(const CallEvent &Call, CheckerContext &C) const {
ProgramStateRef state = C.getState();
SValBuilder &svalBuilder = C.getSValBuilder();
const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
- DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
- C.blockCount())
- .castAs<DefinedSVal>();
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ DefinedSVal RetVal =
+ svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
+ .castAs<DefinedSVal>();
state = state->BindExpr(CE, C.getLocationContext(), RetVal);
ConstraintManager &CM = C.getConstraintManager();
@@ -226,145 +153,153 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef stateNotNull, stateNull;
std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
- if (SymbolRef Sym = RetVal.getAsSymbol()) {
- // if RetVal is not NULL, set the symbol's state to Opened.
- stateNotNull =
- stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE));
- stateNull =
- stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE));
-
- C.addTransition(stateNotNull);
- C.addTransition(stateNull);
- }
-}
+ SymbolRef Sym = RetVal.getAsSymbol();
+ assert(Sym && "RetVal must be a symbol here.");
+ stateNotNull = stateNotNull->set<StreamMap>(Sym, StreamState::getOpened());
+ stateNull = stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed());
-void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C);
- if (state)
- C.addTransition(state);
+ C.addTransition(stateNotNull);
+ C.addTransition(stateNull);
}
-void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
- return;
-}
+void StreamChecker::evalFreopen(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
-void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
return;
-}
-void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!(state = CheckNullStream(C.getSVal(CE->getArg(0)), state, C)))
+ Optional<DefinedSVal> StreamVal = Call.getArgSVal(2).getAs<DefinedSVal>();
+ if (!StreamVal)
return;
- // Check the legality of the 'whence' argument of 'fseek'.
- SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
- Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>();
-
- if (!CI)
+ // Do not allow NULL as passed stream pointer.
+ // This is not specified in the man page but may crash on some system.
+ checkNullStream(*StreamVal, C, State);
+ // Check if error was generated.
+ if (C.isDifferent())
return;
- int64_t x = CI->getValue().getSExtValue();
- if (x >= 0 && x <= 2)
+ SymbolRef StreamSym = StreamVal->getAsSymbol();
+ // Do not care about special values for stream ("(FILE *)0x12345"?).
+ if (!StreamSym)
return;
- if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
- if (!BT_illegalwhence)
- BT_illegalwhence.reset(
- new BuiltinBug(this, "Illegal whence argument",
- "The whence argument to fseek() should be "
- "SEEK_SET, SEEK_END, or SEEK_CUR."));
- C.emitReport(std::make_unique<PathSensitiveBugReport>(
- *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
- }
+ // Generate state for non-failed case.
+ // Return value is the passed stream pointer.
+ // According to the documentations, the stream is closed first
+ // but any close error is ignored. The state changes to (or remains) opened.
+ ProgramStateRef StateRetNotNull =
+ State->BindExpr(CE, C.getLocationContext(), *StreamVal);
+ // Generate state for NULL return value.
+ // Stream switches to OpenFailed state.
+ ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(),
+ C.getSValBuilder().makeNull());
+
+ StateRetNotNull =
+ StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened());
+ StateRetNull =
+ StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed());
+
+ C.addTransition(StateRetNotNull);
+ C.addTransition(StateRetNull);
}
-void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
- return;
+void StreamChecker::evalFclose(const CallEvent &Call, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (checkDoubleClose(Call, C, State))
+ C.addTransition(State);
}
-void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+void StreamChecker::evalFseek(const CallEvent &Call, CheckerContext &C) const {
+ const Expr *AE2 = Call.getArgExpr(2);
+ if (!AE2)
return;
-}
-void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
- return;
-}
+ ProgramStateRef State = C.getState();
-void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+ bool StateChanged = checkNullStream(Call.getArgSVal(0), C, State);
+ // Check if error was generated.
+ if (C.isDifferent())
return;
-}
-void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
- return;
-}
+ // Check the legality of the 'whence' argument of 'fseek'.
+ checkFseekWhence(State->getSVal(AE2, C.getLocationContext()), C, State);
-void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
- return;
-}
+ if (!C.isDifferent() && StateChanged)
+ C.addTransition(State);
-void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
- return;
+ return;
}
-void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
- ProgramStateRef state = C.getState();
- if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
- return;
+void StreamChecker::checkArgNullStream(const CallEvent &Call, CheckerContext &C,
+ unsigned ArgI) const {
+ ProgramStateRef State = C.getState();
+ if (checkNullStream(Call.getArgSVal(ArgI), C, State))
+ C.addTransition(State);
}
-ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
- CheckerContext &C) const {
+bool StreamChecker::checkNullStream(SVal SV, CheckerContext &C,
+ ProgramStateRef &State) const {
Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>();
if (!DV)
- return nullptr;
+ return false;
ConstraintManager &CM = C.getConstraintManager();
- ProgramStateRef stateNotNull, stateNull;
- std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
+ ProgramStateRef StateNotNull, StateNull;
+ std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *DV);
- if (!stateNotNull && stateNull) {
- if (ExplodedNode *N = C.generateErrorNode(stateNull)) {
+ if (!StateNotNull && StateNull) {
+ if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
if (!BT_nullfp)
BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
"Stream pointer might be NULL."));
C.emitReport(std::make_unique<PathSensitiveBugReport>(
*BT_nullfp, BT_nullfp->getDescription(), N));
}
- return nullptr;
+ return false;
}
- return stateNotNull;
+
+ if (StateNotNull) {
+ State = StateNotNull;
+ return true;
+ }
+
+ return false;
}
-ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
- ProgramStateRef state,
- CheckerContext &C) const {
- SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
+void StreamChecker::checkFseekWhence(SVal SV, CheckerContext &C,
+ ProgramStateRef &State) const {
+ Optional<nonloc::ConcreteInt> CI = SV.getAs<nonloc::ConcreteInt>();
+ if (!CI)
+ return;
+
+ int64_t X = CI->getValue().getSExtValue();
+ if (X >= 0 && X <= 2)
+ return;
+
+ if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
+ if (!BT_illegalwhence)
+ BT_illegalwhence.reset(
+ new BuiltinBug(this, "Illegal whence argument",
+ "The whence argument to fseek() should be "
+ "SEEK_SET, SEEK_END, or SEEK_CUR."));
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
+ *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
+ }
+}
+
+bool StreamChecker::checkDoubleClose(const CallEvent &Call, CheckerContext &C,
+ ProgramStateRef &State) const {
+ SymbolRef Sym = Call.getArgSVal(0).getAsSymbol();
if (!Sym)
- return state;
+ return false;
- const StreamState *SS = state->get<StreamMap>(Sym);
+ const StreamState *SS = State->get<StreamMap>(Sym);
// If the file stream is not tracked, return.
if (!SS)
- return state;
+ return false;
// Check: Double close a File Descriptor could cause undefined behaviour.
// Conforming to man-pages
@@ -378,19 +313,21 @@ ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
C.emitReport(std::make_unique<PathSensitiveBugReport>(
*BT_doubleclose, BT_doubleclose->getDescription(), N));
}
- return nullptr;
+ return false;
}
// Close the File Descriptor.
- return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
+ State = State->set<StreamMap>(Sym, StreamState::getClosed());
+
+ return true;
}
void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
CheckerContext &C) const {
- ProgramStateRef state = C.getState();
+ ProgramStateRef State = C.getState();
// TODO: Clean up the state.
- const StreamMapTy &Map = state->get<StreamMap>();
+ const StreamMapTy &Map = State->get<StreamMap>();
for (const auto &I: Map) {
SymbolRef Sym = I.first;
const StreamState &SS = I.second;
@@ -399,7 +336,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
ExplodedNode *N = C.generateErrorNode();
if (!N)
- return;
+ continue;
if (!BT_ResourceLeak)
BT_ResourceLeak.reset(
diff --git a/clang/lib/StaticAnalyzer/Checkers/Taint.cpp b/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
index 574d4ed1e600..5b46ffb656cf 100644
--- a/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
@@ -37,9 +37,7 @@ void taint::printTaint(ProgramStateRef State, raw_ostream &Out, const char *NL,
Out << I.first << " : " << I.second << NL;
}
-void dumpTaint(ProgramStateRef State) {
- printTaint(State, llvm::errs());
-}
+void dumpTaint(ProgramStateRef State) { printTaint(State, llvm::errs()); }
ProgramStateRef taint::addTaint(ProgramStateRef State, const Stmt *S,
const LocationContext *LCtx,
@@ -64,8 +62,8 @@ ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V,
// region of the parent region.
if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) {
if (Optional<SVal> binding =
- State->getStateManager().getStoreManager()
- .getDefaultBinding(*LCV)) {
+ State->getStateManager().getStoreManager().getDefaultBinding(
+ *LCV)) {
if (SymbolRef Sym = binding->getAsSymbol())
return addPartialTaint(State, Sym, LCV->getRegion(), Kind);
}
@@ -94,6 +92,32 @@ ProgramStateRef taint::addTaint(ProgramStateRef State, SymbolRef Sym,
return NewState;
}
+ProgramStateRef taint::removeTaint(ProgramStateRef State, SVal V) {
+ SymbolRef Sym = V.getAsSymbol();
+ if (Sym)
+ return removeTaint(State, Sym);
+
+ const MemRegion *R = V.getAsRegion();
+ return removeTaint(State, R);
+}
+
+ProgramStateRef taint::removeTaint(ProgramStateRef State, const MemRegion *R) {
+ if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R))
+ return removeTaint(State, SR->getSymbol());
+ return State;
+}
+
+ProgramStateRef taint::removeTaint(ProgramStateRef State, SymbolRef Sym) {
+ // If this is a symbol cast, remove the cast before adding the taint. Taint
+ // is cast agnostic.
+ while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym))
+ Sym = SC->getOperand();
+
+ ProgramStateRef NewState = State->remove<TaintMap>(Sym);
+ assert(NewState);
+ return NewState;
+}
+
ProgramStateRef taint::addPartialTaint(ProgramStateRef State,
SymbolRef ParentSym,
const SubRegion *SubRegion,
@@ -157,7 +181,8 @@ bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) {
// Traverse all the symbols this symbol depends on to see if any are tainted.
for (SymExpr::symbol_iterator SI = Sym->symbol_begin(),
- SE = Sym->symbol_end(); SI != SE; ++SI) {
+ SE = Sym->symbol_end();
+ SI != SE; ++SI) {
if (!isa<SymbolData>(*SI))
continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/Taint.h b/clang/lib/StaticAnalyzer/Checkers/Taint.h
index 8940916c1933..659a3c898d56 100644
--- a/clang/lib/StaticAnalyzer/Checkers/Taint.h
+++ b/clang/lib/StaticAnalyzer/Checkers/Taint.h
@@ -27,34 +27,39 @@ using TaintTagType = unsigned;
static constexpr TaintTagType TaintTagGeneric = 0;
/// Create a new state in which the value of the statement is marked as tainted.
-LLVM_NODISCARD ProgramStateRef
-addTaint(ProgramStateRef State, const Stmt *S, const LocationContext *LCtx,
- TaintTagType Kind = TaintTagGeneric);
+LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, const Stmt *S,
+ const LocationContext *LCtx,
+ TaintTagType Kind = TaintTagGeneric);
/// Create a new state in which the value is marked as tainted.
-LLVM_NODISCARD ProgramStateRef
-addTaint(ProgramStateRef State, SVal V,
- TaintTagType Kind = TaintTagGeneric);
+LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, SVal V,
+ TaintTagType Kind = TaintTagGeneric);
/// Create a new state in which the symbol is marked as tainted.
-LLVM_NODISCARD ProgramStateRef
-addTaint(ProgramStateRef State, SymbolRef Sym,
- TaintTagType Kind = TaintTagGeneric);
+LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, SymbolRef Sym,
+ TaintTagType Kind = TaintTagGeneric);
/// Create a new state in which the pointer represented by the region
/// is marked as tainted.
-LLVM_NODISCARD ProgramStateRef
-addTaint(ProgramStateRef State, const MemRegion *R,
- TaintTagType Kind = TaintTagGeneric);
+LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State,
+ const MemRegion *R,
+ TaintTagType Kind = TaintTagGeneric);
+
+LLVM_NODISCARD ProgramStateRef removeTaint(ProgramStateRef State, SVal V);
+
+LLVM_NODISCARD ProgramStateRef removeTaint(ProgramStateRef State,
+ const MemRegion *R);
+
+LLVM_NODISCARD ProgramStateRef removeTaint(ProgramStateRef State,
+ SymbolRef Sym);
/// Create a new state in a which a sub-region of a given symbol is tainted.
/// This might be necessary when referring to regions that can not have an
/// individual symbol, e.g. if they are represented by the default binding of
/// a LazyCompoundVal.
-LLVM_NODISCARD ProgramStateRef
-addPartialTaint(ProgramStateRef State,
- SymbolRef ParentSym, const SubRegion *SubRegion,
- TaintTagType Kind = TaintTagGeneric);
+LLVM_NODISCARD ProgramStateRef addPartialTaint(
+ ProgramStateRef State, SymbolRef ParentSym, const SubRegion *SubRegion,
+ TaintTagType Kind = TaintTagGeneric);
/// Check if the statement has a tainted value in the given state.
bool isTainted(ProgramStateRef State, const Stmt *S,
@@ -99,4 +104,3 @@ public:
} // namespace clang
#endif
-
diff --git a/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index 12cee5f8d4f7..fd93fc33115f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -11,8 +11,9 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
diff --git a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index 73e1a0d0000f..fdd03c75920d 100644
--- a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -43,6 +43,9 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx,
CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr),
options(Options) {
AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
+ AnaCtxMgr.getCFGBuildOptions().OmitImplicitValueInitializers = true;
+ AnaCtxMgr.getCFGBuildOptions().AddCXXDefaultInitExprInAggregates =
+ Options.ShouldIncludeDefaultInitForAggregates;
}
AnalysisManager::~AnalysisManager() {
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 7ba93b858baf..0525b5c41e34 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -505,7 +505,7 @@ NoStoreFuncVisitor::findRegionOfInterestInRecord(
// Recursively examine the base classes.
// Note that following base classes does not increase the recursion depth.
if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
- for (const auto II : RDX->bases())
+ for (const auto &II : RDX->bases())
if (const RecordDecl *RRD = II.getType()->getAsRecordDecl())
if (Optional<RegionVector> Out =
findRegionOfInterestInRecord(RRD, State, R, Vec, depth))
@@ -1606,9 +1606,6 @@ SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N)
AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
if (!Options.ShouldSuppressInlinedDefensiveChecks)
IsSatisfied = true;
-
- assert(N->getState()->isNull(V).isConstrainedTrue() &&
- "The visitor only tracks the cases where V is constrained to 0");
}
void SuppressInlineDefensiveChecksVisitor::Profile(
@@ -1639,13 +1636,12 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
// Check if in the previous state it was feasible for this value
// to *not* be null.
- if (!Pred->getState()->isNull(V).isConstrainedTrue()) {
+ if (!Pred->getState()->isNull(V).isConstrainedTrue() &&
+ Succ->getState()->isNull(V).isConstrainedTrue()) {
IsSatisfied = true;
- assert(Succ->getState()->isNull(V).isConstrainedTrue());
-
// Check if this is inlined defensive checks.
- const LocationContext *CurLC =Succ->getLocationContext();
+ const LocationContext *CurLC = Succ->getLocationContext();
const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext();
if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) {
BR.markInvalid("Suppress IDC", CurLC);
@@ -2012,11 +2008,16 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
// Add visitor, which will suppress inline defensive checks.
if (auto DV = V.getAs<DefinedSVal>())
- if (!DV->isZeroConstant() && LVState->isNull(*DV).isConstrainedTrue() &&
- EnableNullFPSuppression)
+ if (!DV->isZeroConstant() && EnableNullFPSuppression) {
+ // Note that LVNode may be too late (i.e., too far from the InputNode)
+ // because the lvalue may have been computed before the inlined call
+ // was evaluated. InputNode may as well be too early here, because
+ // the symbol is already dead; this, however, is fine because we can
+ // still find the node in which it collapsed to null previously.
report.addVisitor(
- std::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV,
- LVNode));
+ std::make_unique<SuppressInlineDefensiveChecksVisitor>(
+ *DV, InputNode));
+ }
if (auto KV = V.getAs<KnownSVal>())
report.addVisitor(std::make_unique<FindLastStoreBRVisitor>(
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 5f04a59ba055..168d6fe6ec48 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
@@ -29,20 +30,20 @@
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Analysis/ProgramPoint.h"
-#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
+#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
@@ -519,7 +520,7 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
// TODO: Support allocator calls.
if (Call.getKind() != CE_CXXAllocator)
- if (Call.isArgumentConstructedDirectly(Idx))
+ if (Call.isArgumentConstructedDirectly(Call.getASTArgumentIndex(Idx)))
continue;
// TODO: Allocators should receive the correct size and possibly alignment,
@@ -1080,7 +1081,7 @@ ObjCMessageKind ObjCMethodCall::getMessageKind() const {
const ObjCPropertyDecl *ObjCMethodCall::getAccessedProperty() const {
// Look for properties accessed with property syntax (foo.bar = ...)
- if ( getMessageKind() == OCM_PropertyAccess) {
+ if (getMessageKind() == OCM_PropertyAccess) {
const PseudoObjectExpr *POE = getContainingPseudoObjectExpr();
assert(POE && "Property access without PseudoObjectExpr?");
@@ -1309,6 +1310,8 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
}
const ObjCMethodDecl *MD = Val.getValue();
+ if (MD && !MD->hasBody())
+ MD = MD->getCanonicalDecl();
if (CanBeSubClassed)
return RuntimeDefinition(MD, Receiver);
else
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
index f676bd895283..a9361837cf68 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -91,7 +91,7 @@ void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr,
}
assert(checkers);
- for (const auto checker : *checkers)
+ for (const auto &checker : *checkers)
checker(D, mgr, BR);
}
@@ -99,7 +99,7 @@ void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) {
assert(D && D->hasBody());
- for (const auto BodyChecker : BodyCheckers)
+ for (const auto &BodyChecker : BodyCheckers)
BodyChecker(D, mgr, BR);
}
@@ -402,7 +402,7 @@ void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
BugReporter &BR,
ExprEngine &Eng) {
- for (const auto EndAnalysisChecker : EndAnalysisCheckers)
+ for (const auto &EndAnalysisChecker : EndAnalysisCheckers)
EndAnalysisChecker(G, BR, Eng);
}
@@ -455,7 +455,7 @@ void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC,
// creates a successor for Pred, we do not need to generate an
// autotransition for it.
NodeBuilder Bldr(Pred, Dst, BC);
- for (const auto checkFn : EndFunctionCheckers) {
+ for (const auto &checkFn : EndFunctionCheckers) {
const ProgramPoint &L =
FunctionExitPoint(RS, Pred->getLocationContext(), checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
@@ -542,7 +542,7 @@ void CheckerManager::runCheckersForNewAllocator(
/// Run checkers for live symbols.
void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state,
SymbolReaper &SymReaper) {
- for (const auto LiveSymbolsChecker : LiveSymbolsCheckers)
+ for (const auto &LiveSymbolsChecker : LiveSymbolsCheckers)
LiveSymbolsChecker(state, SymReaper);
}
@@ -599,7 +599,7 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
ArrayRef<const MemRegion *> Regions,
const LocationContext *LCtx,
const CallEvent *Call) {
- for (const auto RegionChangesChecker : RegionChangesCheckers) {
+ for (const auto &RegionChangesChecker : RegionChangesCheckers) {
// If any checker declares the state infeasible (or if it starts that way),
// bail out.
if (!state)
@@ -621,7 +621,7 @@ CheckerManager::runCheckersForPointerEscape(ProgramStateRef State,
(Kind != PSK_DirectEscapeOnCall &&
Kind != PSK_IndirectEscapeOnCall)) &&
"Call must not be NULL when escaping on call");
- for (const auto PointerEscapeChecker : PointerEscapeCheckers) {
+ for (const auto &PointerEscapeChecker : PointerEscapeCheckers) {
// If any checker declares the state infeasible (or if it starts that
// way), bail out.
if (!State)
@@ -635,7 +635,7 @@ CheckerManager::runCheckersForPointerEscape(ProgramStateRef State,
ProgramStateRef
CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
SVal Cond, bool Assumption) {
- for (const auto EvalAssumeChecker : EvalAssumeCheckers) {
+ for (const auto &EvalAssumeChecker : EvalAssumeCheckers) {
// If any checker declares the state infeasible (or if it starts that way),
// bail out.
if (!state)
@@ -658,7 +658,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
// Check if any of the EvalCall callbacks can evaluate the call.
- for (const auto EvalCallChecker : EvalCallCheckers) {
+ for (const auto &EvalCallChecker : EvalCallCheckers) {
// TODO: Support the situation when the call doesn't correspond
// to any Expr.
ProgramPoint L = ProgramPoint::getProgramPoint(
@@ -697,7 +697,7 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit(
const TranslationUnitDecl *TU,
AnalysisManager &mgr,
BugReporter &BR) {
- for (const auto EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers)
+ for (const auto &EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers)
EndOfTranslationUnitChecker(TU, mgr, BR);
}
@@ -904,6 +904,6 @@ CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) {
}
CheckerManager::~CheckerManager() {
- for (const auto CheckerDtor : CheckerDtors)
+ for (const auto &CheckerDtor : CheckerDtors)
CheckerDtor();
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index e92e95354f5f..f917a4c8637b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -728,7 +728,8 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
// Create a state in which dead bindings are removed from the environment
// and the store. TODO: The function should just return new env and store,
// not a new state.
- CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
+ CleanedState = StateMgr.removeDeadBindingsFromEnvironmentAndStore(
+ CleanedState, SFC, SymReaper);
// Process any special transfer function for dead symbols.
// A tag to track convenience transitions, which can be removed at cleanup.
@@ -1171,13 +1172,16 @@ void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
}
}
-ProgramStateRef ExprEngine::escapeValue(ProgramStateRef State, SVal V,
- PointerEscapeKind K) const {
+ProgramStateRef ExprEngine::escapeValues(ProgramStateRef State,
+ ArrayRef<SVal> Vs,
+ PointerEscapeKind K,
+ const CallEvent *Call) const {
class CollectReachableSymbolsCallback final : public SymbolVisitor {
- InvalidatedSymbols Symbols;
+ InvalidatedSymbols &Symbols;
public:
- explicit CollectReachableSymbolsCallback(ProgramStateRef) {}
+ explicit CollectReachableSymbolsCallback(InvalidatedSymbols &Symbols)
+ : Symbols(Symbols) {}
const InvalidatedSymbols &getSymbols() const { return Symbols; }
@@ -1186,11 +1190,13 @@ ProgramStateRef ExprEngine::escapeValue(ProgramStateRef State, SVal V,
return true;
}
};
+ InvalidatedSymbols Symbols;
+ CollectReachableSymbolsCallback CallBack(Symbols);
+ for (SVal V : Vs)
+ State->scanReachableSymbols(V, CallBack);
- const CollectReachableSymbolsCallback &Scanner =
- State->scanReachableSymbols<CollectReachableSymbolsCallback>(V);
return getCheckerManager().runCheckersForPointerEscape(
- State, Scanner.getSymbols(), /*CallEvent*/ nullptr, K, nullptr);
+ State, CallBack.getSymbols(), Call, K, nullptr);
}
void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
@@ -1245,6 +1251,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPParallelForDirectiveClass:
case Stmt::OMPParallelForSimdDirectiveClass:
case Stmt::OMPParallelSectionsDirectiveClass:
+ case Stmt::OMPParallelMasterDirectiveClass:
case Stmt::OMPTaskDirectiveClass:
case Stmt::OMPTaskyieldDirectiveClass:
case Stmt::OMPBarrierDirectiveClass:
@@ -1268,6 +1275,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPMasterTaskLoopDirectiveClass:
case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
case Stmt::OMPParallelMasterTaskLoopDirectiveClass:
+ case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass:
case Stmt::OMPDistributeDirectiveClass:
case Stmt::OMPDistributeParallelForDirectiveClass:
case Stmt::OMPDistributeParallelForSimdDirectiveClass:
@@ -1313,6 +1321,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::WhileStmtClass:
case Expr::MSDependentExistsStmtClass:
llvm_unreachable("Stmt should not be in analyzer evaluation loop");
+ case Stmt::ImplicitValueInitExprClass:
+ // These nodes are shared in the CFG and would case caching out.
+ // Moreover, no additional evaluation required for them, the
+ // analyzer can reconstruct these values from the AST.
+ llvm_unreachable("Should be pruned from CFG");
case Stmt::ObjCSubscriptRefExprClass:
case Stmt::ObjCPropertyRefExprClass:
@@ -1383,7 +1396,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::IntegerLiteralClass:
case Stmt::FixedPointLiteralClass:
case Stmt::CharacterLiteralClass:
- case Stmt::ImplicitValueInitExprClass:
case Stmt::CXXScalarValueInitExprClass:
case Stmt::CXXBoolLiteralExprClass:
case Stmt::ObjCBoolLiteralExprClass:
@@ -1426,7 +1438,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
bool IsTemporary = false;
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
- ArgE = MTE->GetTemporaryExpr();
+ ArgE = MTE->getSubExpr();
IsTemporary = true;
}
@@ -1481,7 +1493,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
for (auto Child : Ex->children()) {
assert(Child);
SVal Val = State->getSVal(Child, LCtx);
- State = escapeValue(State, Val, PSK_EscapeOther);
+ State = escapeValues(State, Val, PSK_EscapeOther);
}
Bldr2.generateNode(S, N, State);
@@ -2682,33 +2694,52 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,
// destructor. We won't see the destructor during analysis, but it's there.
// (4) We are binding to a MemRegion with stack storage that the store
// does not understand.
+ProgramStateRef ExprEngine::processPointerEscapedOnBind(
+ ProgramStateRef State, ArrayRef<std::pair<SVal, SVal>> LocAndVals,
+ const LocationContext *LCtx, PointerEscapeKind Kind,
+ const CallEvent *Call) {
+ SmallVector<SVal, 8> Escaped;
+ for (const std::pair<SVal, SVal> &LocAndVal : LocAndVals) {
+ // Cases (1) and (2).
+ const MemRegion *MR = LocAndVal.first.getAsRegion();
+ if (!MR || !MR->hasStackStorage()) {
+ Escaped.push_back(LocAndVal.second);
+ continue;
+ }
+
+ // Case (3).
+ if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion()))
+ if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame())
+ if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl())
+ if (!RD->hasTrivialDestructor()) {
+ Escaped.push_back(LocAndVal.second);
+ continue;
+ }
+
+ // Case (4): in order to test that, generate a new state with the binding
+ // added. If it is the same state, then it escapes (since the store cannot
+ // represent the binding).
+ // Do this only if we know that the store is not supposed to generate the
+ // same state.
+ SVal StoredVal = State->getSVal(MR);
+ if (StoredVal != LocAndVal.second)
+ if (State ==
+ (State->bindLoc(loc::MemRegionVal(MR), LocAndVal.second, LCtx)))
+ Escaped.push_back(LocAndVal.second);
+ }
+
+ if (Escaped.empty())
+ return State;
+
+ return escapeValues(State, Escaped, Kind, Call);
+}
+
ProgramStateRef
ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc,
SVal Val, const LocationContext *LCtx) {
-
- // Cases (1) and (2).
- const MemRegion *MR = Loc.getAsRegion();
- if (!MR || !MR->hasStackStorage())
- return escapeValue(State, Val, PSK_EscapeOnBind);
-
- // Case (3).
- if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion()))
- if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame())
- if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl())
- if (!RD->hasTrivialDestructor())
- return escapeValue(State, Val, PSK_EscapeOnBind);
-
- // Case (4): in order to test that, generate a new state with the binding
- // added. If it is the same state, then it escapes (since the store cannot
- // represent the binding).
- // Do this only if we know that the store is not supposed to generate the
- // same state.
- SVal StoredVal = State->getSVal(MR);
- if (StoredVal != Val)
- if (State == (State->bindLoc(loc::MemRegionVal(MR), Val, LCtx)))
- return escapeValue(State, Val, PSK_EscapeOnBind);
-
- return State;
+ std::pair<SVal, SVal> LocAndVal(Loc, Val);
+ return processPointerEscapedOnBind(State, LocAndVal, LCtx, PSK_EscapeOnBind,
+ nullptr);
}
ProgramStateRef
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 02a398c77ac8..b17f26aa9c53 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -102,8 +102,8 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
state = state->BindExpr(B, LCtx, Result);
} else {
// If we cannot evaluate the operation escape the operands.
- state = escapeValue(state, LeftV, PSK_EscapeOther);
- state = escapeValue(state, RightV, PSK_EscapeOther);
+ state = escapeValues(state, LeftV, PSK_EscapeOther);
+ state = escapeValues(state, RightV, PSK_EscapeOther);
}
Bldr.generateNode(B, *it, state);
@@ -275,7 +275,7 @@ ProgramStateRef ExprEngine::handleLValueBitCast(
V = evalMinus(V);
state = state->BindExpr(CastE, LCtx, V);
if (V.isUnknown() && !OrigV.isUnknown()) {
- state = escapeValue(state, OrigV, PSK_EscapeOther);
+ state = escapeValues(state, OrigV, PSK_EscapeOther);
}
Bldr.generateNode(CastE, Pred, state);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 058be985540d..b816aab7c18f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -27,7 +27,7 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
- const Expr *tempExpr = ME->GetTemporaryExpr()->IgnoreParens();
+ const Expr *tempExpr = ME->getSubExpr()->IgnoreParens();
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 345d4d817dea..01a371e664b2 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/Decl.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "PrettyStackTraceLocationContext.h"
#include "clang/AST/CXXInheritance.h"
@@ -592,9 +593,45 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
for (auto I : dstCallEvaluated)
finishArgumentConstruction(dstArgumentCleanup, I, Call);
- // Finally, run any post-call checks.
- getCheckerManager().runCheckersForPostCall(Dst, dstArgumentCleanup,
+ ExplodedNodeSet dstPostCall;
+ getCheckerManager().runCheckersForPostCall(dstPostCall, dstArgumentCleanup,
Call, *this);
+
+ // Escaping symbols conjured during invalidating the regions above.
+ // Note that, for inlined calls the nodes were put back into the worklist,
+ // so we can assume that every node belongs to a conservative call at this
+ // point.
+
+ // Run pointerEscape callback with the newly conjured symbols.
+ SmallVector<std::pair<SVal, SVal>, 8> Escaped;
+ for (auto I : dstPostCall) {
+ NodeBuilder B(I, Dst, *currBldrCtx);
+ ProgramStateRef State = I->getState();
+ Escaped.clear();
+ {
+ unsigned Arg = -1;
+ for (const ParmVarDecl *PVD : Call.parameters()) {
+ ++Arg;
+ QualType ParamTy = PVD->getType();
+ if (ParamTy.isNull() ||
+ (!ParamTy->isPointerType() && !ParamTy->isReferenceType()))
+ continue;
+ QualType Pointee = ParamTy->getPointeeType();
+ if (Pointee.isConstQualified() || Pointee->isVoidType())
+ continue;
+ if (const MemRegion *MR = Call.getArgSVal(Arg).getAsRegion())
+ Escaped.emplace_back(loc::MemRegionVal(MR), State->getSVal(MR, Pointee));
+ }
+ }
+
+ State = processPointerEscapedOnBind(State, Escaped, I->getLocationContext(),
+ PSK_EscapeOutParameters, &Call);
+
+ if (State == I->getState())
+ Dst.insert(I);
+ else
+ B.generateNode(I->getLocation(), State, I);
+ }
}
ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
@@ -670,8 +707,7 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
// Conservatively evaluate call by invalidating regions and binding
// a conjured return value.
void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
- ExplodedNode *Pred,
- ProgramStateRef State) {
+ ExplodedNode *Pred, ProgramStateRef State) {
State = Call.invalidateRegions(currBldrCtx->blockCount(), State);
State = bindReturnValue(Call, Pred->getLocationContext(), State);
diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index f50d82de3b28..14006f79fd0f 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -91,10 +91,9 @@ ProgramStateManager::~ProgramStateManager() {
I->second.second(I->second.first);
}
-ProgramStateRef
-ProgramStateManager::removeDeadBindings(ProgramStateRef state,
- const StackFrameContext *LCtx,
- SymbolReaper& SymReaper) {
+ProgramStateRef ProgramStateManager::removeDeadBindingsFromEnvironmentAndStore(
+ ProgramStateRef state, const StackFrameContext *LCtx,
+ SymbolReaper &SymReaper) {
// This code essentially performs a "mark-and-sweep" of the VariableBindings.
// The roots are any Block-level exprs and Decls that our liveness algorithm
@@ -112,8 +111,7 @@ ProgramStateManager::removeDeadBindings(ProgramStateRef state,
NewState.setStore(newStore);
SymReaper.setReapedStore(newStore);
- ProgramStateRef Result = getPersistentState(NewState);
- return ConstraintMgr->removeDeadBindings(Result, SymReaper);
+ return getPersistentState(NewState);
}
ProgramStateRef ProgramState::bindLoc(Loc LV,
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 5d2ef59e2d66..4797f564a837 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1951,7 +1951,8 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
if (hasSymbolicIndex)
return UnknownVal();
- if (!hasPartialLazyBinding)
+ // Additionally allow introspection of a block's internal layout.
+ if (!hasPartialLazyBinding && !isa<BlockDataRegion>(R->getBaseRegion()))
return UndefinedVal();
}
diff --git a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
index 190ab7e21dbc..12332aaf936f 100644
--- a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Path.h"
@@ -27,10 +28,12 @@ using namespace ento;
namespace {
class SarifDiagnostics : public PathDiagnosticConsumer {
std::string OutputFile;
+ const LangOptions &LO;
public:
- SarifDiagnostics(AnalyzerOptions &, const std::string &Output)
- : OutputFile(Output) {}
+ SarifDiagnostics(AnalyzerOptions &, const std::string &Output,
+ const LangOptions &LO)
+ : OutputFile(Output), LO(LO) {}
~SarifDiagnostics() override = default;
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
@@ -45,9 +48,9 @@ public:
void ento::createSarifDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &Output, const Preprocessor &,
+ const std::string &Output, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &) {
- C.push_back(new SarifDiagnostics(AnalyzerOpts, Output));
+ C.push_back(new SarifDiagnostics(AnalyzerOpts, Output, PP.getLangOpts()));
}
static StringRef getFileName(const FileEntry &FE) {
@@ -142,26 +145,56 @@ static json::Object createArtifactLocation(const FileEntry &FE,
return json::Object{{"uri", FileURI}, {"index", Index}};
}
-static json::Object createTextRegion(SourceRange R, const SourceManager &SM) {
+static unsigned int adjustColumnPos(const SourceManager &SM, SourceLocation Loc,
+ unsigned int TokenLen = 0) {
+ assert(!Loc.isInvalid() && "invalid Loc when adjusting column position");
+
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedExpansionLoc(Loc);
+ assert(LocInfo.second > SM.getExpansionColumnNumber(Loc) &&
+ "position in file is before column number?");
+
+ bool InvalidBuffer = false;
+ const MemoryBuffer *Buf = SM.getBuffer(LocInfo.first, &InvalidBuffer);
+ assert(!InvalidBuffer && "got an invalid buffer for the location's file");
+ assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) &&
+ "token extends past end of buffer?");
+
+ // Adjust the offset to be the start of the line, since we'll be counting
+ // Unicode characters from there until our column offset.
+ unsigned int Off = LocInfo.second - (SM.getExpansionColumnNumber(Loc) - 1);
+ unsigned int Ret = 1;
+ while (Off < (LocInfo.second + TokenLen)) {
+ Off += getNumBytesForUTF8(Buf->getBuffer()[Off]);
+ Ret++;
+ }
+
+ return Ret;
+}
+
+static json::Object createTextRegion(const LangOptions &LO, SourceRange R,
+ const SourceManager &SM) {
json::Object Region{
{"startLine", SM.getExpansionLineNumber(R.getBegin())},
- {"startColumn", SM.getExpansionColumnNumber(R.getBegin())},
+ {"startColumn", adjustColumnPos(SM, R.getBegin())},
};
if (R.getBegin() == R.getEnd()) {
- Region["endColumn"] = SM.getExpansionColumnNumber(R.getBegin());
+ Region["endColumn"] = adjustColumnPos(SM, R.getBegin());
} else {
Region["endLine"] = SM.getExpansionLineNumber(R.getEnd());
- Region["endColumn"] = SM.getExpansionColumnNumber(R.getEnd()) + 1;
+ Region["endColumn"] = adjustColumnPos(
+ SM, R.getEnd(),
+ Lexer::MeasureTokenLength(R.getEnd(), SM, LO));
}
return Region;
}
-static json::Object createPhysicalLocation(SourceRange R, const FileEntry &FE,
+static json::Object createPhysicalLocation(const LangOptions &LO,
+ SourceRange R, const FileEntry &FE,
const SourceManager &SMgr,
json::Array &Artifacts) {
return json::Object{
{{"artifactLocation", createArtifactLocation(FE, Artifacts)},
- {"region", createTextRegion(R, SMgr)}}};
+ {"region", createTextRegion(LO, R, SMgr)}}};
}
enum class Importance { Important, Essential, Unimportant };
@@ -213,7 +246,8 @@ static Importance calculateImportance(const PathDiagnosticPiece &Piece) {
return Importance::Unimportant;
}
-static json::Object createThreadFlow(const PathPieces &Pieces,
+static json::Object createThreadFlow(const LangOptions &LO,
+ const PathPieces &Pieces,
json::Array &Artifacts) {
const SourceManager &SMgr = Pieces.front()->getLocation().getManager();
json::Array Locations;
@@ -221,7 +255,7 @@ static json::Object createThreadFlow(const PathPieces &Pieces,
const PathDiagnosticLocation &P = Piece->getLocation();
Locations.push_back(createThreadFlowLocation(
createLocation(createPhysicalLocation(
- P.asRange(),
+ LO, P.asRange(),
*P.asLocation().getExpansionLoc().getFileEntry(),
SMgr, Artifacts),
Piece->getString()),
@@ -230,13 +264,15 @@ static json::Object createThreadFlow(const PathPieces &Pieces,
return json::Object{{"locations", std::move(Locations)}};
}
-static json::Object createCodeFlow(const PathPieces &Pieces,
+static json::Object createCodeFlow(const LangOptions &LO,
+ const PathPieces &Pieces,
json::Array &Artifacts) {
return json::Object{
- {"threadFlows", json::Array{createThreadFlow(Pieces, Artifacts)}}};
+ {"threadFlows", json::Array{createThreadFlow(LO, Pieces, Artifacts)}}};
}
-static json::Object createResult(const PathDiagnostic &Diag,
+static json::Object createResult(const LangOptions &LO,
+ const PathDiagnostic &Diag,
json::Array &Artifacts,
const StringMap<unsigned> &RuleMapping) {
const PathPieces &Path = Diag.path.flatten(false);
@@ -247,10 +283,10 @@ static json::Object createResult(const PathDiagnostic &Diag,
return json::Object{
{"message", createMessage(Diag.getVerboseDescription())},
- {"codeFlows", json::Array{createCodeFlow(Path, Artifacts)}},
+ {"codeFlows", json::Array{createCodeFlow(LO, Path, Artifacts)}},
{"locations",
json::Array{createLocation(createPhysicalLocation(
- Diag.getLocation().asRange(),
+ LO, Diag.getLocation().asRange(),
*Diag.getLocation().asLocation().getExpansionLoc().getFileEntry(),
SMgr, Artifacts))}},
{"ruleIndex", Iter->getValue()},
@@ -320,18 +356,20 @@ static json::Object createTool(std::vector<const PathDiagnostic *> &Diags,
{"rules", createRules(Diags, RuleMapping)}}}};
}
-static json::Object createRun(std::vector<const PathDiagnostic *> &Diags) {
+static json::Object createRun(const LangOptions &LO,
+ std::vector<const PathDiagnostic *> &Diags) {
json::Array Results, Artifacts;
StringMap<unsigned> RuleMapping;
json::Object Tool = createTool(Diags, RuleMapping);
llvm::for_each(Diags, [&](const PathDiagnostic *D) {
- Results.push_back(createResult(*D, Artifacts, RuleMapping));
+ Results.push_back(createResult(LO, *D, Artifacts, RuleMapping));
});
return json::Object{{"tool", std::move(Tool)},
{"results", std::move(Results)},
- {"artifacts", std::move(Artifacts)}};
+ {"artifacts", std::move(Artifacts)},
+ {"columnKind", "unicodeCodePoints"}};
}
void SarifDiagnostics::FlushDiagnosticsImpl(
@@ -351,6 +389,6 @@ void SarifDiagnostics::FlushDiagnosticsImpl(
{"$schema",
"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"},
{"version", "2.1.0"},
- {"runs", json::Array{createRun(Diags)}}};
+ {"runs", json::Array{createRun(LO, Diags)}}};
OS << llvm::formatv("{0:2}\n", json::Value(std::move(Sarif)));
}
diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp
index b4ab6877726c..b33129c88cea 100644
--- a/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -393,6 +393,11 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
return UnknownVal();
}
+static bool hasSameUnqualifiedPointeeType(QualType ty1, QualType ty2) {
+ return ty1->getPointeeType().getCanonicalType().getTypePtr() ==
+ ty2->getPointeeType().getCanonicalType().getTypePtr();
+}
+
/// CastRetrievedVal - Used by subclasses of StoreManager to implement
/// implicit casts that arise from loads from regions that are reinterpreted
/// as another region.
@@ -421,10 +426,11 @@ SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R,
// FIXME: We really need a single good function to perform casts for us
// correctly every time we need it.
if (castTy->isPointerType() && !castTy->isVoidPointerType())
- if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(V.getAsRegion()))
- if (SR->getSymbol()->getType().getCanonicalType() !=
- castTy.getCanonicalType())
- return loc::MemRegionVal(castRegion(SR, castTy));
+ if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(V.getAsRegion())) {
+ QualType sr = SR->getSymbol()->getType();
+ if (!hasSameUnqualifiedPointeeType(sr, castTy))
+ return loc::MemRegionVal(castRegion(SR, castTy));
+ }
return svalBuilder.dispatchCast(V, castTy);
}
diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 8236907ea773..fea8100c3b3b 100644
--- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -722,13 +722,6 @@ std::string AnalysisConsumer::getFunctionName(const Decl *D) {
} else if (const auto *OCD = dyn_cast<ObjCCategoryImplDecl>(DC)) {
OS << OCD->getClassInterface()->getName() << '('
<< OCD->getName() << ')';
- } else if (isa<ObjCProtocolDecl>(DC)) {
- // We can extract the type of the class from the self pointer.
- if (ImplicitParamDecl *SelfDecl = OMD->getSelfDecl()) {
- QualType ClassTy =
- cast<ObjCObjectPointerType>(SelfDecl->getType())->getPointeeType();
- ClassTy.print(OS, PrintingPolicy(LangOptions()));
- }
}
OS << ' ' << OMD->getSelector().getAsString() << ']';
diff --git a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
index e00fd976f6b8..f5c05281adab 100644
--- a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
@@ -591,7 +591,8 @@ void CheckerRegistry::printCheckerOptionList(raw_ostream &Out) const {
/*MinLineWidth*/ 90);
Out << "\n\n";
};
- for (const std::pair<StringRef, const CmdLineOption &> &Entry : OptionMap) {
+ for (const std::pair<const StringRef, const CmdLineOption &> &Entry :
+ OptionMap) {
const CmdLineOption &Option = Entry.second;
std::string FullOption = (Entry.first + ":" + Option.OptionName).str();
diff --git a/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
index 687fda75db48..7baae6778ebd 100644
--- a/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
@@ -11,6 +11,7 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangStandard.h"
#include "clang/Basic/Stack.h"
+#include "clang/AST/DeclObjC.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"