Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -147,6 +147,8 @@ /// Compute the offset within the top level memory object. RegionOffset getAsOffset() const; + uint64_t getExtent() const; + /// Get a string representation of a region for debug use. std::string getString() const; Index: clang/lib/StaticAnalyzer/Core/MemRegion.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1617,6 +1617,46 @@ return *cachedOffset; } +uint64_t MemRegion::getExtent() const { + const MemRegion *R = this; + + // Prior to this patch we assumed the + // extent of all region is infinite, so in case + // we fail to find the extent, we default to + // "infinity". + uint64_t Extent = UINT64_MAX; + + if (const auto *TVR = dyn_cast(R)) { + if (const auto *FR = dyn_cast(R)) { + const FieldDecl *FD = FR->getDecl(); + if (FD->isBitField()) + return FD->getBitWidthValue(getContext()); + } + + const auto Ty = TVR->getDesugaredValueType(getContext()); + if (!Ty->isIncompleteType()) + Extent = getContext().getTypeSize(Ty); + } else if (const auto *SR = dyn_cast(R)) { + const auto Ty = SR->getSymbol()->getType().getDesugaredType(getContext()); + if (!Ty->isIncompleteType() && !Ty->isVoidPointerType() && + !Ty->isVoidType()) + Extent = getContext().getTypeSize(Ty); + } + + // We either failed to find the extent or we assign something to a + // placeholder. E.g.: + // struct hh { + // char s1[0]; + // char *s2; + // }; + // + // memcpy(h0.s1, input, 4); + if (Extent == 0) + return UINT64_MAX; + + return Extent; +} + //===----------------------------------------------------------------------===// // BlockDataRegion //===----------------------------------------------------------------------===// Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -48,24 +48,29 @@ llvm::PointerIntPair P; uint64_t Data; + uint64_t Extent; /// Create a key for a binding to region \p r, which has a symbolic offset /// from region \p Base. - explicit BindingKey(const SubRegion *r, const SubRegion *Base, Kind k) - : P(r, k | Symbolic), Data(reinterpret_cast(Base)) { + explicit BindingKey(const SubRegion *r, const SubRegion *Base, Kind k, + uint64_t extent) + : P(r, k | Symbolic), Data(reinterpret_cast(Base)), + Extent(extent) { assert(r && Base && "Must have known regions."); assert(getConcreteOffsetRegion() == Base && "Failed to store base region"); } /// Create a key for a binding at \p offset from base region \p r. - explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) - : P(r, k), Data(offset) { + explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k, + uint64_t extent) + : P(r, k), Data(offset), Extent(extent) { assert(r && "Must have known regions."); assert(getOffset() == offset && "Failed to store offset"); assert((r == r->getBaseRegion() || isa(r)) && "Not a base"); } + public: bool isDirect() const { return P.getInt() & Direct; } @@ -76,6 +81,16 @@ assert(!hasSymbolicOffset()); return Data; } + uint64_t getExtent() const { return Extent; } + + uint64_t getRangeBegin() const { + assert(!hasSymbolicOffset()); + return getOffset(); + } + uint64_t getRangeEnd() const { + assert(!hasSymbolicOffset()); + return getOffset() + getExtent(); + } const SubRegion *getConcreteOffsetRegion() const { assert(hasSymbolicOffset()); @@ -91,6 +106,7 @@ void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddPointer(P.getOpaqueValue()); ID.AddInteger(Data); + ID.AddInteger(Extent); } static BindingKey Make(const MemRegion *R, Kind k); @@ -100,12 +116,47 @@ return true; if (P.getOpaqueValue() > X.P.getOpaqueValue()) return false; - return Data < X.Data; + + return Data == X.Data ? Extent < X.Extent : Data < X.Data; } bool operator==(const BindingKey &X) const { - return P.getOpaqueValue() == X.P.getOpaqueValue() && - Data == X.Data; + return P.getOpaqueValue() == X.P.getOpaqueValue() && Data == X.Data && + Extent == X.Extent; + } + + bool contains(const BindingKey &X) const { + if (getRegion() != X.getRegion()) + return false; + + return X.getRangeBegin() >= getRangeBegin() && + X.getRangeBegin() < getRangeEnd() && + X.getRangeEnd() <= getRangeEnd() && + X.getRangeEnd() > getRangeBegin(); + } + + bool overlapsWith(const BindingKey &X) const { + if (hasSymbolicOffset() || X.hasSymbolicOffset()) + return false; + + if (getRegion() != X.getRegion()) + return false; + + uint64_t keyBegin = getRangeBegin(); + uint64_t keyEnd = getRangeEnd(); + uint64_t KBegin = X.getRangeBegin(); + uint64_t KEnd = X.getRangeEnd(); + + // this: |-----| + // X: |-----| + if (keyBegin <= KBegin && KBegin < keyEnd) + return true; + // this: |-----| + // X: |-----| + if (keyBegin >= KBegin && keyBegin < KEnd) + return true; + + return contains(X) || X.contains(*this); } LLVM_DUMP_METHOD void dump() const; @@ -115,9 +166,10 @@ BindingKey BindingKey::Make(const MemRegion *R, Kind k) { const RegionOffset &RO = R->getAsOffset(); if (RO.hasSymbolicOffset()) - return BindingKey(cast(R), cast(RO.getRegion()), k); + return BindingKey(cast(R), cast(RO.getRegion()), k, + R->getExtent()); - return BindingKey(RO.getRegion(), RO.getOffset(), k); + return BindingKey(RO.getRegion(), RO.getOffset(), k, R->getExtent()); } namespace llvm { @@ -130,6 +182,9 @@ else Out << "null"; + Out << ", \"extent\": "; + Out << K.getExtent(); + return Out; } @@ -143,7 +198,207 @@ // Actual Store type. //===----------------------------------------------------------------------===// -typedef llvm::ImmutableMap ClusterBindings; +// FIXME: Remove this artifact once the patch is finalized. +#define RSPPDEBUG 0 + +class ClusterBindings : public llvm::ImmutableMap { +public: + ClusterBindings(const llvm::ImmutableMap &m) + : llvm::ImmutableMap(m) {} + ClusterBindings(llvm::ImmutableMap &&m) + : llvm::ImmutableMap(m) {} + +public: + const SVal *lookup(const BindingKey &K) const { + +#if RSPPDEBUG + llvm::errs() << "Lookup key: "; + K.dump(); + llvm::errs() << "\n"; + dump(); +#endif + + auto res = llvm::ImmutableMap::lookup(K); + +#if RSPPDEBUG + llvm::errs() << "Found: " << res << '\n'; + llvm::errs() << "--------------------------------------------------\n"; +#endif + + return res; + } + + const SVal *lookupPartial(const BindingKey &K) const { +#if RSPPDEBUG + llvm::errs() << "Partial lookup key: "; + K.dump(); + llvm::errs() << "\n"; + dump(); +#endif + + if (K.hasSymbolicOffset()) + return nullptr; + + // Try to find the smallest binding, the key is the part of. + size_t lowerBound = 0; + size_t upperBound = UINT64_MAX; + const SVal *hit = nullptr; + for (auto &[Key, Val] : *this) { + if (!Key.hasSymbolicOffset() && Key.getRegion() == K.getRegion() && + Key.isDirect() == K.isDirect()) { + + bool insideLower = K.getRangeBegin() >= Key.getRangeBegin() && + K.getRangeBegin() < Key.getRangeEnd() && + Key.getRangeBegin() >= lowerBound; + bool insideUpper = K.getRangeEnd() <= Key.getRangeEnd() && + K.getRangeEnd() > Key.getRangeBegin() && + Key.getRangeEnd() <= upperBound; + + if (insideLower && insideUpper) { + lowerBound = Key.getOffset(); + upperBound = Key.getOffset() + Key.getExtent(); + hit = &Val; + } + } + } + +#if RSPPDEBUG + llvm::errs() << "hit: "; + if (hit) + hit->dump(); + else + llvm::errs() << "nullptr"; + + llvm::errs() << "\n--------------------------------------------------\n"; +#endif + + return hit; + } + + std::vector getValuesCoveringBinding(const BindingKey &K) const { + if (K.hasSymbolicOffset()) + return {}; + + using tmpVectorType = std::pair; + std::vector tmp; + + // Filter out bindings with same type as the one we search for. + // We also make the number of elements smaller by only filtering + // those that are near the current binding. + for (auto &[key, val] : *this) { + if (key.hasSymbolicOffset() || key.isDirect() != K.isDirect() || + key == K || key.getRegion() != K.getRegion()) + continue; + + uint64_t keyBegin = key.getRangeBegin(); + uint64_t keyEnd = key.getRangeEnd(); + uint64_t KBegin = K.getRangeBegin(); + uint64_t KEnd = K.getRangeEnd(); + + // key: |-----| + // K: |-----| + if (keyBegin <= KBegin && KBegin < keyEnd) + tmp.emplace_back(&key, &val); + // key: |-| + // K: |-----| + else if (keyBegin >= KBegin && keyEnd <= KEnd) + tmp.emplace_back(&key, &val); + // key: |-----| + // K: |-----| + else if (keyBegin >= KBegin && keyBegin < KEnd) + tmp.emplace_back(&key, &val); + } + + if (tmp.empty()) + return {}; + + // Sort the bidnings by their ranges in ascending order. + std::sort(tmp.begin(), tmp.end(), + [](const tmpVectorType &lhs, const tmpVectorType &rhs) { + if (lhs.first->getRangeBegin() > rhs.first->getRangeBegin()) + return false; + if (lhs.first->getRangeBegin() < rhs.first->getRangeEnd()) + return true; + return lhs.first->getRangeEnd() < rhs.first->getRangeEnd(); + }); + + // Concatenate the ranges and values. + std::vector out; + std::vector iv{tmp[0].first->getRangeBegin(), + tmp[0].first->getRangeEnd()}; + + for (auto &[key, val] : tmp) { + + // We found a gap in the region of the binding we look for, so it cannot + // be fully covered by initialized values. + if (key->getRangeBegin() > iv[1]) + return {}; + + if (key->getRangeEnd() >= iv[1]) { + iv[1] = key->getRangeEnd(); + out.push_back(*val); + } + } + +#if RSPPDEBUG + dump(); + llvm::errs() << "Key: " << K << '\n'; + + llvm::errs() << "Overlapping bindings: \n"; + llvm::errs() << "{\n"; + for (auto &&v : out) { + llvm::errs() << v << '\n'; + } + llvm::errs() << "}\n"; +#endif + + if (iv[0] <= K.getRangeBegin() && iv[1] >= K.getRangeEnd()) + return out; + + return {}; + }; + + bool regionContainsOverlappingBindings(const BindingKey &K) const { + if (K.hasSymbolicOffset()) + return false; + + size_t cnt = 0; + for (auto &&[key, val] : *this) { + if (key.hasSymbolicOffset() || key.getRegion() != K.getRegion() || + key.isDirect() != K.isDirect()) + continue; + + if (key.getRangeBegin() >= K.getRangeBegin() && + key.getRangeBegin() < K.getRangeEnd() && + key.getRangeEnd() <= K.getRangeEnd() && + key.getRangeEnd() > K.getRangeBegin()) + ++cnt; + else if (K.getRangeBegin() >= key.getRangeBegin() && + K.getRangeBegin() < key.getRangeEnd() && + K.getRangeEnd() <= key.getRangeEnd() && + K.getRangeEnd() > key.getRangeBegin()) + ++cnt; + + if (cnt > 1) + return true; + } + + return false; + } + +#if RSPPDEBUG + void dump() const { + for (auto &&[Key, Val] : *this) { + llvm::errs() << '{'; + Key.dump(); + llvm::errs() << " value: "; + Val.dump(); + llvm::errs() << '}' << '\n'; + } + } +#endif +}; + typedef llvm::ImmutableMapRef ClusterBindingsRef; typedef std::pair BindingPair; @@ -198,7 +453,7 @@ RegionBindingsRef addBinding(const MemRegion *R, BindingKey::Kind k, SVal V) const; - const SVal *lookup(BindingKey K) const; + const SVal *lookup(BindingKey K, bool partial = false) const; const SVal *lookup(const MemRegion *R, BindingKey::Kind k) const; using llvm::ImmutableMapRef::lookup; @@ -218,6 +473,31 @@ /// binding associated with a region and its subregions. std::optional getDefaultBinding(const MemRegion *R) const; + // Return a binding without checking the extent. This is useful in cases, when + // some slicing happens. E.g.: + // int x = 10; <- Region: X, Offset: 0, Extent: 32 + // *((char *)&x) = 1; <- Region: X, Offset: 0, Extent: 8 + std::optional getPartialBinding(const MemRegion *R, + BindingKey::Kind Kind) const; + + // Return a list of values that cover the region. + // E.g.: + // struct S { + // char a; + // char b; + // char c; + // char d; + // }; + // + // void foo() { + // S s{1, 2, 3, 4}; + // int x = *(int *)&s; <-- return {1, 2, 3, 4} + // } + std::vector getValuesCoveringRegion(const MemRegion *R, + BindingKey::Kind Kind) const; + + bool isRegionCoveredByMultipleBindings(const MemRegion *R, + BindingKey::Kind Kind) const; /// Return the internal tree as a Store. Store asStore() const { llvm::PointerIntPair Ptr = { @@ -274,29 +554,62 @@ return V ? std::optional(*V) : std::nullopt; } +std::optional +RegionBindingsRef::getPartialBinding(const MemRegion *R, + BindingKey::Kind Kind) const { + const SVal *V = lookup(BindingKey::Make(R, Kind), true); + return V ? std::optional(*V) : std::nullopt; +} + +std::vector +RegionBindingsRef::getValuesCoveringRegion(const MemRegion *R, + BindingKey::Kind Kind) const { + auto K = BindingKey::Make(R, Kind); + const ClusterBindings *Cluster = lookup(K.getBaseRegion()); + if (!Cluster) + return {}; + return Cluster->getValuesCoveringBinding(K); +} + +bool RegionBindingsRef::isRegionCoveredByMultipleBindings( + const MemRegion *R, BindingKey::Kind Kind) const { + auto K = BindingKey::Make(R, Kind); + const ClusterBindings *Cluster = lookup(K.getBaseRegion()); + if (!Cluster) + return false; + return Cluster->regionContainsOverlappingBindings(K); +} + RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const { const MemRegion *Base = K.getBaseRegion(); const ClusterBindings *ExistingCluster = lookup(Base); ClusterBindings Cluster = - (ExistingCluster ? *ExistingCluster : CBFactory->getEmptyMap()); + (ExistingCluster + ? *ExistingCluster + : static_cast(CBFactory->getEmptyMap())); + + for (auto &&B : Cluster) { + if (K.overlapsWith(B.first) && B.first.isDirect() && K.isDirect()) { + Cluster = CBFactory->add(Cluster, B.first, UnknownVal{}); + } + } ClusterBindings NewCluster = CBFactory->add(Cluster, K, V); return add(Base, NewCluster); } - RegionBindingsRef RegionBindingsRef::addBinding(const MemRegion *R, BindingKey::Kind k, SVal V) const { return addBinding(BindingKey::Make(R, k), V); } -const SVal *RegionBindingsRef::lookup(BindingKey K) const { +const SVal *RegionBindingsRef::lookup(BindingKey K, bool partial) const { const ClusterBindings *Cluster = lookup(K.getBaseRegion()); if (!Cluster) return nullptr; - return Cluster->lookup(K); + return partial ? Cluster->lookupPartial(K) : Cluster->lookup(K); } const SVal *RegionBindingsRef::lookup(const MemRegion *R, @@ -860,62 +1173,29 @@ TopKey = BindingKey::Make(Top, BindingKey::Default); } - // Find the length (in bits) of the region being invalidated. - uint64_t Length = UINT64_MAX; - SVal Extent = Top->getMemRegionManager().getStaticSize(Top, SVB); - if (std::optional ExtentCI = - Extent.getAs()) { - const llvm::APSInt &ExtentInt = ExtentCI->getValue(); - assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned()); - // Extents are in bytes but region offsets are in bits. Be careful! - Length = ExtentInt.getLimitedValue() * SVB.getContext().getCharWidth(); - } else if (const FieldRegion *FR = dyn_cast(Top)) { - if (FR->getDecl()->isBitField()) - Length = FR->getDecl()->getBitWidthValue(SVB.getContext()); - } - for (ClusterBindings::iterator I = Cluster.begin(), E = Cluster.end(); I != E; ++I) { BindingKey NextKey = I.getKey(); - if (NextKey.getRegion() == TopKey.getRegion()) { - // FIXME: This doesn't catch the case where we're really invalidating a - // region with a symbolic offset. Example: - // R: points[i].y - // Next: points[0].x - - if (NextKey.getOffset() > TopKey.getOffset() && - NextKey.getOffset() - TopKey.getOffset() < Length) { - // Case 1: The next binding is inside the region we're invalidating. - // Include it. - Bindings.push_back(*I); - - } else if (NextKey.getOffset() == TopKey.getOffset()) { - // Case 2: The next binding is at the same offset as the region we're - // invalidating. In this case, we need to leave default bindings alone, - // since they may be providing a default value for a regions beyond what - // we're invalidating. - // FIXME: This is probably incorrect; consider invalidating an outer - // struct whose first field is bound to a LazyCompoundVal. - if (IncludeAllDefaultBindings || NextKey.isDirect()) - Bindings.push_back(*I); - } - + if (TopKey.hasSymbolicOffset()) { + Bindings.push_back(*I); } else if (NextKey.hasSymbolicOffset()) { const MemRegion *Base = NextKey.getConcreteOffsetRegion(); if (Top->isSubRegionOf(Base) && Top != Base) { - // Case 3: The next key is symbolic and we just changed something within + // The next key is symbolic and we just changed something within // its concrete region. We don't know if the binding is still valid, so // we'll be conservative and include it. if (IncludeAllDefaultBindings || NextKey.isDirect()) if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) Bindings.push_back(*I); } else if (const SubRegion *BaseSR = dyn_cast(Base)) { - // Case 4: The next key is symbolic, but we changed a known + // The next key is symbolic, but we changed a known // super-region. In this case the binding is certainly included. - if (BaseSR->isSubRegionOf(Top)) - if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) - Bindings.push_back(*I); + if (BaseSR->isSubRegionOf(Top) && + isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) + Bindings.push_back(*I); } + } else if (TopKey.contains(NextKey)) { + Bindings.push_back(*I); } } } @@ -958,8 +1238,10 @@ ClusterBindingsRef Result(*Cluster, CBFactory); for (SmallVectorImpl::const_iterator I = Bindings.begin(), E = Bindings.end(); - I != E; ++I) - Result = Result.remove(I->first); + I != E; ++I) { + + Result = Result.remove(I->first); + } // If we're invalidating a region with a symbolic offset, we need to make sure // we don't treat the base region as uninitialized anymore. @@ -1542,6 +1824,8 @@ getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B, const SubRegion *R, bool AllowSubregionBindings) { std::optional V = B.getDefaultBinding(R); + if (!V) + V = B.getPartialBinding(R, BindingKey::Default); if (!V) return std::nullopt; @@ -1921,8 +2205,27 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { // Check if the region has a binding. - if (const std::optional &V = B.getDirectBinding(R)) + if (const std::optional &V = B.getDirectBinding(R)) { + if (B.isRegionCoveredByMultipleBindings(R, BindingKey::Direct)) + return UnknownVal(); + return *V; + } + + // FIXME: Value slicing is not supported yet. + if (const std::optional &V = + B.getPartialBinding(R, BindingKey::Direct)) { + if (SymbolRef parentSym = V->getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + if (V->isConstant()) + return UnknownVal(); + } + + // FIXME: Figure out how to concatenate these values. + if (const auto &OV = B.getValuesCoveringRegion(R, BindingKey::Direct); + !OV.empty()) + return UnknownVal(); const MemRegion* superR = R->getSuperRegion(); @@ -1973,8 +2276,27 @@ const FieldRegion* R) { // Check if the region has a binding. - if (const std::optional &V = B.getDirectBinding(R)) + if (const std::optional &V = B.getDirectBinding(R)) { + if (B.isRegionCoveredByMultipleBindings(R, BindingKey::Direct)) + return UnknownVal(); + return *V; + } + + // FIXME: Value slicing is not supported yet, so we return Unknown. + if (const std::optional &V = + B.getPartialBinding(R, BindingKey::Direct)) { + if (SymbolRef parentSym = V->getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + if (V->isConstant()) + return UnknownVal(); + } + + // FIXME: Figure out how to concatenate these values. + if (const auto &OV = B.getValuesCoveringRegion(R, BindingKey::Direct); + !OV.empty()) + return UnknownVal(); // If the containing record was initialized, try to get its constant value. const FieldDecl *FD = R->getDecl(); @@ -2028,7 +2350,10 @@ RegionBindingsConstRef B, const MemRegion *superR, const TypedValueRegion *R, QualType Ty) { - if (const std::optional &D = B.getDefaultBinding(superR)) { + std::optional D = B.getDefaultBinding(superR); + if (!D) + D = B.getPartialBinding(superR, BindingKey::Default); + if (D) { const SVal &val = *D; if (SymbolRef parentSym = val.getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -2056,26 +2381,8 @@ if (const ElementRegion *ER = dyn_cast(LazyBindingRegion)) Result = getBindingForElement(LazyBinding, ER); else - Result = getBindingForField(LazyBinding, - cast(LazyBindingRegion)); - - // FIXME: This is a hack to deal with RegionStore's inability to distinguish a - // default value for /part/ of an aggregate from a default value for the - // /entire/ aggregate. The most common case of this is when struct Outer - // has as its first member a struct Inner, which is copied in from a stack - // variable. In this case, even if the Outer's default value is symbolic, 0, - // or unknown, it gets overridden by the Inner's default value of undefined. - // - // This is a general problem -- if the Inner is zero-initialized, the Outer - // will now look zero-initialized. The proper way to solve this is with a - // new version of RegionStore that tracks the extent of a binding as well - // as the offset. - // - // This hack only takes care of the undefined case because that can very - // quickly result in a warning. - if (Result.isUndef()) - Result = UnknownVal(); - + Result = + getBindingForField(LazyBinding, cast(LazyBindingRegion)); return Result; } @@ -2163,6 +2470,9 @@ if (!hasPartialLazyBinding && !isa(R->getBaseRegion())) { if (const std::optional &V = B.getDefaultBinding(R)) return *V; + if (const std::optional &V = + B.getPartialBinding(R, BindingKey::Default)) + return *V; return UndefinedVal(); } } @@ -2174,13 +2484,36 @@ SVal RegionStoreManager::getBindingForObjCIvar(RegionBindingsConstRef B, const ObjCIvarRegion* R) { // Check if the region has a binding. - if (const std::optional &V = B.getDirectBinding(R)) + if (const std::optional &V = B.getDirectBinding(R)) { + if (B.isRegionCoveredByMultipleBindings(R, BindingKey::Direct)) + return UnknownVal(); + return *V; + } + + // FIXME: Value slicing is not supported yet, so we return Unknown. + if (const std::optional &V = + B.getPartialBinding(R, BindingKey::Direct)) { + if (SymbolRef parentSym = V->getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + if (V->isConstant()) + return UnknownVal(); + } + + // FIXME: Figure out how to concatenate these values. + if (const auto &OV = B.getValuesCoveringRegion(R, BindingKey::Direct); + !OV.empty()) + return UnknownVal(); const MemRegion *superR = R->getSuperRegion(); // Check if the super region has a default binding. - if (const std::optional &V = B.getDefaultBinding(superR)) { + std::optional V = B.getDefaultBinding(superR); + if (!V) + V = B.getPartialBinding(superR, BindingKey::Default); + + if (V) { if (SymbolRef parentSym = V->getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -2195,12 +2528,33 @@ const VarRegion *R) { // Check if the region has a binding. - if (std::optional V = B.getDirectBinding(R)) + if (std::optional V = B.getDirectBinding(R)) { + if (B.isRegionCoveredByMultipleBindings(R, BindingKey::Direct)) + return UnknownVal(); + return *V; + } + + // FIXME: Value slicing is not supported yet, so we return Unknown. + if (std::optional V = B.getPartialBinding(R, BindingKey::Direct)) { + if (SymbolRef parentSym = V->getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + if (V->isConstant()) + return UnknownVal(); + } + + // FIXME: Figure out how to concatenate these values. + if (const auto &OV = B.getValuesCoveringRegion(R, BindingKey::Direct); + !OV.empty()) + return UnknownVal(); if (std::optional V = B.getDefaultBinding(R)) return *V; + if (std::optional V = B.getPartialBinding(R, BindingKey::Default)) + return *V; + // Lazily derive a value for the VarRegion. const VarDecl *VD = R->getDecl(); const MemSpaceRegion *MS = R->getMemorySpace(); Index: clang/test/Analysis/array-init-loop.cpp =================================================================== --- clang/test/Analysis/array-init-loop.cpp +++ clang/test/Analysis/array-init-loop.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -std=c++17 -verify %s void clang_analyzer_eval(bool); @@ -19,7 +19,11 @@ auto [a, b, c, d, e] = arr; - int x = e; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(d); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(e); // expected-warning{{UNDEFINED}} } void lambda_init() { @@ -44,21 +48,20 @@ void lambda_uninit() { int arr[5]; - // FIXME: These should be Undefined, but we fail to read Undefined from a lazyCompoundVal - int l = [arr] { return arr[0]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + int l = [arr] { return arr[0]; }(); + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} l = [arr] { return arr[1]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} l = [arr] { return arr[2]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} l = [arr] { return arr[3]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} l = [arr] { return arr[4]; }(); - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} } struct S { @@ -86,14 +89,11 @@ S copy = orig; - // FIXME: These should be Undefined, but we fail to read Undefined from a lazyCompoundVal. - // If the struct is not considered a small struct, instead of a copy, we store a lazy compound value. - // As the struct has an array data member, it is not considered small. - clang_analyzer_eval(copy.arr[0]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(copy.arr[1]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(copy.arr[2]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(copy.arr[3]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(copy.arr[4]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(copy.arr[0]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(copy.arr[1]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(copy.arr[2]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(copy.arr[3]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(copy.arr[4]); // expected-warning{{UNDEFINED}} } void move_ctor_init() { @@ -118,12 +118,11 @@ S moved = (S &&) orig; - // FIXME: These should be Undefined, but we fail to read Undefined from a lazyCompoundVal. - clang_analyzer_eval(moved.arr[0]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(moved.arr[1]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(moved.arr[2]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(moved.arr[3]); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(moved.arr[4]); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(moved.arr[0]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(moved.arr[1]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(moved.arr[2]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(moved.arr[3]); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(moved.arr[4]); // expected-warning{{UNDEFINED}} } // The struct has a user defined copy and move ctor, which allow us to @@ -168,7 +167,8 @@ void array_uninit_non_pod() { S3 arr[1]; - auto [a] = arr; // expected-warning@159{{ in implicit constructor is garbage or undefined }} + auto [a] = arr; + clang_analyzer_eval(a.i); // expected-warning{{UNDEFINED}} } void lambda_init_non_pod() { @@ -191,7 +191,8 @@ void lambda_uninit_non_pod() { S3_duplicate arr[4]; - int l = [arr] { return arr[3].i; }(); // expected-warning@164{{ in implicit constructor is garbage or undefined }} + int l = [arr] { return arr[3].i; }(); + clang_analyzer_eval(l); // expected-warning{{UNDEFINED}} } // If this struct is being copy/move constructed by the implicit ctors, ArrayInitLoopExpr Index: clang/test/Analysis/array-punned-region.c =================================================================== --- clang/test/Analysis/array-punned-region.c +++ clang/test/Analysis/array-punned-region.c @@ -20,7 +20,8 @@ int array_struct_bitfield_2() { BITFIELD_CAST ff = {0}; BITFIELD_CAST *pff = &ff; - int a = *((int *)pff + 2); // expected-warning{{Assigned value is garbage or undefined [core.uninitialized.Assign]}} + int a = *((int *)pff + 2); + clang_analyzer_eval(a == 0); // expected-warning{{TRUE}} return a; } Index: clang/test/Analysis/bstring_UninitRead.c =================================================================== --- clang/test/Analysis/bstring_UninitRead.c +++ clang/test/Analysis/bstring_UninitRead.c @@ -31,10 +31,7 @@ int dst[5] = {0}; int *p; - p = mempcpy(dst, src, 4 * sizeof(int)); // expected-warning{{Bytes string function accesses uninitialized/garbage values}} - // FIXME: This behaviour is actually surprising and needs to be fixed, - // mempcpy seems to consider the very last byte of the src buffer uninitialized - // and returning undef unfortunately. It should have returned unknown or a conjured value instead. + p = mempcpy(dst, src, 4 * sizeof(int)); clang_analyzer_eval(p == &dst[4]); // no-warning (above is fatal) } @@ -52,8 +49,7 @@ struct st *p2; p1 = (&s2) + 1; - p2 = mempcpy(&s2, &s1, sizeof(struct st)); // expected-warning{{Bytes string function accesses uninitialized/garbage values}} - // FIXME: It seems same as mempcpy14() case. + p2 = mempcpy(&s2, &s1, sizeof(struct st)); clang_analyzer_eval(p1 == p2); // no-warning (above is fatal) } Index: clang/test/Analysis/casts.c =================================================================== --- clang/test/Analysis/casts.c +++ clang/test/Analysis/casts.c @@ -1,5 +1,5 @@ // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin9 -fenable-matrix -analyzer-checker=core,alpha.core,debug.ExprInspection -Wno-pointer-to-int-cast -Wno-strict-prototypes -verify -analyzer-config eagerly-assume=false %s -// RUN: %clang_analyze_cc1 -triple i386-apple-darwin9 -fenable-matrix -analyzer-checker=core,alpha.core,debug.ExprInspection -Wno-pointer-to-int-cast -Wno-strict-prototypes -verify -analyzer-config eagerly-assume=false %s +// RUN: %clang_analyze_cc1 -triple i386-apple-darwin9 -fenable-matrix -analyzer-checker=core,alpha.core,debug.ExprInspection -Wno-pointer-to-int-cast -Wno-strict-prototypes -verify -analyzer-config eagerly-assume=false -DBIT32TEST=1 %s // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin9 -fenable-matrix -analyzer-checker=core,alpha.core,debug.ExprInspection -Wno-pointer-to-int-cast -Wno-strict-prototypes -verify -DEAGERLY_ASSUME=1 -w %s // RUN: %clang_analyze_cc1 -triple i386-apple-darwin9 -fenable-matrix -analyzer-checker=core,alpha.core,debug.ExprInspection -Wno-pointer-to-int-cast -Wno-strict-prototypes -verify -DEAGERLY_ASSUME=1 -DBIT32=1 -w %s @@ -159,21 +159,33 @@ void testCastVoidPtrToIntPtrThroughIntTypedAssignment(void) { int *x; (*((int *)(&x))) = (int)getVoidPtr(); + #if defined I386 || defined BIT32TEST *x = 1; // no-crash + #else + *x = 1; // expected-warning{{undefined}} + #endif } void testCastUIntPtrToIntPtrThroughIntTypedAssignment(void) { unsigned u; int *x; (*((int *)(&x))) = (int)&u; + #if defined I386 || defined BIT32TEST *x = 1; clang_analyzer_eval(u == 1); // expected-warning{{TRUE}} + #else + *x = 1; // expected-warning{{undefined}} + #endif } void testCastVoidPtrToIntPtrThroughUIntTypedAssignment(void) { int *x; (*((int *)(&x))) = (int)(unsigned *)getVoidPtr(); + #if defined I386 || defined BIT32TEST *x = 1; // no-crash + #else + *x = 1; // expected-warning{{undefined}} + #endif } void testLocNonLocSymbolAssume(int a, int *b) { Index: clang/test/Analysis/copy-elision.cpp =================================================================== --- clang/test/Analysis/copy-elision.cpp +++ clang/test/Analysis/copy-elision.cpp @@ -58,13 +58,8 @@ C() : t(T(4)) { S s = {1, 2, 3}; t.s = s; - // FIXME: Should be TRUE regardless of copy elision. clang_analyzer_eval(t.w == 4); -#ifdef ELIDE - // expected-warning@-2{{TRUE}} -#else - // expected-warning@-4{{UNKNOWN}} -#endif + // expected-warning@-1{{TRUE}} } }; Index: clang/test/Analysis/ctor.mm =================================================================== --- clang/test/Analysis/ctor.mm +++ clang/test/Analysis/ctor.mm @@ -831,8 +831,7 @@ #endif clang_analyzer_eval(w == 4); // expected-warning{{TRUE}} - // FIXME: Should be UNKNOWN. Changed in B() since glob_y was assigned. - clang_analyzer_eval(y == glob_y); // expected-warning{{TRUE}} + clang_analyzer_eval(y == glob_y); // expected-warning{{UNKNOWN}} #ifdef I386 clang_analyzer_eval(z == glob_z); // expected-warning{{UNKNOWN}} Index: clang/test/Analysis/cxx-uninitialized-object-ptr-ref.cpp =================================================================== --- clang/test/Analysis/cxx-uninitialized-object-ptr-ref.cpp +++ clang/test/Analysis/cxx-uninitialized-object-ptr-ref.cpp @@ -289,10 +289,12 @@ } struct CyclicPointerTest1 { - int *ptr; // expected-note{{object references itself 'this->ptr'}} + // FIXME: Should be a note for self reference. + int *ptr; int dontGetFilteredByNonPedanticMode = 0; - CyclicPointerTest1() : ptr(reinterpret_cast(&ptr)) {} // expected-warning{{1 uninitialized field}} + // FIXME: Should be uninitialized field. + CyclicPointerTest1() : ptr(reinterpret_cast(&ptr)) {} }; void fCyclicPointerTest1() { Index: clang/test/Analysis/cxx-uninitialized-object.cpp =================================================================== --- clang/test/Analysis/cxx-uninitialized-object.cpp +++ clang/test/Analysis/cxx-uninitialized-object.cpp @@ -871,12 +871,13 @@ Callable functor; int dontGetFilteredByNonPedanticMode = 0; - MultipleLambdaCapturesTest2(const Callable &functor, int) : functor(functor) {} // expected-warning{{1 uninitialized field}} + MultipleLambdaCapturesTest2(const Callable &functor, int) : functor(functor) {} // expected-warning{{2 uninitialized field}} }; void fMultipleLambdaCapturesTest2() { int b1, b2 = 3, b3; auto equals = [b1, &b2, &b3](int a) { return a == b1 == b2 == b3; }; // expected-note{{uninitialized pointee 'this->functor./*captured variable*/b3'}} + // expected-note@-1{{uninitialized field 'this->functor./*captured variable*/b1'}} MultipleLambdaCapturesTest2(equals, int()); } Index: clang/test/Analysis/dump_egraph.cpp =================================================================== --- clang/test/Analysis/dump_egraph.cpp +++ clang/test/Analysis/dump_egraph.cpp @@ -21,7 +21,7 @@ // CHECK: \"location_context\": \"#0 Call\", \"calling\": \"T::T\", \"location\": \{ \"line\": 15, \"column\": 5, \"file\": \"{{.*}}dump_egraph.cpp\" \}, \"items\": [\l        \{ \"init_id\": {{[0-9]+}}, \"kind\": \"construct into member variable\", \"argument_index\": null, \"pretty\": \"s\", \"value\": \"&t.s\" -// CHECK: \"cluster\": \"t\", \"pointer\": \"{{0x[0-9a-f]+}}\", \"items\": [\l        \{ \"kind\": \"Default\", \"offset\": 0, \"value\": \"conj_$2\{int, LC5, no stmt, #1\}\" +// CHECK: \"cluster\": \"t\", \"pointer\": \"{{0x[0-9a-f]+}}\", \"items\": [\l        \{ \"kind\": \"Default\", \"offset\": 0, \"extent\": {{[0-9]+}}, \"value\": \"conj_$2\{int, LC5, no stmt, #1\}\" // CHECK: \"dynamic_types\": [\l      \{ \"region\": \"HeapSymRegion\{conj_$1\{S *, LC1, S{{[0-9]+}}, #1\}\}\", \"dyn_type\": \"S\", \"sub_classable\": false \}\l Index: clang/test/Analysis/expr-inspection.c =================================================================== --- clang/test/Analysis/expr-inspection.c +++ clang/test/Analysis/expr-inspection.c @@ -31,7 +31,7 @@ // CHECK: "program_state": { // CHECK-NEXT: "store": { "pointer": "{{0x[0-9a-f]+}}", "items": [ // CHECK-NEXT: { "cluster": "y", "pointer": "{{0x[0-9a-f]+}}", "items": [ -// CHECK-NEXT: { "kind": "Direct", "offset": {{[0-9]+}}, "value": "2 S32b" } +// CHECK-NEXT: { "kind": "Direct", "offset": {{[0-9]+}}, "extent": {{[0-9]+}}, "value": "2 S32b" } // CHECK-NEXT: ]} // CHECK-NEXT: ]}, // CHECK-NEXT: "environment": { "pointer": "{{0x[0-9a-f]+}}", "items": [ Index: clang/test/Analysis/pr22954.c =================================================================== --- clang/test/Analysis/pr22954.c +++ clang/test/Analysis/pr22954.c @@ -827,7 +827,7 @@ z37 = (struct zz *)((char*)(z37) + 4); // Increment back. - clang_analyzer_eval(z37->s1[0] == 11); // expected-warning{{TRUE}} + clang_analyzer_eval(z37->s1[0] == 11); // expected-warning{{UNKNOWN}} clang_analyzer_eval(z37->s1[1] == 1); // expected-warning{{UNKNOWN}} clang_analyzer_eval(z37->s1[2] == 1); // expected-warning{{UNKNOWN}} clang_analyzer_eval(z37->s1[3] == 1); // expected-warning{{UNKNOWN}} @@ -865,7 +865,7 @@ clang_analyzer_eval(z38->s1[0] == 1); // expected-warning{{UNKNOWN}} clang_analyzer_eval(z38->s1[1] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(z38->s1[2] == 11); // expected-warning{{TRUE}} + clang_analyzer_eval(z38->s1[2] == 11); // expected-warning{{UNKNOWN}} clang_analyzer_eval(z38->s1[3] == 1); // expected-warning{{UNKNOWN}} clang_analyzer_eval(z38->s2 == 10); // expected-warning{{UNKNOWN}} @@ -912,7 +912,7 @@ // FIXME: d39->s2 should evaluate to at least UNKNOWN or FALSE, // 'collectSubRegionBindings(...)' in RegionStore.cpp will need to // handle a regions' upper boundary overflowing. - clang_analyzer_eval(d39->s2 == 10); // expected-warning{{TRUE}} + clang_analyzer_eval(d39->s2 == 10); // expected-warning{{UNKNOWN}} return 0; } Index: clang/test/Analysis/pr37802.cpp =================================================================== --- clang/test/Analysis/pr37802.cpp +++ clang/test/Analysis/pr37802.cpp @@ -1,7 +1,5 @@ // RUN: %clang_analyze_cc1 -w -analyzer-checker=core -verify %s -// expected-no-diagnostics - typedef __typeof(sizeof(int)) size_t; void *operator new(size_t, void *h) { return h; } @@ -25,7 +23,7 @@ template void f(T &&); void f(J t) { - f(*t.p); + f(*t.p); // expected-warning{{function call argument is an uninitialized value}} } }; class Z { @@ -73,7 +71,7 @@ public: void boolf(bool &&); void f(J &&); - void f(J t) { boolf(*t.p); } + void f(J t) { boolf(*t.p); } // expected-warning{{Dereference of undefined pointer}} }; class Z { Index: clang/test/Analysis/store-extent-rework.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/store-extent-rework.cpp @@ -0,0 +1,159 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection,alpha.unix.cstring -verify %s + +template +void clang_analyzer_eval(T); + +template +void clang_analyzer_explain(T); + +template +void clang_analyzer_dump(T); + +typedef __typeof__(sizeof(int)) size_t; +void *memset(void *dest, int ch, size_t count); + +namespace multipleDefaults { +struct Point +{ + int x; + int y; + int z; +}; + +struct Circle +{ + Point origin; + int size; +}; + +Point makePoint(int x, int y) +{ + Point result; + result.x = x; + result.y = y; + result.z = 3; + return result; +} + +void multipleDefaultOnStack() { + Circle testObj; + memset(&testObj, 0, sizeof(testObj)); + + clang_analyzer_eval(testObj.size == 0); //expected-warning{{TRUE}} + testObj.origin = makePoint(1, 2); + clang_analyzer_eval(testObj.size == 0); //expected-warning{{TRUE}} + + clang_analyzer_eval(testObj.origin.x == 1); //expected-warning{{TRUE}} + clang_analyzer_eval(testObj.origin.y == 2); //expected-warning{{TRUE}} + clang_analyzer_eval(testObj.origin.z == 3); //expected-warning{{TRUE}} +} +} // namespace multipleDefaults + +namespace multipleCovered { +struct S { + char a; + char b; + char c; + char d; +}; + +void everyByteInitialized() { + S s = {1, 2, 3, 4}; + int x = *((int *)&s); + + // FIXME: Every byte inside 'x' is initialized, we just can't concatenate them. + clang_analyzer_eval(x); //expected-warning{{UNKNOWN}} +} + +void garbageByte1() { + S s; + s.b = 2; + s.c = 3; + s.d = 4; + + // Here one byte of 'x' is a garbage value. + int x = *((int *)&s); //expected-warning{{Assigned value is garbage or undefined}} + (void) x; +} + +void garbageByte2() { + S s; + s.a = 1; + s.c = 3; + s.d = 4; + + int x = *((int *)&s); //expected-warning{{Assigned value is garbage or undefined}} + (void) x; +} + +void garbageByte3() { + S s; + s.a = 1; + s.b = 2; + s.d = 4; + + int x = *((int *)&s); //expected-warning{{Assigned value is garbage or undefined}} + (void) x; +} + +void garbageByte4() { + S s; + s.a = 1; + s.b = 2; + s.c = 3; + + int x = *((int *)&s); //expected-warning{{Assigned value is garbage or undefined}} + (void) x; +} +} // namespace multipleCovered + +namespace Overlapping { +void overlapping1() { + int x = 0x01020304; + *((char *)&x + 1) = 1; + + // FIXME: Should be 0x01020301 + clang_analyzer_eval(x); //expected-warning{{UNKNOWN}} + + // FIXME: Should be 1. + clang_analyzer_eval(*((char *)&x + 1)); //expected-warning{{UNKNOWN}} +} + +void overlapping2() { + int x = 0x01020304; + *(char *)&x = 0; + + // FIXME: Should be 0. + clang_analyzer_eval(*(char *)&x); //expected-warning{{UNKNOWN}} + + // FIXME: Should be 0x01020300. + clang_analyzer_eval(x); //expected-warning{{UNKNOWN}} +} +} // namespace Overlapping + + namespace PartialLCV { + struct A { + int x; + int y; + }; + + struct B { + int z; + A a; + }; + + struct C { + B b; + int x; + }; + + void top() { + C c{}; // bind zero default + B b{1, {2, 3}}; + c.b = b; + A a = c.b.a; + + clang_analyzer_dump(a.x); //expected-warning{{2}} + } + +} // namespace PartialLCV Index: clang/test/Analysis/uninit-structured-binding-array.cpp =================================================================== --- clang/test/Analysis/uninit-structured-binding-array.cpp +++ clang/test/Analysis/uninit-structured-binding-array.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -std=c++17 -verify %s void clang_analyzer_eval(bool); @@ -7,7 +7,7 @@ auto [a, b] = arr; arr[0] = 0; - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} } void array_value_b(void) { @@ -16,8 +16,6 @@ clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} clang_analyzer_eval(b == 2); // expected-warning{{TRUE}} - - int x = a; // no-warning } void array_value_c(void) { @@ -28,9 +26,7 @@ auto [a, b, c] = arr; clang_analyzer_eval(b == arr[1]); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} } void array_value_d(void) { @@ -41,9 +37,7 @@ auto [a, b, c] = arr; clang_analyzer_eval(b == arr[1]); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = c; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} } void array_value_e(void) { @@ -56,9 +50,6 @@ clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} clang_analyzer_eval(j == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // no-warning } void array_value_f(void) { @@ -70,15 +61,15 @@ auto [i, j] = uninit; clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void array_lref_a(void) { int arr[2]; auto &[a, b] = arr; - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} } void array_lref_b(void) { @@ -87,8 +78,6 @@ clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} clang_analyzer_eval(b == 2); // expected-warning{{TRUE}} - - int x = a; // no-warning } void array_lref_c(void) { @@ -98,9 +87,7 @@ arr[0] = 1; clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} - - int x = a; // no-warning - int y = b; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} } void array_lref_d(void) { @@ -111,9 +98,7 @@ auto &[a, b, c] = arr; clang_analyzer_eval(b == 1); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} } void array_lref_e(void) { @@ -124,9 +109,7 @@ auto &[a, b, c] = arr; clang_analyzer_eval(b == 1); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = c; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} } void array_lref_f(void) { @@ -139,9 +122,6 @@ clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} clang_analyzer_eval(j == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // no-warning } void array_lref_g(void) { @@ -153,15 +133,14 @@ auto &[i, j] = uninit; clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void array_rref_a(void) { int arr[2]; auto &&[a, b] = arr; - int x = a; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} } void array_rref_b(void) { @@ -181,9 +160,7 @@ arr[0] = 1; clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} - - int x = a; // no-warning - int y = b; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} } void array_rref_d(void) { @@ -194,22 +171,8 @@ auto &&[a, b, c] = arr; clang_analyzer_eval(b == 1); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = a; // expected-warning{{Assigned value is garbage or undefined}} -} - -void array_rref_e(void) { - int arr[3]; - - arr[1] = 1; - - auto &&[a, b, c] = arr; - - clang_analyzer_eval(b == 1); // expected-warning{{TRUE}} - - int y = b; // no-warning - int x = c; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} } void array_rref_f(void) { @@ -222,9 +185,6 @@ clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} clang_analyzer_eval(j == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // no-warning } void array_rref_g(void) { @@ -236,9 +196,7 @@ auto &&[i, j] = uninit; clang_analyzer_eval(i == 0); // expected-warning{{TRUE}} - - int a = i; // no-warning - int b = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void array_change_a(void) { @@ -276,7 +234,11 @@ auto [a, b, c, d, e] = arr; - int x = e; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(d); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(e); // expected-warning{{UNDEFINED}} } void array_big_a(void) { @@ -284,13 +246,12 @@ auto [a, b, c, d, e, f] = arr; - // FIXME: These will be Undefined when we handle reading Undefined values from lazyCompoundVal. - clang_analyzer_eval(a == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c == 3); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(d == 4); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(e == 5); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(f == 6); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(b); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(c); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(d); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(e); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(f); // expected-warning{{UNDEFINED}} } struct S { Index: clang/test/Analysis/uninit-structured-binding-struct.cpp =================================================================== --- clang/test/Analysis/uninit-structured-binding-struct.cpp +++ clang/test/Analysis/uninit-structured-binding-struct.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -std=c++17 -verify %s void clang_analyzer_eval(bool); @@ -12,7 +12,8 @@ auto [i, j] = tst; - int x = i; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(i); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void b(void) { @@ -22,7 +23,7 @@ auto [i, j] = tst; clang_analyzer_eval(i == 1); // expected-warning{{TRUE}} - int y = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void c(void) { @@ -30,7 +31,8 @@ auto &[i, j] = tst; - int x = i; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(i); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void d(void) { @@ -43,7 +45,7 @@ i = 2; clang_analyzer_eval(tst.a == 2); // expected-warning{{TRUE}} - int y = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void e(void) { @@ -63,7 +65,8 @@ auto &&[i, j] = tst; - int x = i; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(i); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } void g(void) { @@ -73,7 +76,7 @@ auto &&[i, j] = tst; clang_analyzer_eval(i == 1); // expected-warning{{TRUE}} - int y = j; // expected-warning{{Assigned value is garbage or undefined}} + clang_analyzer_eval(j); // expected-warning{{UNDEFINED}} } struct s2 { @@ -93,10 +96,8 @@ auto [i, j] = tst; - // FIXME: These should be undefined, but we have to fix - // reading undefined from lazy compound values first. - clang_analyzer_eval(i.a); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(i.b); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i.a); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(i.b); // expected-warning{{UNDEFINED}} clang_analyzer_eval(j.a == 1); // expected-warning{{TRUE}} clang_analyzer_eval(j.b == 2); // expected-warning{{TRUE}} Index: clang/test/Analysis/uninit-vals.m =================================================================== --- clang/test/Analysis/uninit-vals.m +++ clang/test/Analysis/uninit-vals.m @@ -164,15 +164,11 @@ // expected-note@-1{{TRUE}} testObj->origin = makePoint(0.0, 0.0); - if (testObj->size > 0) { ; } // expected-note{{Assuming field 'size' is <= 0}} + if (testObj->size > 0) { ; } // expected-note{{Field 'size' is <= 0}} // expected-note@-1{{Taking false branch}} - // FIXME: Assigning to 'testObj->origin' kills the default binding for the - // whole region, meaning that we've forgotten that testObj->size should also - // default to 0. Tracked by . - // This should be TRUE. - clang_analyzer_eval(testObj->size == 0); // expected-warning{{UNKNOWN}} - // expected-note@-1{{UNKNOWN}} + clang_analyzer_eval(testObj->size == 0); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} free(testObj); } @@ -219,21 +215,17 @@ // expected-note@-1{{TRUE}} testObj->origin = makeIntPoint(1, 2); - if (testObj->size > 0) { ; } // expected-note{{Assuming field 'size' is <= 0}} + if (testObj->size > 0) { ; } // expected-note{{Field 'size' is <= 0}} // expected-note@-1{{Taking false branch}} - // expected-note@-2{{Assuming field 'size' is <= 0}} + // expected-note@-2{{Field 'size' is <= 0}} // expected-note@-3{{Taking false branch}} - // expected-note@-4{{Assuming field 'size' is <= 0}} + // expected-note@-4{{Field 'size' is <= 0}} // expected-note@-5{{Taking false branch}} - // expected-note@-6{{Assuming field 'size' is <= 0}} + // expected-note@-6{{Field 'size' is <= 0}} // expected-note@-7{{Taking false branch}} - // FIXME: Assigning to 'testObj->origin' kills the default binding for the - // whole region, meaning that we've forgotten that testObj->size should also - // default to 0. Tracked by . - // This should be TRUE. - clang_analyzer_eval(testObj->size == 0); // expected-warning{{UNKNOWN}} - // expected-note@-1{{UNKNOWN}} + clang_analyzer_eval(testObj->size == 0); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} clang_analyzer_eval(testObj->origin.x == 1); // expected-warning{{TRUE}} // expected-note@-1{{TRUE}} clang_analyzer_eval(testObj->origin.y == 2); // expected-warning{{TRUE}} @@ -307,7 +299,8 @@ IntPoint b = a; extern void useInt(int); useInt(b.x); // no-warning - useInt(b.y); // no-warning + useInt(b.y); // expected-warning{{uninitialized}} + // expected-note@-1{{uninitialized}} } void testSmallStructInLargerStruct(void) { Index: clang/utils/analyzer/exploded-graph-rewriter.py =================================================================== --- clang/utils/analyzer/exploded-graph-rewriter.py +++ clang/utils/analyzer/exploded-graph-rewriter.py @@ -186,11 +186,12 @@ # A single binding key in a deserialized RegionStore cluster. class StoreBindingKey: def __init__(self, json_sk): - self.kind = json_sk["kind"] - self.offset = json_sk["offset"] + self.kind = json_sk['kind'] + self.offset = json_sk['offset'] + self.extent = json_sk['extent'] if 'extent' in json_sk else None def _key(self): - return (self.kind, self.offset) + return (self.kind, self.offset, self.extent) def __eq__(self, other): return self._key() == other._key()