diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index d11efc44ccf9..4f0712968bf6 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -1333,9 +1333,7 @@ public: /// Tells that a region's contents is not changed. TK_PreserveContents = 0x1, /// Suppress pointer-escaping of a region. - TK_SuppressEscape = 0x2, - // Do not invalidate super region. - TK_DoNotInvalidateSuperRegion = 0x4 + TK_SuppressEscape = 0x2 // Do not forget to extend StorageTypeForKinds if number of traits exceed // the number of bits StorageTypeForKinds can store. diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index da060414cd4d..54b12410aa56 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -145,8 +145,7 @@ public: static ProgramStateRef InvalidateBuffer(CheckerContext &C, ProgramStateRef state, const Expr *Ex, SVal V, - bool IsSourceBuffer, - const Expr *Size); + bool IsSourceBuffer); static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR); @@ -194,14 +193,6 @@ public: ProgramStateRef state, NonLoc left, NonLoc right) const; - - // Return true if destination buffer of copy function is 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 @@ -823,68 +814,10 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, return strRegion->getStringLiteral(); } -bool CStringChecker::IsFirstBufInBound(CheckerContext &C, - ProgramStateRef state, - const Expr *FirstBuf, - const Expr *Size) { - - // 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); - // Cast is safe as the size argument to copy functions are of integral type. - NonLoc Length = LengthVal.castAs(); - - // Compute the offset of the last element to be accessed: size-1. - NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs(); - NonLoc LastOffset = - svalBuilder.evalBinOpNN(state, BO_Sub, Length, One, sizeTy) - .castAs(); - - // Check that the first buffer is sufficiently long. - SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); - // Cast is safe as caller checks BufVal is a MemRegionVal. - Loc BufLoc = BufStart.castAs(); - - SVal BufEnd = - svalBuilder.evalBinOpLN(state, BO_Add, BufLoc, LastOffset, PtrTy); - - // Check for out of bound array element access. - const MemRegion *R = BufEnd.getAsRegion(); - // BufStart is a MemRegionVal so BufEnd should be one too. - assert(R && "BufEnd should be a MemRegion"); - - // Cast is safe as BufVal's region is a FieldRegion. - const ElementRegion *ER = cast(R); - - assert(ER->getValueType() == C.getASTContext().CharTy && - "IsFirstBufInBound should only be called with char* ElementRegions"); - - // Get the size of the array. - const SubRegion *superReg = cast(ER->getSuperRegion()); - SVal Extent = - svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); - DefinedOrUnknownSVal ExtentSize = Extent.castAs(); - - // Get the index of the accessed element. - DefinedOrUnknownSVal Idx = ER->getIndex().castAs(); - - ProgramStateRef StInBound = state->assumeInBound(Idx, ExtentSize, true); - - return static_cast(StInBound); -} - ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, ProgramStateRef state, const Expr *E, SVal V, - bool IsSourceBuffer, - const Expr *Size) { + bool IsSourceBuffer) { Optional L = V.getAs(); if (!L) return state; @@ -914,16 +847,6 @@ 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, @@ -1077,12 +1000,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, Size); + /*IsSourceBuffer*/false); // 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, nullptr); + /*IsSourceBuffer*/true); C.addTransition(state); } @@ -1697,12 +1620,11 @@ 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, nullptr); + /*IsSourceBuffer*/false); // Invalidate the source (const-invalidation without const-pointer-escaping // the address of the top-level region). - state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true, - nullptr); + state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true); // Set the C string length of the destination, if we know it. if (isBounded && !isAppending) { @@ -1926,7 +1848,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, nullptr); + /*IsSourceBuffer*/false); // 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. diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 0d403bc190ce..417832454359 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -710,7 +710,8 @@ public: } bool AddToWorkList(const MemRegion *R) { - return static_cast(this)->AddToWorkList(R); + const MemRegion *BaseR = R->getBaseRegion(); + return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); } void RunWorkList() { @@ -955,20 +956,9 @@ public: void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); void VisitBinding(SVal V); - - using ClusterAnalysis::AddToWorkList; - - bool AddToWorkList(const MemRegion *R); }; } -bool invalidateRegionsWorker::AddToWorkList(const MemRegion *R) { - bool doNotInvalidateSuperRegion = ITraits.hasTrait( - R, RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); - const MemRegion *BaseR = doNotInvalidateSuperRegion ? R : R->getBaseRegion(); - return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); -} - void invalidateRegionsWorker::VisitBinding(SVal V) { // A symbol? Mark it touched by the invalidation. if (SymbolRef Sym = V.getAsSymbol()) @@ -1081,66 +1071,6 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, } if (const ArrayType *AT = Ctx.getAsArrayType(T)) { - bool doNotInvalidateSuperRegion = ITraits.hasTrait( - baseR, - RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); - - if (doNotInvalidateSuperRegion) { - // We are not doing blank invalidation of the whole array region so we - // have to manually invalidate each elements. - Optional NumElements; - - // Compute lower and upper offsets for region within array. - if (const ConstantArrayType *CAT = dyn_cast(AT)) - NumElements = CAT->getSize().getZExtValue(); - if (!NumElements) // We are not dealing with a constant size array - goto conjure_default; - QualType ElementTy = AT->getElementType(); - uint64_t ElemSize = Ctx.getTypeSize(ElementTy); - const RegionOffset &RO = baseR->getAsOffset(); - const MemRegion *SuperR = baseR->getBaseRegion(); - if (RO.hasSymbolicOffset()) { - // If base region has a symbolic offset, - // we revert to invalidating the super region. - if (SuperR) - AddToWorkList(SuperR); - goto conjure_default; - } - assert(RO.getOffset() >= 0 && "Offset should not be negative"); - uint64_t LowerOffset = RO.getOffset(); - uint64_t UpperOffset = LowerOffset + *NumElements * ElemSize; - - // Invalidate regions which are within array boundaries, - // or have a symbolic offset. - if (!SuperR) - goto conjure_default; - - const ClusterBindings *C = B.lookup(SuperR); - if (!C) - goto conjure_default; - - for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; - ++I) { - const BindingKey &BK = I.getKey(); - Optional ROffset = - BK.hasSymbolicOffset() ? Optional() : BK.getOffset(); - // Check offset is not symbolic and within array's boundaries. - // Handles arrays of 0 elements and of 0-sized elements as well. - if (!ROffset || - (ROffset && - ((*ROffset >= LowerOffset && *ROffset < UpperOffset) || - (LowerOffset == UpperOffset && *ROffset == LowerOffset)))) { - B = B.removeBinding(I.getKey()); - // Bound symbolic regions need to be invalidated for dead symbol - // detection. - SVal V = I.getData(); - const MemRegion *R = V.getAsRegion(); - if (R && isa(R)) - VisitBinding(V); - } - } - } - conjure_default: // Set the default value of the array to conjured symbol. DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, @@ -2257,20 +2187,11 @@ public: void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); using ClusterAnalysis::VisitCluster; - using ClusterAnalysis::AddToWorkList; - - bool AddToWorkList(const MemRegion *R); - bool UpdatePostponed(); void VisitBinding(SVal V); }; } -bool removeDeadBindingsWorker::AddToWorkList(const MemRegion *R) { - const MemRegion *BaseR = R->getBaseRegion(); - return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); -} - void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) { diff --git a/clang/test/Analysis/pr22954.c b/clang/test/Analysis/pr22954.c deleted file mode 100644 index ae385bb862c8..000000000000 --- a/clang/test/Analysis/pr22954.c +++ /dev/null @@ -1,697 +0,0 @@ -// Given code 'struct aa { char s1[4]; char * s2;} a; memcpy(a.s1, ...);', -// this test checks that the CStringChecker only invalidates the destination buffer array a.s1 (instead of a.s1 and a.s2). -// At the moment the whole of the destination array content is invalidated. -// If a.s1 region has a symbolic offset, the whole region of 'a' is invalidated. -// Specific triple set to test structures of size 0. -// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-store=region -verify %s - -typedef __typeof(sizeof(int)) size_t; - -char *strdup(const char *s); -void free(void *); -void *memcpy(void *dst, const void *src, size_t n); // expected-note{{passing argument to parameter 'dst' here}} -void *malloc(size_t n); - -void clang_analyzer_eval(int); - -struct aa { - char s1[4]; - char *s2; -}; - -// Test different types of structure initialisation. -int f0() { - struct aa a0 = {{1, 2, 3, 4}, 0}; - a0.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a0.s1, input, 4); - clang_analyzer_eval(a0.s1[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a0.s1[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a0.s1[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a0.s1[3] == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a0.s2 == 0); // expected-warning{{UNKNOWN}} - free(a0.s2); // no warning - return 0; -} - -int f1() { - struct aa a1; - a1.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a1.s1, input, 4); - clang_analyzer_eval(a1.s1[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a1.s1[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a1.s1[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a1.s1[3] == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a1.s2 == 0); // expected-warning{{UNKNOWN}} - free(a1.s2); // no warning - return 0; -} - -int f2() { - struct aa a2 = {{1, 2}}; - a2.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a2.s1, input, 4); - clang_analyzer_eval(a2.s1[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a2.s1[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a2.s1[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a2.s1[3] == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a2.s2 == 0); // expected-warning{{UNKNOWN}} - free(a2.s2); // no warning - return 0; -} - -int f3() { - struct aa a3 = {{1, 2, 3, 4}, 0}; - a3.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - int * dest = (int*)a3.s1; - memcpy(dest, input, 4); - clang_analyzer_eval(a3.s1[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(dest[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a3.s1[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(dest[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a3.s1[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(dest[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a3.s1[3] == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(dest[3] == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a3.s2 == 0); // expected-warning{{UNKNOWN}} - free(a3.s2); // no warning - return 0; -} - -struct bb { - struct aa a; - char * s2; -}; - -int f4() { - struct bb b0 = {{1, 2, 3, 4}, 0}; - b0.s2 = strdup("hello"); - b0.a.s2 = strdup("hola"); - char input[] = {'a', 'b', 'c', 'd'}; - char * dest = (char*)(b0.a.s1); - memcpy(dest, input, 4); - clang_analyzer_eval(b0.a.s1[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(dest[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b0.a.s1[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(dest[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b0.a.s1[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(dest[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b0.a.s1[3] == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(dest[3] == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b0.s2 == 0); // expected-warning{{UNKNOWN}} - free(b0.a.s2); // no warning - free(b0.s2); // no warning - return 0; -} - -// Test that memory leaks are caught. -int f5() { - struct aa a0 = {{1, 2, 3, 4}, 0}; - a0.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a0.s1, input, 4); - return 0; // expected-warning{{Potential leak of memory pointed to by 'a0.s2'}} -} - -int f6() { - struct aa a1; - a1.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a1.s1, input, 4); - return 0; // expected-warning{{Potential leak of memory pointed to by 'a1.s2'}} -} - -int f7() { - struct aa a2 = {{1, 2}}; - a2.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a2.s1, input, 4); - return 0; // expected-warning{{Potential leak of memory pointed to by 'a2.s2'}} -} - -int f8() { - struct aa a3 = {{1, 2, 3, 4}, 0}; - a3.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - int * dest = (int*)a3.s1; - memcpy(dest, input, 4); - return 0; // expected-warning{{Potential leak of memory pointed to by 'a3.s2'}} -} - -int f9() { - struct bb b0 = {{1, 2, 3, 4}, 0}; - b0.s2 = strdup("hello"); - b0.a.s2 = strdup("hola"); - char input[] = {'a', 'b', 'c', 'd'}; - char * dest = (char*)(b0.a.s1); - memcpy(dest, input, 4); - free(b0.a.s2); // expected-warning{{Potential leak of memory pointed to by 'b0.s2'}} - return 0; -} - -int f10() { - struct bb b0 = {{1, 2, 3, 4}, 0}; - b0.s2 = strdup("hello"); - b0.a.s2 = strdup("hola"); - char input[] = {'a', 'b', 'c', 'd'}; - char * dest = (char*)(b0.a.s1); - memcpy(dest, input, 4); - free(b0.s2); // expected-warning{{Potential leak of memory pointed to by 'b0.a.s2'}} - return 0; -} - -// Test invalidating fields being addresses of array. -struct cc { - char * s1; - char * s2; -}; - -int f11() { - char x[4] = {1, 2}; - x[0] = 1; - x[1] = 2; - struct cc c0; - c0.s2 = strdup("hello"); - c0.s1 = &x[0]; - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(c0.s1, input, 4); - clang_analyzer_eval(x[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(x[1] == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c0.s1[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c0.s1[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c0.s1[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c0.s1[3] == 'd'); // expected-warning{{UNKNOWN}} - free(c0.s2); // no-warning - return 0; -} - -// Test inverting field position between s1 and s2. -struct dd { - char *s2; - char s1[4]; -}; - -int f12() { - struct dd d0 = {0, {1, 2, 3, 4}}; - d0.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(d0.s1, input, 4); - clang_analyzer_eval(d0.s1[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(d0.s1[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(d0.s1[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(d0.s1[3] == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(d0.s2 == 0); // expected-warning{{UNKNOWN}} - free(d0.s2); // no warning - return 0; -} - -// Test arrays of structs. -struct ee { - int a; - char b; -}; - -struct EE { - struct ee s1[2]; - char * s2; -}; - -int f13() { - struct EE E0 = {{{1, 2}, {3, 4}}, 0}; - E0.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(E0.s1, input, 4); - clang_analyzer_eval(E0.s1[0].a == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(E0.s1[0].b == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(E0.s1[1].a == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(E0.s1[1].b == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(E0.s2 == 0); // expected-warning{{UNKNOWN}} - free(E0.s2); // no warning - return 0; -} - -// Test global parameters. -struct aa a15 = {{1, 2, 3, 4}, 0}; - -int f15() { - a15.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a15.s1, input, 4); - clang_analyzer_eval(a15.s1[0] == 'a'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a15.s1[1] == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a15.s1[2] == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a15.s1[3] == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a15.s2 == 0); // expected-warning{{UNKNOWN}} - free(a15.s2); // no warning - return 0; -} - -// Test array of 0 sized elements. -struct empty {}; -struct gg { - struct empty s1[4]; - char * s2; -}; - -int f16() { - struct gg g0 = {{}, 0}; - g0.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(g0.s1, input, 4); - clang_analyzer_eval(*(int*)(&g0.s1[0]) == 'a'); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'g0.s2'}} - clang_analyzer_eval(*(int*)(&g0.s1[1]) == 'b'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(*(int*)(&g0.s1[2]) == 'c'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(*(int*)(&g0.s1[3]) == 'd'); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(g0.s2 == 0); // expected-warning{{UNKNOWN}} - free(g0.s2); // no warning - return 0; -} - -// Test array of 0 elements. -struct hh { - char s1[0]; - char * s2; -}; - -int f17() { - struct hh h0; - h0.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(h0.s1, input, 4); - clang_analyzer_eval(h0.s1[0] == 'a'); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'h0.s2'}} - clang_analyzer_eval(h0.s2 == 0); // expected-warning{{UNKNOWN}} - free(h0.s2); // no warning - return 0; -} - -// Test writing past the array. -struct ii { - char s1[4]; - int i; - int j; - char * s2; -}; - -int f18() { - struct ii i18 = {{1, 2, 3, 4}, 5, 6}; - i18.i = 10; - i18.j = 11; - i18.s2 = strdup("hello"); - char input[100] = {3}; - memcpy(i18.s1, input, 100); - clang_analyzer_eval(i18.s1[0] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'i18.s2'}} - clang_analyzer_eval(i18.s1[1] == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i18.s1[2] == 3); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i18.s1[3] == 4); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i18.i == 10); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i18.j == 11); // expected-warning{{UNKNOWN}} - return 0; -} - -int f181() { - struct ii i181 = {{1, 2, 3, 4}, 5, 6}; - i181.i = 10; - i181.j = 11; - i181.s2 = strdup("hello"); - char input[100] = {3}; - memcpy(i181.s1, input, 5); // invalidate the whole region of i181 - clang_analyzer_eval(i181.s1[0] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'i181.s2'}} - clang_analyzer_eval(i181.s1[1] == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i181.s1[2] == 3); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i181.s1[3] == 4); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i181.i == 10); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i181.j == 11); // expected-warning{{UNKNOWN}} - return 0; -} - -// Test array with a symbolic offset. -struct jj { - char s1[2]; - char * s2; -}; - -struct JJ { - struct jj s1[3]; - char * s2; -}; - -int f19(int i) { - struct JJ J0 = {{{1, 2, 0}, {3, 4, 0}, {5, 6, 0}}, 0}; - J0.s2 = strdup("hello"); - J0.s1[0].s2 = strdup("hello"); - J0.s1[1].s2 = strdup("hi"); - J0.s1[2].s2 = strdup("world"); - char input[2] = {'a', 'b'}; - memcpy(J0.s1[i].s1, input, 2); - clang_analyzer_eval(J0.s1[0].s1[0] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by field 's2'}}\ - expected-warning{{Potential leak of memory pointed to by 'J0.s2'}} - clang_analyzer_eval(J0.s1[0].s1[1] == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(J0.s1[1].s1[0] == 3); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(J0.s1[1].s1[1] == 4); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(J0.s1[2].s1[0] == 5); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(J0.s1[2].s1[1] == 6); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(J0.s1[i].s1[0] == 5); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(J0.s1[i].s1[1] == 6); // expected-warning{{UNKNOWN}} - // FIXME: memory leak warning for J0.s2 should be emitted here instead of after memcpy call. - return 0; // no warning -} - -// Test array with its super region having symbolic offseted regions. -int f20(int i) { - struct aa * a20 = malloc(sizeof(struct aa) * 2); - a20[0].s1[0] = 1; - a20[0].s1[1] = 2; - a20[0].s1[2] = 3; - a20[0].s1[3] = 4; - a20[0].s2 = strdup("hello"); - a20[1].s1[0] = 5; - a20[1].s1[1] = 6; - a20[1].s1[2] = 7; - a20[1].s1[3] = 8; - a20[1].s2 = strdup("world"); - a20[i].s2 = strdup("hola"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a20[0].s1, input, 4); - clang_analyzer_eval(a20[0].s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[0].s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[0].s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[0].s1[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[0].s2 == 0); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[1].s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[1].s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[1].s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[1].s1[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[1].s2 == 0); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[i].s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[i].s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[i].s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[i].s1[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a20[i].s2 == 0); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'a20'}} - - return 0; -} - -// Test array's region and super region both having symbolic offsets. -int f21(int i) { - struct aa * a21 = malloc(sizeof(struct aa) * 2); - a21[0].s1[0] = 1; - a21[0].s1[1] = 2; - a21[0].s1[2] = 3; - a21[0].s1[3] = 4; - a21[0].s2 = 0; - a21[1].s1[0] = 5; - a21[1].s1[1] = 6; - a21[1].s1[2] = 7; - a21[1].s1[3] = 8; - a21[1].s2 = 0; - a21[i].s2 = strdup("hello"); - a21[i].s1[0] = 1; - a21[i].s1[1] = 2; - a21[i].s1[2] = 3; - a21[i].s1[3] = 4; - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a21[i].s1, input, 4); - clang_analyzer_eval(a21[0].s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[0].s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[0].s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[0].s1[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[0].s2 == 0); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[1].s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[1].s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[1].s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[1].s1[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[1].s2 == 0); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[i].s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[i].s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[i].s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[i].s1[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a21[i].s2 == 0); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'a21'}} - - return 0; -} - -// Test regions aliasing other regions. -struct ll { - char s1[4]; - char * s2; -}; - -struct mm { - char s3[4]; - char * s4; -}; - -int f24() { - struct ll l24 = {{1, 2, 3, 4}, 0}; - struct mm * m24 = (struct mm *)&l24; - m24->s4 = strdup("hello"); - char input[] = {1, 2, 3, 4}; - memcpy(m24->s3, input, 4); - clang_analyzer_eval(m24->s3[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m24->s3[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m24->s3[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m24->s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l24.s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l24.s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l24.s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l24.s1[3] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by field 's4'}} - return 0; -} - -// Test region with potential aliasing and symbolic offsets. -// Store assumes no aliasing. -int f25(int i, int j, struct ll * l, struct mm * m) { - m->s4 = strdup("hola"); // m->s4 not tracked - m->s3[0] = 1; - m->s3[1] = 2; - m->s3[2] = 3; - m->s3[3] = 4; - m->s3[j] = 5; // invalidates m->s3 - l->s2 = strdup("hello"); // l->s2 not tracked - l->s1[0] = 6; - l->s1[1] = 7; - l->s1[2] = 8; - l->s1[3] = 9; - l->s1[i] = 10; // invalidates l->s1 - char input[] = {1, 2, 3, 4}; - memcpy(m->s3, input, 4); // does not invalidate l->s1[i] - clang_analyzer_eval(m->s3[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m->s3[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m->s3[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m->s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m->s3[i] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m->s3[j] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l->s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l->s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l->s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l->s1[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l->s1[i] == 1); // expected-warning{{FALSE}} - clang_analyzer_eval(l->s1[j] == 1); // expected-warning{{UNKNOWN}} - return 0; -} - -// Test size with symbolic size argument. -int f26(int i) { - struct aa a26 = {{1, 2, 3, 4}, 0}; - a26.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a26.s1, input, i); // i assumed in bound - clang_analyzer_eval(a26.s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a26.s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a26.s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a26.s1[3] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'a26.s2'}} - return 0; -} - -// Test sizeof as a size argument. -int f261() { - struct aa a261 = {{1, 2, 3, 4}, 0}; - a261.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a261.s1, input, sizeof(a261.s1)); - clang_analyzer_eval(a261.s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a261.s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a261.s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a261.s1[3] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'a261.s2'}} - return 0; -} - -// Test negative size argument. -int f262() { - struct aa a262 = {{1, 2, 3, 4}, 0}; - a262.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(a262.s1, input, -1); - clang_analyzer_eval(a262.s1[0] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'a262.s2'}} - clang_analyzer_eval(a262.s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a262.s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(a262.s1[3] == 1); // expected-warning{{UNKNOWN}} - return 0; -} - -// Test casting regions with symbolic offseted sub regions. -int f27(int i) { - struct mm m27 = {{1, 2, 3, 4}, 0}; - m27.s4 = strdup("hello"); - m27.s3[i] = 5; - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(((struct ll*)(&m27))->s1, input, 4); - clang_analyzer_eval(m27.s3[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m27.s3[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m27.s3[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m27.s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m27.s3[i] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'm27.s4'}} - return 0; -} - -int f28(int i, int j, int k, int l) { - struct mm m28[2]; - m28[i].s4 = strdup("hello"); - m28[j].s3[k] = 1; - struct ll * l28 = (struct ll*)(&m28[1]); - l28->s1[l] = 2; - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(l28->s1, input, 4); - clang_analyzer_eval(m28[0].s3[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[0].s3[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[0].s3[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[0].s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[1].s3[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[1].s3[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[1].s3[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[1].s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[i].s3[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[i].s3[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[i].s3[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[i].s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m28[j].s3[k] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(l28->s1[l] == 2); // expected-warning{{UNKNOWN}} - return 0; -} - -int f29(int i, int j, int k, int l, int m) { - struct mm m29[2]; - m29[i].s4 = strdup("hello"); - m29[j].s3[k] = 1; - struct ll * l29 = (struct ll*)(&m29[l]); - l29->s1[m] = 2; - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(l29->s1, input, 4); - clang_analyzer_eval(m29[0].s3[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[0].s3[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[0].s3[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[0].s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[1].s3[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[1].s3[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[1].s3[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[1].s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[i].s3[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[i].s3[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[i].s3[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[i].s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[j].s3[k] == 1); // expected-warning{{TRUE}}\ - expected-warning{{Potential leak of memory pointed to by field 's4'}} - clang_analyzer_eval(l29->s1[m] == 2); // expected-warning{{UNKNOWN}} - return 0; -} - -// Test unions' fields. -union uu { - char x; - char s1[4]; -}; - -int f30() { - union uu u30 = { .s1 = {1, 2, 3, 4}}; - char input[] = {1, 2, 3, 4}; - memcpy(u30.s1, input, 4); - clang_analyzer_eval(u30.s1[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(u30.s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(u30.s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(u30.s1[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(u30.x == 1); // expected-warning{{UNKNOWN}} - return 0; -} - -struct kk { - union uu u; - char * s2; -}; - -int f31() { - struct kk k31; - k31.s2 = strdup("hello"); - k31.u.x = 1; - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(k31.u.s1, input, 4); - clang_analyzer_eval(k31.u.s1[0] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'k31.s2'}} - clang_analyzer_eval(k31.u.s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(k31.u.s1[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(k31.u.s1[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(k31.u.x == 1); // expected-warning{{UNKNOWN}} - // FIXME: memory leak warning for k31.s2 should be emitted here. - return 0; -} - -union vv { - int x; - char * s2; -}; - -int f32() { - union vv v32; - v32.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(v32.s2, input, 4); - clang_analyzer_eval(v32.s2[0] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(v32.s2[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(v32.s2[2] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(v32.s2[3] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{Potential leak of memory pointed to by 'v32.s2'}} - return 0; -} - -struct nn { - int s1; - int i; - int j; - int k; - char * s2; -}; - -// Test bad types to dest buffer. -int f33() { - struct nn n33 = {1, 2, 3, 4, 0}; - n33.s2 = strdup("hello"); - char input[] = {'a', 'b', 'c', 'd'}; - memcpy(n33.s1, input, 4); // expected-warning{{incompatible integer to pointer conversion passing 'int' to parameter of type 'void *'}} - clang_analyzer_eval(n33.i == 2); // expected-warning{{TRUE}} - clang_analyzer_eval(n33.j == 3); // expected-warning{{TRUE}} - clang_analyzer_eval(n33.k == 4); // expected-warning{{TRUE}} - clang_analyzer_eval(((char*)(n33.s1))[0] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{cast to 'char *' from smaller integer type 'int'}} - clang_analyzer_eval(((char*)(n33.s1))[1] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{cast to 'char *' from smaller integer type 'int'}} - clang_analyzer_eval(((char*)(n33.s1))[2] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{cast to 'char *' from smaller integer type 'int'}} - clang_analyzer_eval(((char*)(n33.s1))[3] == 1); // expected-warning{{UNKNOWN}}\ - expected-warning{{cast to 'char *' from smaller integer type 'int'}} - clang_analyzer_eval(n33.s2 == 0); //expected-warning{{UNKNOWN}} - return 0; // expected-warning{{Potential leak of memory pointed to by 'n33.s2'}} -}