summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp98
1 files changed, 91 insertions, 7 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 07c341400e7..d32b2822229 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -145,7 +145,8 @@ public:
static ProgramStateRef InvalidateBuffer(CheckerContext &C,
ProgramStateRef state,
const Expr *Ex, SVal V,
- bool IsSourceBuffer);
+ bool IsSourceBuffer,
+ const Expr *Size);
static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
const MemRegion *MR);
@@ -193,6 +194,14 @@ public:
ProgramStateRef state,
NonLoc left,
NonLoc right) const;
+
+ // Return true if the destination buffer of the copy function may be in bound.
+ // Expects SVal of Size to be positive and unsigned.
+ // Expects SVal of FirstBuf to be a FieldRegion.
+ static bool IsFirstBufInBound(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *FirstBuf,
+ const Expr *Size);
};
} //end anonymous namespace
@@ -814,10 +823,74 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
return strRegion->getStringLiteral();
}
+bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *FirstBuf,
+ const Expr *Size) {
+ // If we do not know that the buffer is long enough we return 'true'.
+ // Otherwise the parent region of this field region would also get
+ // invalidated, which would lead to warnings based on an unknown state.
+
+ // Originally copied from CheckBufferAccess and CheckLocation.
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ ASTContext &Ctx = svalBuilder.getContext();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ QualType sizeTy = Size->getType();
+ QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
+ SVal BufVal = state->getSVal(FirstBuf, LCtx);
+
+ SVal LengthVal = state->getSVal(Size, LCtx);
+ Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ if (!Length)
+ return true; // cf top comment.
+
+ // Compute the offset of the last element to be accessed: size-1.
+ NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
+ NonLoc LastOffset =
+ svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy)
+ .castAs<NonLoc>();
+
+ // Check that the first buffer is sufficiently long.
+ SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType());
+ Optional<Loc> BufLoc = BufStart.getAs<Loc>();
+ if (!BufLoc)
+ return true; // cf top comment.
+
+ SVal BufEnd =
+ svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, LastOffset, PtrTy);
+
+ // Check for out of bound array element access.
+ const MemRegion *R = BufEnd.getAsRegion();
+ if (!R)
+ return true; // cf top comment.
+
+ const ElementRegion *ER = dyn_cast<ElementRegion>(R);
+ if (!ER)
+ return true; // cf top comment.
+
+ assert(ER->getValueType() == C.getASTContext().CharTy &&
+ "IsFirstBufInBound should only be called with char* ElementRegions");
+
+ // Get the size of the array.
+ const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
+ SVal Extent =
+ svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder));
+ DefinedOrUnknownSVal ExtentSize = Extent.castAs<DefinedOrUnknownSVal>();
+
+ // Get the index of the accessed element.
+ DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
+
+ ProgramStateRef StInBound = state->assumeInBound(Idx, ExtentSize, true);
+
+ return static_cast<bool>(StInBound);
+}
+
ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
ProgramStateRef state,
const Expr *E, SVal V,
- bool IsSourceBuffer) {
+ bool IsSourceBuffer,
+ const Expr *Size) {
Optional<Loc> L = V.getAs<Loc>();
if (!L)
return state;
@@ -847,6 +920,16 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
RegionAndSymbolInvalidationTraits::TK_PreserveContents);
ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
CausesPointerEscape = true;
+ } else {
+ const MemRegion::Kind& K = R->getKind();
+ if (K == MemRegion::FieldRegionKind)
+ if (Size && IsFirstBufInBound(C, state, E, Size)) {
+ // If destination buffer is a field region and access is in bound,
+ // do not invalidate its super region.
+ ITraits.setTrait(
+ R,
+ RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
+ }
}
return state->invalidateRegions(R, E, C.blockCount(), LCtx,
@@ -1000,12 +1083,12 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
// This would probably remove any existing bindings past the end of the
// copied region, but that's still an improvement over blank invalidation.
state = InvalidateBuffer(C, state, Dest, C.getSVal(Dest),
- /*IsSourceBuffer*/false);
+ /*IsSourceBuffer*/false, Size);
// Invalidate the source (const-invalidation without const-pointer-escaping
// the address of the top-level region).
state = InvalidateBuffer(C, state, Source, C.getSVal(Source),
- /*IsSourceBuffer*/true);
+ /*IsSourceBuffer*/true, nullptr);
C.addTransition(state);
}
@@ -1620,11 +1703,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// This would probably remove any existing bindings past the end of the
// string, but that's still an improvement over blank invalidation.
state = InvalidateBuffer(C, state, Dst, *dstRegVal,
- /*IsSourceBuffer*/false);
+ /*IsSourceBuffer*/false, nullptr);
// Invalidate the source (const-invalidation without const-pointer-escaping
// the address of the top-level region).
- state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true);
+ state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true,
+ nullptr);
// Set the C string length of the destination, if we know it.
if (isBounded && !isAppending) {
@@ -1848,7 +1932,7 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
// Invalidate the search string, representing the change of one delimiter
// character to NUL.
State = InvalidateBuffer(C, State, SearchStrPtr, Result,
- /*IsSourceBuffer*/false);
+ /*IsSourceBuffer*/false, nullptr);
// Overwrite the search string pointer. The new value is either an address
// further along in the same string, or NULL if there are no more tokens.
OpenPOWER on IntegriCloud