diff options
author | Devin Coughlin <dcoughlin@apple.com> | 2015-09-24 16:52:56 +0000 |
---|---|---|
committer | Devin Coughlin <dcoughlin@apple.com> | 2015-09-24 16:52:56 +0000 |
commit | 0da2e9334551dcccfe40786769fbb7e6f52f6da3 (patch) | |
tree | 8535ffb6959a44eecd5962a6a558383d3bedeb80 /clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | |
parent | d983ae0a0ec5beeb844992dc23f0b923b7499d75 (diff) | |
download | bcm5719-llvm-0da2e9334551dcccfe40786769fbb7e6f52f6da3.tar.gz bcm5719-llvm-0da2e9334551dcccfe40786769fbb7e6f52f6da3.zip |
[analyzer] When memcpy'ing into a fixed-size array, do not invalidate entire region.
Change the analyzer's modeling of memcpy to be more precise when copying into fixed-size
array fields. With this change, instead of invalidating the entire containing region the
analyzer now invalidates only offsets for the array itself when it can show that the
memcpy stays within the bounds of the array.
This addresses false positive memory leak warnings of the kind reported by
krzysztof in https://llvm.org/bugs/show_bug.cgi?id=22954
(This is the second attempt, now with assertion failures resolved.)
A patch by Pierre Gousseau!
Differential Revision: http://reviews.llvm.org/D12571
llvm-svn: 248516
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 98 |
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. |