ASDenysPetrov updated this revision to Diff 298388.
ASDenysPetrov added a comment.
Updated. Moved `castRegion()` to `SValBuilder`.
Actually it wasn't so hard as I thought :)
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D89055/new/
https://reviews.llvm.org/D89055
Files:
clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
clang/lib/StaticAnalyzer/Core/Store.cpp
clang/test/Analysis/casts.c
clang/test/Analysis/string.c
Index: clang/test/Analysis/string.c
===================================================================
--- clang/test/Analysis/string.c
+++ clang/test/Analysis/string.c
@@ -363,6 +363,14 @@
strcpy(x, y); // no-warning
}
+// PR37503
+void *get_void_ptr();
+char ***type_punned_ptr;
+void strcpy_no_assertion(char c) {
+ *(unsigned char **)type_punned_ptr = (unsigned char *)(get_void_ptr());
+ strcpy(**type_punned_ptr, &c); // no-crash
+}
+
//===----------------------------------------------------------------------===
// stpcpy()
//===----------------------------------------------------------------------===
Index: clang/test/Analysis/casts.c
===================================================================
--- clang/test/Analysis/casts.c
+++ clang/test/Analysis/casts.c
@@ -245,3 +245,8 @@
return a * a;
}
+void no_crash_reinterpret_char_as_uchar(char ***a, int *b) {
+ *(unsigned char **)a = (unsigned char *)b;
+ if (**a == 0) // no-crash
+ ;
+}
Index: clang/lib/StaticAnalyzer/Core/Store.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/Store.cpp
+++ clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -57,13 +57,6 @@
return Store;
}
-const ElementRegion *StoreManager::MakeElementRegion(const SubRegion *Base,
- QualType EleTy,
- uint64_t index) {
- NonLoc idx = svalBuilder.makeArrayIndex(index);
- return MRMgr.getElementRegion(EleTy, idx, Base, svalBuilder.getContext());
-}
-
const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R,
QualType T) {
NonLoc idx = svalBuilder.makeZeroArrayIndex();
@@ -71,161 +64,6 @@
return MRMgr.getElementRegion(T, idx, R, Ctx);
}
-const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) {
- ASTContext &Ctx = StateMgr.getContext();
-
- // Handle casts to Objective-C objects.
- if (CastToTy->isObjCObjectPointerType())
- return R->StripCasts();
-
- if (CastToTy->isBlockPointerType()) {
- // FIXME: We may need different solutions, depending on the symbol
- // involved. Blocks can be casted to/from 'id', as they can be treated
- // as Objective-C objects. This could possibly be handled by enhancing
- // our reasoning of downcasts of symbolic objects.
- if (isa<CodeTextRegion>(R) || isa<SymbolicRegion>(R))
- return R;
-
- // We don't know what to make of it. Return a NULL region, which
- // will be interpreted as UnknownVal.
- return nullptr;
- }
-
- // Now assume we are casting from pointer to pointer. Other cases should
- // already be handled.
- QualType PointeeTy = CastToTy->getPointeeType();
- QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
-
- // Handle casts to void*. We just pass the region through.
- if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy)
- return R;
-
- // Handle casts from compatible types.
- if (R->isBoundable())
- if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
- QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
- if (CanonPointeeTy == ObjTy)
- return R;
- }
-
- // Process region cast according to the kind of the region being cast.
- switch (R->getKind()) {
- case MemRegion::CXXThisRegionKind:
- case MemRegion::CodeSpaceRegionKind:
- case MemRegion::StackLocalsSpaceRegionKind:
- case MemRegion::StackArgumentsSpaceRegionKind:
- case MemRegion::HeapSpaceRegionKind:
- case MemRegion::UnknownSpaceRegionKind:
- case MemRegion::StaticGlobalSpaceRegionKind:
- case MemRegion::GlobalInternalSpaceRegionKind:
- case MemRegion::GlobalSystemSpaceRegionKind:
- case MemRegion::GlobalImmutableSpaceRegionKind: {
- llvm_unreachable("Invalid region cast");
- }
-
- case MemRegion::FunctionCodeRegionKind:
- case MemRegion::BlockCodeRegionKind:
- case MemRegion::BlockDataRegionKind:
- case MemRegion::StringRegionKind:
- // FIXME: Need to handle arbitrary downcasts.
- case MemRegion::SymbolicRegionKind:
- case MemRegion::AllocaRegionKind:
- case MemRegion::CompoundLiteralRegionKind:
- case MemRegion::FieldRegionKind:
- case MemRegion::ObjCIvarRegionKind:
- case MemRegion::ObjCStringRegionKind:
- case MemRegion::NonParamVarRegionKind:
- case MemRegion::ParamVarRegionKind:
- case MemRegion::CXXTempObjectRegionKind:
- case MemRegion::CXXBaseObjectRegionKind:
- case MemRegion::CXXDerivedObjectRegionKind:
- return MakeElementRegion(cast<SubRegion>(R), PointeeTy);
-
- case MemRegion::ElementRegionKind: {
- // If we are casting from an ElementRegion to another type, the
- // algorithm is as follows:
- //
- // (1) Compute the "raw offset" of the ElementRegion from the
- // base region. This is done by calling 'getAsRawOffset()'.
- //
- // (2a) If we get a 'RegionRawOffset' after calling
- // 'getAsRawOffset()', determine if the absolute offset
- // can be exactly divided into chunks of the size of the
- // casted-pointee type. If so, create a new ElementRegion with
- // the pointee-cast type as the new ElementType and the index
- // being the offset divded by the chunk size. If not, create
- // a new ElementRegion at offset 0 off the raw offset region.
- //
- // (2b) If we don't a get a 'RegionRawOffset' after calling
- // 'getAsRawOffset()', it means that we are at offset 0.
- //
- // FIXME: Handle symbolic raw offsets.
-
- const ElementRegion *elementR = cast<ElementRegion>(R);
- const RegionRawOffset &rawOff = elementR->getAsArrayOffset();
- const MemRegion *baseR = rawOff.getRegion();
-
- // If we cannot compute a raw offset, throw up our hands and return
- // a NULL MemRegion*.
- if (!baseR)
- return nullptr;
-
- CharUnits off = rawOff.getOffset();
-
- if (off.isZero()) {
- // Edge case: we are at 0 bytes off the beginning of baseR. We
- // check to see if type we are casting to is the same as the base
- // region. If so, just return the base region.
- if (const auto *TR = dyn_cast<TypedValueRegion>(baseR)) {
- QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
- QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
- if (CanonPointeeTy == ObjTy)
- return baseR;
- }
-
- // Otherwise, create a new ElementRegion at offset 0.
- return MakeElementRegion(cast<SubRegion>(baseR), PointeeTy);
- }
-
- // We have a non-zero offset from the base region. We want to determine
- // if the offset can be evenly divided by sizeof(PointeeTy). If so,
- // we create an ElementRegion whose index is that value. Otherwise, we
- // create two ElementRegions, one that reflects a raw offset and the other
- // that reflects the cast.
-
- // Compute the index for the new ElementRegion.
- int64_t newIndex = 0;
- const MemRegion *newSuperR = nullptr;
-
- // We can only compute sizeof(PointeeTy) if it is a complete type.
- if (!PointeeTy->isIncompleteType()) {
- // Compute the size in **bytes**.
- CharUnits pointeeTySize = Ctx.getTypeSizeInChars(PointeeTy);
- if (!pointeeTySize.isZero()) {
- // Is the offset a multiple of the size? If so, we can layer the
- // ElementRegion (with elementType == PointeeTy) directly on top of
- // the base region.
- if (off % pointeeTySize == 0) {
- newIndex = off / pointeeTySize;
- newSuperR = baseR;
- }
- }
- }
-
- if (!newSuperR) {
- // Create an intermediate ElementRegion to represent the raw byte.
- // This will be the super region of the final ElementRegion.
- newSuperR = MakeElementRegion(cast<SubRegion>(baseR), Ctx.CharTy,
- off.getQuantity());
- }
-
- return MakeElementRegion(cast<SubRegion>(newSuperR), PointeeTy, newIndex);
- }
- }
-
- llvm_unreachable("unreachable");
-}
-
static bool regionMatchesCXXRecordType(SVal V, QualType Ty) {
const MemRegion *MR = V.getAsRegion();
if (!MR)
@@ -418,21 +256,6 @@
return UnknownVal();
}
- // When retrieving symbolic pointer and expecting a non-void pointer,
- // wrap them into element regions of the expected type if necessary.
- // SValBuilder::dispatchCast() doesn't do that, but it is necessary to
- // make sure that the retrieved value makes sense, because there's no other
- // cast in the AST that would tell us to cast it to the correct pointer type.
- // We might need to do that for non-void pointers as well.
- // FIXME: We really need a single good function to perform casts for us
- // correctly every time we need it.
- if (castTy->isPointerType() && !castTy->isVoidPointerType())
- if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(V.getAsRegion())) {
- QualType sr = SR->getSymbol()->getType();
- if (!hasSameUnqualifiedPointeeType(sr, castTy))
- return loc::MemRegionVal(castRegion(SR, castTy));
- }
-
return svalBuilder.dispatchCast(V, castTy);
}
Index: clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -65,8 +65,34 @@
// Transfer function for Casts.
//===----------------------------------------------------------------------===//
+static bool hasSameUnqualifiedPointeeType(QualType ty1, QualType ty2) {
+ return ty1->getPointeeType().getCanonicalType().getTypePtr() ==
+ ty2->getPointeeType().getCanonicalType().getTypePtr();
+}
+
SVal SimpleSValBuilder::dispatchCast(SVal Val, QualType CastTy) {
assert(Val.getAs<Loc>() || Val.getAs<NonLoc>());
+
+ // When retrieving symbolic pointer and expecting a non-void pointer,
+ // wrap them into element regions of the expected type if necessary.
+ // it is necessary to make sure that the retrieved value makes sense,
+ // because there's no other cast in the AST that would tell us to cast
+ // it to the correct pointer type. We might need to do that for non-void
+ // pointers as well.
+ // FIXME: We really need a single good function to perform casts for us
+ // correctly every time we need it.
+ if (CastTy->isPointerType() && !CastTy->isVoidPointerType()) {
+ if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(Val.getAsRegion())) {
+ QualType sr = SR->getSymbol()->getType();
+ if (!hasSameUnqualifiedPointeeType(sr, CastTy))
+ return loc::MemRegionVal(castRegion(SR, CastTy));
+ }
+ // Next fixes pointer dereference using type different from its initial
+ // one. See PR37503 for details.
+ if (const auto *ER = dyn_cast_or_null<ElementRegion>(Val.getAsRegion()))
+ return loc::MemRegionVal(castRegion(ER, CastTy));
+ }
+
return Val.getAs<Loc>() ? evalCastFromLoc(Val.castAs<Loc>(), CastTy)
: evalCastFromNonLoc(Val.castAs<NonLoc>(), CastTy);
}
@@ -135,7 +161,7 @@
// can be introduced by the frontend for corner cases, e.g
// casting from va_list* to __builtin_va_list&.
//
- if (Loc::isLocType(castTy) || castTy->isReferenceType())
+ if (Loc::isLocType(castTy))
return val;
// FIXME: Handle transparent unions where a value can be "transparently"
Index: clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -530,6 +530,169 @@
return evalCast(val, castTy, originalTy);
}
+const ElementRegion *SValBuilder::MakeElementRegion(const SubRegion *Base,
+ QualType EleTy,
+ uint64_t index /*= 0*/) {
+ NonLoc idx = makeArrayIndex(index);
+ return MemMgr.getElementRegion(EleTy, idx, Base, getContext());
+}
+
+const MemRegion *SValBuilder::castRegion(const MemRegion *R,
+ QualType CastToTy) {
+ ASTContext &Ctx = getContext();
+
+ // Handle casts to Objective-C objects.
+ if (CastToTy->isObjCObjectPointerType())
+ return R->StripCasts();
+
+ if (CastToTy->isBlockPointerType()) {
+ // FIXME: We may need different solutions, depending on the symbol
+ // involved. Blocks can be casted to/from 'id', as they can be treated
+ // as Objective-C objects. This could possibly be handled by enhancing
+ // our reasoning of downcasts of symbolic objects.
+ if (isa<CodeTextRegion>(R) || isa<SymbolicRegion>(R))
+ return R;
+
+ // We don't know what to make of it. Return a NULL region, which
+ // will be interpreted as UnknownVal.
+ return nullptr;
+ }
+
+ // Now assume we are casting from pointer to pointer. Other cases should
+ // already be handled.
+ QualType PointeeTy = CastToTy->getPointeeType();
+ QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
+
+ // Handle casts to void*. We just pass the region through.
+ if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy)
+ return R;
+
+ // Handle casts from compatible types.
+ if (R->isBoundable())
+ if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
+ QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
+ if (CanonPointeeTy == ObjTy)
+ return R;
+ }
+
+ // Process region cast according to the kind of the region being cast.
+ switch (R->getKind()) {
+ case MemRegion::CXXThisRegionKind:
+ case MemRegion::CodeSpaceRegionKind:
+ case MemRegion::StackLocalsSpaceRegionKind:
+ case MemRegion::StackArgumentsSpaceRegionKind:
+ case MemRegion::HeapSpaceRegionKind:
+ case MemRegion::UnknownSpaceRegionKind:
+ case MemRegion::StaticGlobalSpaceRegionKind:
+ case MemRegion::GlobalInternalSpaceRegionKind:
+ case MemRegion::GlobalSystemSpaceRegionKind:
+ case MemRegion::GlobalImmutableSpaceRegionKind: {
+ llvm_unreachable("Invalid region cast");
+ }
+
+ case MemRegion::FunctionCodeRegionKind:
+ case MemRegion::BlockCodeRegionKind:
+ case MemRegion::BlockDataRegionKind:
+ case MemRegion::StringRegionKind:
+ // FIXME: Need to handle arbitrary downcasts.
+ case MemRegion::SymbolicRegionKind:
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::CompoundLiteralRegionKind:
+ case MemRegion::FieldRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
+ case MemRegion::ObjCStringRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
+ case MemRegion::CXXTempObjectRegionKind:
+ case MemRegion::CXXBaseObjectRegionKind:
+ case MemRegion::CXXDerivedObjectRegionKind:
+ return MakeElementRegion(cast<SubRegion>(R), PointeeTy);
+
+ case MemRegion::ElementRegionKind: {
+ // If we are casting from an ElementRegion to another type, the
+ // algorithm is as follows:
+ //
+ // (1) Compute the "raw offset" of the ElementRegion from the
+ // base region. This is done by calling 'getAsRawOffset()'.
+ //
+ // (2a) If we get a 'RegionRawOffset' after calling
+ // 'getAsRawOffset()', determine if the absolute offset
+ // can be exactly divided into chunks of the size of the
+ // casted-pointee type. If so, create a new ElementRegion with
+ // the pointee-cast type as the new ElementType and the index
+ // being the offset divded by the chunk size. If not, create
+ // a new ElementRegion at offset 0 off the raw offset region.
+ //
+ // (2b) If we don't a get a 'RegionRawOffset' after calling
+ // 'getAsRawOffset()', it means that we are at offset 0.
+ //
+ // FIXME: Handle symbolic raw offsets.
+
+ const ElementRegion *elementR = cast<ElementRegion>(R);
+ const RegionRawOffset &rawOff = elementR->getAsArrayOffset();
+ const MemRegion *baseR = rawOff.getRegion();
+
+ // If we cannot compute a raw offset, throw up our hands and return
+ // a NULL MemRegion*.
+ if (!baseR)
+ return nullptr;
+
+ CharUnits off = rawOff.getOffset();
+
+ if (off.isZero()) {
+ // Edge case: we are at 0 bytes off the beginning of baseR. We
+ // check to see if type we are casting to is the same as the base
+ // region. If so, just return the base region.
+ if (const auto *TR = dyn_cast<TypedValueRegion>(baseR)) {
+ QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
+ QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
+ if (CanonPointeeTy == ObjTy)
+ return baseR;
+ }
+
+ // Otherwise, create a new ElementRegion at offset 0.
+ return MakeElementRegion(cast<SubRegion>(baseR), PointeeTy);
+ }
+
+ // We have a non-zero offset from the base region. We want to determine
+ // if the offset can be evenly divided by sizeof(PointeeTy). If so,
+ // we create an ElementRegion whose index is that value. Otherwise, we
+ // create two ElementRegions, one that reflects a raw offset and the other
+ // that reflects the cast.
+
+ // Compute the index for the new ElementRegion.
+ int64_t newIndex = 0;
+ const MemRegion *newSuperR = nullptr;
+
+ // We can only compute sizeof(PointeeTy) if it is a complete type.
+ if (!PointeeTy->isIncompleteType()) {
+ // Compute the size in **bytes**.
+ CharUnits pointeeTySize = Ctx.getTypeSizeInChars(PointeeTy);
+ if (!pointeeTySize.isZero()) {
+ // Is the offset a multiple of the size? If so, we can layer the
+ // ElementRegion (with elementType == PointeeTy) directly on top of
+ // the base region.
+ if (off % pointeeTySize == 0) {
+ newIndex = off / pointeeTySize;
+ newSuperR = baseR;
+ }
+ }
+ }
+
+ if (!newSuperR) {
+ // Create an intermediate ElementRegion to represent the raw byte.
+ // This will be the super region of the final ElementRegion.
+ newSuperR = MakeElementRegion(cast<SubRegion>(baseR), Ctx.CharTy,
+ off.getQuantity());
+ }
+
+ return MakeElementRegion(cast<SubRegion>(newSuperR), PointeeTy, newIndex);
+ }
+ }
+
+ llvm_unreachable("unreachable");
+}
+
// FIXME: should rewrite according to the cast kind.
SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
castTy = Context.getCanonicalType(castTy);
@@ -574,8 +737,7 @@
if (Loc::isLocType(castTy) && originalTy->isIntegralOrEnumerationType()) {
if (Optional<nonloc::LocAsInteger> LV = val.getAs<nonloc::LocAsInteger>()) {
if (const MemRegion *R = LV->getLoc().getAsRegion()) {
- StoreManager &storeMgr = StateMgr.getStoreManager();
- R = storeMgr.castRegion(R, castTy);
+ R = castRegion(R, castTy);
return R ? SVal(loc::MemRegionVal(R)) : UnknownVal();
}
return LV->getLoc();
@@ -650,12 +812,10 @@
assert(Loc::isLocType(originalTy) || originalTy->isFunctionType() ||
originalTy->isBlockPointerType() || castTy->isReferenceType());
- StoreManager &storeMgr = StateMgr.getStoreManager();
-
- // Delegate to store manager to get the result of casting a region to a
- // different type. If the MemRegion* returned is NULL, this expression
- // Evaluates to UnknownVal.
- R = storeMgr.castRegion(R, castTy);
+ // Get the result of casting a region to a different type.
+ // If the MemRegion* returned is NULL, this expression evaluates to
+ // UnknownVal.
+ R = castRegion(R, castTy);
return R ? SVal(loc::MemRegionVal(R)) : UnknownVal();
}
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
@@ -178,11 +178,6 @@
const ElementRegion *GetElementZeroRegion(const SubRegion *R, QualType T);
- /// castRegion - Used by ExprEngine::VisitCast to handle casts from
- /// a MemRegion* to a specific location type. 'R' is the region being
- /// casted and 'CastToTy' the result type of the cast.
- const MemRegion *castRegion(const MemRegion *region, QualType CastToTy);
-
virtual StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx,
SymbolReaper &SymReaper) = 0;
@@ -276,9 +271,6 @@
virtual void iterBindings(Store store, BindingsHandler& f) = 0;
protected:
- const ElementRegion *MakeElementRegion(const SubRegion *baseRegion,
- QualType pointeeTy,
- uint64_t index = 0);
/// CastRetrievedVal - Used by subclasses of StoreManager to implement
/// implicit casts that arise from loads from regions that are reinterpreted
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
@@ -79,6 +79,12 @@
// FIXME: Make these protected again once RegionStoreManager correctly
// handles loads from different bound value types.
virtual SVal dispatchCast(SVal val, QualType castTy) = 0;
+ const ElementRegion *MakeElementRegion(const SubRegion *Base, QualType EleTy,
+ uint64_t index = 0);
+ /// castRegion - Used by ExprEngine::VisitCast to handle casts from
+ /// a MemRegion* to a specific location type. 'R' is the region being
+ /// casted and 'CastToTy' the result type of the cast.
+ const MemRegion *castRegion(const MemRegion *R, QualType CastToTy);
public:
SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits