class MemRegionManager;
class MemSpaceRegion;
+//===----------------------------------------------------------------------===//
+// Base region classes.
+//===----------------------------------------------------------------------===//
+
/// MemRegion - The root abstract class for all memory regions.
class MemRegion : public llvm::FoldingSetNode {
public:
}
MemRegionManager* getMemRegionManager() const;
-
+
bool isSubRegionOf(const MemRegion* R) const;
-
+
static bool classof(const MemRegion* R) {
return R->getKind() > MemSpaceRegionKind;
}
};
+
+//===----------------------------------------------------------------------===//
+// Auxillary data classes for use with MemRegions.
+//===----------------------------------------------------------------------===//
+
+class ElementRegion;
+
+class RegionRawOffset : public std::pair<const MemRegion*, int64_t> {
+private:
+ friend class ElementRegion;
+
+ RegionRawOffset(const MemRegion* reg, int64_t offset = 0)
+ : std::pair<const MemRegion*, int64_t>(reg, offset) {}
+
+public:
+ // FIXME: Eventually support symbolic offsets.
+ int64_t getByteOffset() const { return second; }
+ const MemRegion *getRegion() const { return first; }
+
+ void dumpToStream(llvm::raw_ostream& os) const;
+ void dump() const;
+};
+
+//===----------------------------------------------------------------------===//
+// MemRegion subclasses.
+//===----------------------------------------------------------------------===//
/// AllocaRegion - A region that represents an untyped blob of bytes created
/// by a call to 'alloca'.
return ElementType;
}
+ RegionRawOffset getAsRawOffset() const;
+
void dumpToStream(llvm::raw_ostream& os) const;
void Profile(llvm::FoldingSetNodeID& ID) const;
return R->getKind() == ElementRegionKind;
}
};
-
+
template<typename RegionTy>
const RegionTy* MemRegion::getAs() const {
if (const RegionTy* RT = dyn_cast<RegionTy>(this))
/// object).
ObjCIvarRegion* getObjCIvarRegion(const ObjCIvarDecl* ivd,
const MemRegion* superRegion);
+
CodeTextRegion* getCodeTextRegion(SymbolRef sym, QualType t);
CodeTextRegion* getCodeTextRegion(const FunctionDecl* fd, QualType t);
private:
CastResult MakeElementRegion(const GRState *state, const MemRegion *region,
- QualType pointeeTy, QualType castToTy);
+ QualType pointeeTy, QualType castToTy,
+ uint64_t index = 0);
};
// FIXME: Do we still need this?
public:
ValueManager(llvm::BumpPtrAllocator &alloc, ASTContext &context,
GRStateManager &stateMgr)
- : Context(context), BasicVals(Context, alloc),
- SymMgr(Context, BasicVals, alloc),
- MemMgr(Context, alloc), StateMgr(stateMgr),
- ArrayIndexTy(Context.IntTy),
- ArrayIndexWidth(Context.getTypeSize(ArrayIndexTy))
+ : Context(context), BasicVals(context, alloc),
+ SymMgr(context, BasicVals, alloc),
+ MemMgr(context, alloc), StateMgr(stateMgr),
+ ArrayIndexTy(context.IntTy),
+ ArrayIndexWidth(context.getTypeSize(ArrayIndexTy))
{
// FIXME: Generalize later.
SVator.reset(clang::CreateSimpleSValuator(*this));
#include "llvm/Support/raw_ostream.h"
#include "clang/Analysis/PathSensitive/MemRegion.h"
+#include "clang/Analysis/PathSensitive/ValueManager.h"
using namespace clang;
}
void ElementRegion::dumpToStream(llvm::raw_ostream& os) const {
- os << superRegion << '[' << Index << ']';
+ os << "element{" << superRegion << ','
+ << Index << ',' << getElementType().getAsString() << '}';
}
void FieldRegion::dumpToStream(llvm::raw_ostream& os) const {
os << cast<VarDecl>(D)->getNameAsString();
}
+void RegionRawOffset::dump() const {
+ dumpToStream(llvm::errs());
+}
+
+void RegionRawOffset::dumpToStream(llvm::raw_ostream& os) const {
+ os << "raw_offset{" << getRegion() << ',' << getByteOffset() << '}';
+}
+
//===----------------------------------------------------------------------===//
// MemRegionManager methods.
//===----------------------------------------------------------------------===//
-
+
MemSpaceRegion* MemRegionManager::LazyAllocate(MemSpaceRegion*& region) {
if (!region) {
region = (MemSpaceRegion*) A.Allocate<MemSpaceRegion>();
return getRegion<AllocaRegion>(E, cnt);
}
-
const MemSpaceRegion *MemRegion::getMemorySpace() const {
const MemRegion *R = this;
const SubRegion* SR = dyn_cast<SubRegion>(this);
// want to strip away ElementRegions, however, where the index is 0.
SVal index = ER->getIndex();
if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) {
- if (CI->getValue().getZExtValue() == 0) {
+ if (CI->getValue().getSExtValue() == 0) {
R = ER->getSuperRegion();
continue;
}
}
return R;
}
+
+// FIXME: Merge with the implementation of the same method in Store.cpp
+static bool IsCompleteType(ASTContext &Ctx, QualType Ty) {
+ if (const RecordType *RT = Ty->getAs<RecordType>()) {
+ const RecordDecl *D = RT->getDecl();
+ if (!D->getDefinition(Ctx))
+ return false;
+ }
+
+ return true;
+}
+
+RegionRawOffset ElementRegion::getAsRawOffset() const {
+ int64_t offset = 0;
+ const ElementRegion *ER = this;
+ const MemRegion *superR = NULL;
+ ASTContext &C = getContext();
+
+ // FIXME: Handle multi-dimensional arrays.
+
+ while (ER) {
+ superR = ER->getSuperRegion();
+
+ // FIXME: generalize to symbolic offsets.
+ SVal index = ER->getIndex();
+ if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) {
+ // Update the offset.
+ int64_t i = CI->getValue().getSExtValue();
+
+ if (i != 0) {
+ QualType elemType = ER->getElementType();
+
+ // If we are pointing to an incomplete type, go no further.
+ if (!IsCompleteType(C, elemType)) {
+ superR = ER;
+ break;
+ }
+
+ int64_t size = (int64_t) (C.getTypeSize(elemType) / 8);
+ offset += (i * size);
+ }
+
+ // Go to the next ElementRegion (if any).
+ ER = dyn_cast<ElementRegion>(superR);
+ continue;
+ }
+
+ return NULL;
+ }
+
+ assert(superR && "super region cannot be NULL");
+ return RegionRawOffset(superR, offset);
+}
+
// This GDM entry tracks what regions have a default value if they have no bound
// value and have not been killed.
//
-namespace { class VISIBILITY_HIDDEN RegionDefaultValue {}; }
+namespace {
+class VISIBILITY_HIDDEN RegionDefaultValue {
+public:
+ typedef llvm::ImmutableMap<const MemRegion*, SVal> MapTy;
+};
+}
static int RegionDefaultValueIndex = 0;
namespace clang {
template<> struct GRStateTrait<RegionDefaultValue>
- : public GRStatePartialTrait<llvm::ImmutableMap<const MemRegion*, SVal> > {
+ : public GRStatePartialTrait<RegionDefaultValue::MapTy> {
static void* GDMIndex() { return &RegionDefaultValueIndex; }
};
}
+//===----------------------------------------------------------------------===//
+// Utility functions.
+//===----------------------------------------------------------------------===//
+
+static bool IsAnyPointerOrIntptr(QualType ty, ASTContext &Ctx) {
+ if (ty->isAnyPointerType())
+ return true;
+
+ return ty->isIntegerType() && ty->isScalarType() &&
+ Ctx.getTypeSize(ty) == Ctx.getTypeSize(Ctx.VoidPtrTy);
+}
+
//===----------------------------------------------------------------------===//
// Main RegionStore logic.
//===----------------------------------------------------------------------===//
namespace {
-
-class VISIBILITY_HIDDEN RegionStoreSubRegionMap : public SubRegionMap {
- typedef llvm::DenseMap<const MemRegion*,
- llvm::ImmutableSet<const MemRegion*> > Map;
- llvm::ImmutableSet<const MemRegion*>::Factory F;
+class VISIBILITY_HIDDEN RegionStoreSubRegionMap : public SubRegionMap {
+ typedef llvm::ImmutableSet<const MemRegion*> SetTy;
+ typedef llvm::DenseMap<const MemRegion*, SetTy> Map;
+ SetTy::Factory F;
Map M;
-
public:
void add(const MemRegion* Parent, const MemRegion* SubRegion) {
Map::iterator I = M.find(Parent);
return true;
}
+
+ typedef SetTy::iterator iterator;
+
+ std::pair<iterator, iterator> begin_end(const MemRegion *R) {
+ Map::iterator I = M.find(R);
+ SetTy S = I == M.end() ? F.GetEmptySet() : I->second;
+ return std::make_pair(S.begin(), S.end());
+ }
};
class VISIBILITY_HIDDEN RegionStoreManager : public StoreManager {
virtual ~RegionStoreManager() {}
- SubRegionMap* getSubRegionMap(const GRState *state);
+ SubRegionMap *getSubRegionMap(const GRState *state);
+
+ RegionStoreSubRegionMap *getRegionStoreSubRegionMap(const GRState *state);
/// getLValueString - Returns an SVal representing the lvalue of a
/// StringLiteral. Within RegionStore a StringLiteral has an
const GRState *InvalidateRegion(const GRState *state, const MemRegion *R,
const Expr *E, unsigned Count);
+private:
+ RegionBindingsTy RemoveSubRegionBindings(RegionBindingsTy B,
+ const MemRegion *R,
+ RegionStoreSubRegionMap &M);
+
+public:
const GRState *Bind(const GRState *state, Loc LV, SVal V);
const GRState *BindCompoundLiteral(const GRState *state,
return new RegionStoreManager(StMgr, F);
}
-SubRegionMap* RegionStoreManager::getSubRegionMap(const GRState *state) {
+RegionStoreSubRegionMap*
+RegionStoreManager::getRegionStoreSubRegionMap(const GRState *state) {
RegionBindingsTy B = GetRegionBindings(state->getStore());
RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap();
- for (RegionBindingsTy::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
+ llvm::SmallPtrSet<const MemRegion*, 10> Marked;
+ llvm::SmallVector<const SubRegion*, 10> WL;
+
+ for (RegionBindingsTy::iterator I=B.begin(), E=B.end(); I!=E; ++I)
if (const SubRegion* R = dyn_cast<SubRegion>(I.getKey()))
- M->add(R->getSuperRegion(), R);
- }
+ WL.push_back(R);
+ RegionDefaultValue::MapTy DVM = state->get<RegionDefaultValue>();
+ for (RegionDefaultValue::MapTy::iterator I = DVM.begin(), E = DVM.end();
+ I != E; ++I)
+ if (const SubRegion* R = dyn_cast<SubRegion>(I.getKey()))
+ WL.push_back(R);
+
+ // We also need to record in the subregion map "intermediate" regions that
+ // don't have direct bindings but are super regions of those that do.
+ while (!WL.empty()) {
+ const SubRegion *R = WL.back();
+ WL.pop_back();
+
+ if (Marked.count(R))
+ continue;
+
+ const MemRegion *superR = R->getSuperRegion();
+ M->add(superR, R);
+ if (const SubRegion *sr = dyn_cast<SubRegion>(superR))
+ WL.push_back(sr);
+ }
+
return M;
}
+SubRegionMap *RegionStoreManager::getSubRegionMap(const GRState *state) {
+ return getRegionStoreSubRegionMap(state);
+}
+
//===----------------------------------------------------------------------===//
// Binding invalidation.
//===----------------------------------------------------------------------===//
+RegionBindingsTy
+RegionStoreManager::RemoveSubRegionBindings(RegionBindingsTy B,
+ const MemRegion *R,
+ RegionStoreSubRegionMap &M) {
+
+ RegionStoreSubRegionMap::iterator I, E;
+
+ for (llvm::tie(I, E) = M.begin_end(R); I != E; ++I)
+ B = RemoveSubRegionBindings(B, *I, M);
+
+ return RBFactory.Remove(B, R);
+}
+
+
const GRState *RegionStoreManager::InvalidateRegion(const GRState *state,
const MemRegion *R,
const Expr *E,
unsigned Count) {
ASTContext& Ctx = StateMgr.getContext();
+ // Strip away casts.
+ R = R->getBaseRegion();
+
+ // Get the mapping of regions -> subregions.
+ llvm::OwningPtr<RegionStoreSubRegionMap>
+ SubRegions(getRegionStoreSubRegionMap(state));
+
+ // Remove the bindings to subregions.
+ RegionBindingsTy B = GetRegionBindings(state->getStore());
+ B = RemoveSubRegionBindings(B, R, *SubRegions.get());
+ state = state->makeWithStore(B.getRoot());
+
if (!R->isBoundable())
return state;
- if (isa<AllocaRegion>(R) || isa<SymbolicRegion>(R)
- || isa<ObjCObjectRegion>(R)) {
- // Invalidate the alloca region by setting its default value to
+ if (isa<AllocaRegion>(R) || isa<SymbolicRegion>(R) ||
+ isa<ObjCObjectRegion>(R)) {
+ // Invalidate the region by setting its default value to
// conjured symbol. The type of the symbol is irrelavant.
SVal V = ValMgr.getConjuredSymbolVal(E, Ctx.IntTy, Count);
- state = setDefaultValue(state, R, V);
-
- // FIXME: This form of invalidation is a little bogus; we actually need
- // to invalidate all subregions as well.
- return state;
+ return setDefaultValue(state, R, V);
}
const TypedRegion *TR = cast<TypedRegion>(R);
T = NewT;
}
#endif
-
- if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) {
- SVal V = ValMgr.getConjuredSymbolVal(E, T, Count);
- return Bind(state, ValMgr.makeLoc(TR), V);
- }
- else if (const RecordType *RT = T->getAsStructureType()) {
+
+ if (const RecordType *RT = T->getAsStructureType()) {
// FIXME: handle structs with default region value.
const RecordDecl *RD = RT->getDecl()->getDefinition(Ctx);
if (!RD)
return state;
- // Iterate through the fields and construct new symbols.
- for (RecordDecl::field_iterator FI=RD->field_begin(),
- FE=RD->field_end(); FI!=FE; ++FI) {
-
- // For now just handle scalar fields.
- FieldDecl *FD = *FI;
- QualType FT = FD->getType();
- const FieldRegion* FR = MRMgr.getFieldRegion(FD, TR);
-
- if (Loc::IsLocType(FT) ||
- (FT->isIntegerType() && FT->isScalarType())) {
- SVal V = ValMgr.getConjuredSymbolVal(E, FT, Count);
- state = state->bindLoc(ValMgr.makeLoc(FR), V);
- }
- else if (FT->isStructureType()) {
- // set the default value of the struct field to conjured
- // symbol. Note that the type of the symbol is irrelavant.
- // We cannot use the type of the struct otherwise ValMgr won't
- // give us the conjured symbol.
- SVal V = ValMgr.getConjuredSymbolVal(E, Ctx.IntTy, Count);
- state = setDefaultValue(state, FR, V);
- }
- }
- } else if (const ArrayType *AT = Ctx.getAsArrayType(T)) {
+ // Invalidate the region by setting its default value to
+ // conjured symbol. The type of the symbol is irrelavant.
+ SVal V = ValMgr.getConjuredSymbolVal(E, Ctx.IntTy, Count);
+ return setDefaultValue(state, R, V);
+ }
+
+ if (const ArrayType *AT = Ctx.getAsArrayType(T)) {
// Set the default value of the array to conjured symbol.
SVal V = ValMgr.getConjuredSymbolVal(E, AT->getElementType(),
Count);
- state = setDefaultValue(state, TR, V);
- } else {
- // Just blast away other values.
- state = Bind(state, ValMgr.makeLoc(TR), UnknownVal());
+ return setDefaultValue(state, TR, V);
}
- return state;
+ SVal V = ValMgr.getConjuredSymbolVal(E, T, Count);
+ assert(SymbolManager::canSymbolicate(T) || V.isUnknown());
+ return Bind(state, ValMgr.makeLoc(TR), V);
}
//===----------------------------------------------------------------------===//
//
// Such funny addressing will occur due to layering of regions.
+#if 0
ASTContext &Ctx = getContext();
if (!T.isNull() && IsReinterpreted(RTy, T, Ctx)) {
SVal ZeroIdx = ValMgr.makeZeroArrayIndex();
assert(Ctx.getCanonicalType(RTy) ==
Ctx.getCanonicalType(R->getValueType(Ctx)));
}
+#endif
if (RTy->isStructureType())
return SValuator::CastResult(state, RetrieveStruct(state, R));
return SValuator::CastResult(state,
ValMgr.getRegionValueSymbolValOrUnknown(R, RTy));
}
+
+
SVal RegionStoreManager::RetrieveElement(const GRState* state,
const ElementRegion* R) {
return ValMgr.makeIntVal(c, getContext().CharTy);
}
}
+
+ // Special case: the current region represents a cast and it and the super
+ // region both have pointer types or intptr_t types. If so, perform the
+ // retrieve from the super region and appropriately "cast" the value.
+ // This is needed to support OSAtomicCompareAndSwap and friends or other
+ // loads that treat integers as pointers and vis versa.
+ if (R->getIndex().isZeroConstant()) {
+ if (const TypedRegion *superTR = dyn_cast<TypedRegion>(superR)) {
+ ASTContext &Ctx = getContext();
+
+ if (IsAnyPointerOrIntptr(superTR->getValueType(Ctx), Ctx)) {
+ QualType valTy = R->getValueType(Ctx);
+ if (IsAnyPointerOrIntptr(valTy, Ctx)) {
+ // Retrieve the value from the super region. This will be casted to
+ // valTy when we return to 'Retrieve'.
+ const SValuator::CastResult &cr = Retrieve(state,
+ loc::MemRegionVal(superR),
+ valTy);
+ return cr.getSVal();
+ }
+ }
+ }
+ }
// Check if the super region has a default value.
if (const SVal *D = state->get<RegionDefaultValue>(superR)) {
return *V;
const MemRegion* superR = R->getSuperRegion();
- if (const SVal* D = state->get<RegionDefaultValue>(superR)) {
- if (D->hasConjuredSymbol())
- return ValMgr.getRegionValueSymbolVal(R);
+ while (superR) {
+ if (const SVal* D = state->get<RegionDefaultValue>(superR)) {
+ if (SymbolRef parentSym = D->getAsSymbol())
+ return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R);
- if (D->isZeroConstant())
- return ValMgr.makeZeroVal(Ty);
+ if (D->isZeroConstant())
+ return ValMgr.makeZeroVal(Ty);
- if (D->isUnknown())
- return *D;
+ if (D->isUnknown())
+ return *D;
- assert(0 && "Unknown default value");
- }
+ assert(0 && "Unknown default value");
+ }
+
+ // If our super region is a field or element itself, walk up the region
+ // hierarchy to see if there is a default value installed in an ancestor.
+ if (isa<FieldRegion>(superR) || isa<ElementRegion>(superR)) {
+ superR = cast<SubRegion>(superR)->getSuperRegion();
+ continue;
+ }
+
+ break;
+ }
#if HEAP_UNDEFINED
// FIXME: Is this correct? Should it be UnknownVal?
return state;
// If we get here, the location should be a region.
- const MemRegion* R = cast<loc::MemRegionVal>(L).getRegion();
+ const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion();
// Check if the region is a struct region.
if (const TypedRegion* TR = dyn_cast<TypedRegion>(R))
if (TR->getValueType(getContext())->isStructureType())
return BindStruct(state, TR, V);
- RegionBindingsTy B = GetRegionBindings(state->getStore());
-
- B = RBFactory.Add(B, R, V);
+ // Special case: the current region represents a cast and it and the super
+ // region both have pointer types or intptr_t types. If so, perform the
+ // bind to the super region.
+ // This is needed to support OSAtomicCompareAndSwap and friends or other
+ // loads that treat integers as pointers and vis versa.
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
+ if (ER->getIndex().isZeroConstant()) {
+ if (const TypedRegion *superR =
+ dyn_cast<TypedRegion>(ER->getSuperRegion())) {
+ ASTContext &Ctx = getContext();
+ QualType superTy = superR->getValueType(Ctx);
+ QualType erTy = ER->getValueType(Ctx);
+
+ if (IsAnyPointerOrIntptr(superTy, Ctx) &&
+ IsAnyPointerOrIntptr(erTy, Ctx)) {
+ SValuator::CastResult cr =
+ ValMgr.getSValuator().EvalCast(V, state, superTy, erTy);
+ return Bind(cr.getState(), loc::MemRegionVal(superR), cr.getSVal());
+ }
+ }
+ }
+ }
+ // Perform the binding.
+ RegionBindingsTy B = GetRegionBindings(state->getStore());
+ B = RBFactory.Add(B, R, V);
return state->makeWithStore(B.getRoot());
}
typedef llvm::ImmutableSet<const MemRegion*> SubRegionsTy;
typedef llvm::ImmutableMap<const MemRegion*, SubRegionsTy> SubRegionsMapTy;
- // FIXME: As a future optimization we can modifiy BumpPtrAllocator to have
- // the ability to reuse memory. This way we can keep TmpAlloc around as
- // an instance variable of RegionStoreManager (avoiding repeated malloc
- // overhead).
- llvm::BumpPtrAllocator TmpAlloc;
-
- // Factory objects.
- SubRegionsMapTy::Factory SubRegMapF(TmpAlloc);
- SubRegionsTy::Factory SubRegF(TmpAlloc);
-
// The backmap from regions to subregions.
- SubRegionsMapTy SubRegMap = SubRegMapF.GetEmptyMap();
+ llvm::OwningPtr<RegionStoreSubRegionMap>
+ SubRegions(getRegionStoreSubRegionMap(state));
// Do a pass over the regions in the store. For VarRegions we check if
// the variable is still live and if so add it to the list of live roots.
// For other regions we populate our region backmap.
llvm::SmallVector<const MemRegion*, 10> IntermediateRoots;
+ // Scan the direct bindings for "intermediate" roots.
for (RegionBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
- IntermediateRoots.push_back(I.getKey());
+ const MemRegion *R = I.getKey();
+ IntermediateRoots.push_back(R);
}
+ // Scan the default bindings for "intermediate" roots.
+ RegionDefaultValue::MapTy DVM = state->get<RegionDefaultValue>();
+ for (RegionDefaultValue::MapTy::iterator I = DVM.begin(), E = DVM.end();
+ I != E; ++I) {
+ const MemRegion *R = I.getKey();
+ IntermediateRoots.push_back(R);
+ }
+
+ // Process the "intermediate" roots to find if they are referenced by
+ // real roots.
while (!IntermediateRoots.empty()) {
const MemRegion* R = IntermediateRoots.back();
IntermediateRoots.pop_back();
if (SymReaper.isLive(Loc, VR->getDecl())) {
RegionRoots.push_back(VR); // This is a live "root".
}
- }
- else if (const SymbolicRegion* SR = dyn_cast<SymbolicRegion>(R)) {
+ continue;
+ }
+
+ if (const SymbolicRegion* SR = dyn_cast<SymbolicRegion>(R)) {
if (SymReaper.isLive(SR->getSymbol()))
RegionRoots.push_back(SR);
+ continue;
}
- else {
- // Get the super region for R.
- const MemRegion* superR = cast<SubRegion>(R)->getSuperRegion();
-
- // Get the current set of subregions for SuperR.
- const SubRegionsTy* SRptr = SubRegMap.lookup(superR);
- SubRegionsTy SRs = SRptr ? *SRptr : SubRegF.GetEmptySet();
-
- // Add R to the subregions of SuperR.
- SubRegMap = SubRegMapF.Add(SubRegMap, superR, SubRegF.Add(SRs, R));
-
- // Super region may be VarRegion or subregion of another VarRegion. Add it
- // to the work list.
- if (isa<SubRegion>(superR))
- IntermediateRoots.push_back(superR);
- }
+
+ // Add the super region for R to the worklist if it is a subregion.
+ if (const SubRegion* superR =
+ dyn_cast<SubRegion>(cast<SubRegion>(R)->getSuperRegion()))
+ IntermediateRoots.push_back(superR);
}
// Process the worklist of RegionRoots. This performs a "mark-and-sweep"
// of the store. We want to find all live symbols and dead regions.
- llvm::SmallPtrSet<const MemRegion*, 10> Marked;
-
+ llvm::SmallPtrSet<const MemRegion*, 10> Marked;
while (!RegionRoots.empty()) {
// Dequeue the next region on the worklist.
const MemRegion* R = RegionRoots.back();
RegionRoots.pop_back();
// Check if we have already processed this region.
- if (Marked.count(R)) continue;
+ if (Marked.count(R))
+ continue;
// Mark this region as processed. This is needed for termination in case
// a region is referenced more than once.
SymReaper.markLive(SymR->getSymbol());
// Get the data binding for R (if any).
- RegionBindingsTy::data_type* Xptr = B.lookup(R);
+ const SVal* Xptr = B.lookup(R);
+ if (!Xptr) {
+ // No direct binding? Get the default binding for R (if any).
+ Xptr = DVM.lookup(R);
+ }
+
+ // Direct or default binding?
if (Xptr) {
SVal X = *Xptr;
UpdateLiveSymbols(X, SymReaper); // Update the set of live symbols.
// If X is a region, then add it to the RegionRoots.
if (const MemRegion *RX = X.getAsRegion()) {
RegionRoots.push_back(RX);
-
// Mark the super region of the RX as live.
// e.g.: int x; char *y = (char*) &x; if (*y) ...
// 'y' => element region. 'x' is its super region.
- // We only add one level super region for now.
- // FIXME: maybe multiple level of super regions should be added.
if (const SubRegion *SR = dyn_cast<SubRegion>(RX)) {
RegionRoots.push_back(SR->getSuperRegion());
}
// Get the subregions of R. These are RegionRoots as well since they
// represent values that are also bound to R.
- const SubRegionsTy* SRptr = SubRegMap.lookup(R);
- if (!SRptr) continue;
- SubRegionsTy SR = *SRptr;
-
- for (SubRegionsTy::iterator I=SR.begin(), E=SR.end(); I!=E; ++I)
+ RegionStoreSubRegionMap::iterator I, E;
+ for (llvm::tie(I, E) = SubRegions->begin_end(R); I != E; ++I)
RegionRoots.push_back(*I);
-
}
// We have now scanned the store, marking reachable regions and symbols
SVal X = I.getData();
SVal::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end();
- for (; SI != SE; ++SI) SymReaper.maybeDead(*SI);
+ for (; SI != SE; ++SI)
+ SymReaper.maybeDead(*SI);
}
+ // FIXME: remove default bindings as well.
+
return store;
}
void RegionStoreManager::print(Store store, llvm::raw_ostream& OS,
const char* nl, const char *sep) {
RegionBindingsTy B = GetRegionBindings(store);
- OS << "Store:" << nl;
+ OS << "Store (direct bindings):" << nl;
for (RegionBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I)
- OS << ' ' << I.getKey() << " : " << I.getData() << nl;
+ OS << ' ' << I.getKey() << " : " << I.getData() << nl;
}
StoreManager::CastResult
StoreManager::MakeElementRegion(const GRState *state, const MemRegion *region,
- QualType pointeeTy, QualType castToTy) {
-
- // Record the cast type of the region.
- state = setCastType(state, region, castToTy);
-
- // Create a new ElementRegion at offset 0.
- SVal idx = ValMgr.makeZeroArrayIndex();
+ QualType pointeeTy, QualType castToTy,
+ uint64_t index) {
+ // Create a new ElementRegion.
+ SVal idx = ValMgr.makeArrayIndex(index);
return CastResult(state, MRMgr.getElementRegion(pointeeTy, idx, region,
ValMgr.getContext()));
}
+// FIXME: Merge with the implementation of the same method in MemRegion.cpp
static bool IsCompleteType(ASTContext &Ctx, QualType Ty) {
if (const RecordType *RT = Ty->getAs<RecordType>()) {
const RecordDecl *D = RT->getDecl();
ASTContext& Ctx = StateMgr.getContext();
- // We need to know the real type of CastToTy.
- QualType ToTy = Ctx.getCanonicalType(CastToTy);
-
// Handle casts to Objective-C objects.
- if (CastToTy->isObjCObjectPointerType()) {
- state = setCastType(state, R, CastToTy);
- return CastResult(state, R);
- }
-
+ if (CastToTy->isObjCObjectPointerType())
+ return CastResult(state, R->getBaseRegion());
+
if (CastToTy->isBlockPointerType()) {
if (isa<CodeTextRegion>(R))
return CastResult(state, R);
// already be handled.
QualType PointeeTy = CastToTy->getAs<PointerType>()->getPointeeType();
+ // Handle casts from compatible types or to void*.
+ if (R->isBoundable())
+ if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
+ QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx));
+ QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
+ if (CanonPointeeTy == ObjTy || CanonPointeeTy == Ctx.VoidTy)
+ return CastResult(state, R);
+ }
+
// Process region cast according to the kind of the region being cast.
switch (R->getKind()) {
case MemRegion::BEG_TYPED_REGIONS:
case MemRegion::END_TYPED_REGIONS: {
assert(0 && "Invalid region cast");
break;
- }
-
+ }
case MemRegion::CodeTextRegionKind: {
// CodeTextRegion should be cast to only a function or block pointer type,
// although they can in practice be casted to anything, e.g, void*,
}
case MemRegion::StringRegionKind:
- // Handle casts of string literals.
- return MakeElementRegion(state, R, PointeeTy, CastToTy);
-
case MemRegion::ObjCObjectRegionKind:
- case MemRegion::SymbolicRegionKind:
// FIXME: Need to handle arbitrary downcasts.
- case MemRegion::AllocaRegionKind: {
- state = setCastType(state, R, CastToTy);
- break;
- }
-
+ case MemRegion::SymbolicRegionKind:
+ case MemRegion::AllocaRegionKind:
case MemRegion::CompoundLiteralRegionKind:
- case MemRegion::ElementRegionKind:
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
- case MemRegion::VarRegionKind: {
- // VarRegion, ElementRegion, and FieldRegion has an inherent type.
- // Normally they should not be cast. We only layer an ElementRegion when
- // the cast-to pointee type is of smaller size. In other cases, we return
- // the original VarRegion.
+ case MemRegion::VarRegionKind:
+ return MakeElementRegion(state, R, PointeeTy, CastToTy);
+
+ 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.
- // If the pointee or object type is incomplete, do not compute their
- // sizes, and return the original region.
- QualType ObjTy = cast<TypedRegion>(R)->getValueType(Ctx);
+ const ElementRegion *elementR = cast<ElementRegion>(R);
+ const RegionRawOffset &rawOff = elementR->getAsRawOffset();
+ const MemRegion *baseR = rawOff.getRegion();
- if (!IsCompleteType(Ctx, PointeeTy) || !IsCompleteType(Ctx, ObjTy)) {
- state = setCastType(state, R, ToTy);
- break;
+ // If we cannot compute a raw offset, throw up our hands and return
+ // a NULL MemRegion*.
+ if (!baseR)
+ return CastResult(state, NULL);
+
+ int64_t off = rawOff.getByteOffset();
+
+ if (off == 0) {
+ // 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 TypedRegion *TR = dyn_cast<TypedRegion>(baseR)) {
+ QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx));
+ QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
+ if (CanonPointeeTy == ObjTy)
+ return CastResult(state, baseR);
+ }
+
+ // Otherwise, create a new ElementRegion at offset 0.
+ return MakeElementRegion(state, baseR, PointeeTy, CastToTy, 0);
}
+
+ // 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 = 0;
- uint64_t PointeeTySize = Ctx.getTypeSize(PointeeTy);
- uint64_t ObjTySize = Ctx.getTypeSize(ObjTy);
+ // We can only compute sizeof(PointeeTy) if it is a complete type.
+ if (IsCompleteType(Ctx, PointeeTy)) {
+ // Compute the size in **bytes**.
+ int64_t pointeeTySize = (int64_t) (Ctx.getTypeSize(PointeeTy) / 8);
+
+ // 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 ((PointeeTySize > 0 && PointeeTySize < ObjTySize) ||
- (ObjTy->isAggregateType() && PointeeTy->isScalarType()) ||
- ObjTySize == 0 /* R has 'void*' type. */)
- return MakeElementRegion(state, R, PointeeTy, ToTy);
-
- state = setCastType(state, R, ToTy);
- break;
+ if (!newSuperR) {
+ // Create an intermediate ElementRegion to represent the raw byte.
+ // This will be the super region of the final ElementRegion.
+ SVal idx = ValMgr.makeArrayIndex(off);
+ newSuperR = MRMgr.getElementRegion(Ctx.CharTy, idx, baseR, Ctx);
+ }
+
+ return MakeElementRegion(state, newSuperR, PointeeTy, CastToTy, newIndex);
}
}
}
void SymbolConjured::dumpToStream(llvm::raw_ostream& os) const {
- os << "conj_$" << getSymbolID();
+ os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}';
}
void SymbolDerived::dumpToStream(llvm::raw_ostream& os) const {
if (TheLiving.contains(sym))
return true;
+ if (const SymbolDerived *derived = dyn_cast<SymbolDerived>(sym)) {
+ if (isLive(derived->getParentSymbol())) {
+ markLive(sym);
+ return true;
+ }
+ return false;
+ }
+
// Interogate the symbol. It may derive from an input value to
// the analyzed function/method.
return isa<SymbolRegionValue>(sym);
}
SVal ValueManager::getRegionValueSymbolVal(const MemRegion* R, QualType T) {
+
+ if (T.isNull()) {
+ const TypedRegion* TR = cast<TypedRegion>(R);
+ T = TR->getValueType(SymMgr.getContext());
+ }
+
+ if (!SymbolManager::canSymbolicate(T))
+ return UnknownVal();
+
SymbolRef sym = SymMgr.getRegionValueSymbol(R, T);
-
- if (const TypedRegion* TR = dyn_cast<TypedRegion>(R)) {
- if (T.isNull())
- T = TR->getValueType(SymMgr.getContext());
-
- // If T is of function pointer type or a block pointer type, create a
- // CodeTextRegion wrapping that symbol.
- if (T->isFunctionPointerType() || T->isBlockPointerType()) {
- return loc::MemRegionVal(MemMgr.getCodeTextRegion(sym, T));
- }
+
+ // If T is of function pointer type or a block pointer type, create a
+ // CodeTextRegion wrapping that symbol.
+ if (T->isFunctionPointerType() || T->isBlockPointerType())
+ return loc::MemRegionVal(MemMgr.getCodeTextRegion(sym, T));
- if (Loc::IsLocType(T))
- return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
+ if (Loc::IsLocType(T))
+ return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
- // Only handle integers for now.
- if (T->isIntegerType() && T->isScalarType())
- return nonloc::SymbolVal(sym);
- }
-
- return UnknownVal();
+ return nonloc::SymbolVal(sym);
}
SVal ValueManager::getConjuredSymbolVal(const Expr* E, unsigned Count) {
QualType T = E->getType();
+
+ if (!SymbolManager::canSymbolicate(T))
+ return UnknownVal();
+
SymbolRef sym = SymMgr.getConjuredSymbol(E, Count);
// If T is of function pointer type or a block pointer type, create a
// CodeTextRegion wrapping a symbol.
- if (T->isFunctionPointerType() || T->isBlockPointerType()) {
+ if (T->isFunctionPointerType() || T->isBlockPointerType())
return loc::MemRegionVal(MemMgr.getCodeTextRegion(sym, T));
- }
if (Loc::IsLocType(T))
return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
- if (T->isIntegerType() && T->isScalarType())
- return nonloc::SymbolVal(sym);
-
- return UnknownVal();
+ return nonloc::SymbolVal(sym);
}
SVal ValueManager::getConjuredSymbolVal(const Expr* E, QualType T,
unsigned Count) {
+
+ if (!SymbolManager::canSymbolicate(T))
+ return UnknownVal();
SymbolRef sym = SymMgr.getConjuredSymbol(E, T, Count);
// If T is of function pointer type or a block pointer type, create a
// CodeTextRegion wrapping a symbol.
- if (T->isFunctionPointerType() || T->isBlockPointerType()) {
+ if (T->isFunctionPointerType() || T->isBlockPointerType())
return loc::MemRegionVal(MemMgr.getCodeTextRegion(sym, T));
- }
if (Loc::IsLocType(T))
return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
- if (T->isIntegerType() && T->isScalarType())
- return nonloc::SymbolVal(sym);
-
- return UnknownVal();
+ return nonloc::SymbolVal(sym);
}
SVal ValueManager::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
const TypedRegion *R) {
- SymbolRef sym = SymMgr.getDerivedSymbol(parentSymbol, R);
-
QualType T = R->getValueType(R->getContext());
+
+ if (!SymbolManager::canSymbolicate(T))
+ return UnknownVal();
+
+ SymbolRef sym = SymMgr.getDerivedSymbol(parentSymbol, R);
if (Loc::IsLocType(T))
return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
- if (T->isIntegerType() && T->isScalarType())
- return nonloc::SymbolVal(sym);
-
- return UnknownVal();
+ return nonloc::SymbolVal(sym);
}
SVal ValueManager::getFunctionPointer(const FunctionDecl* FD) {
+++ /dev/null
-// RUN: clang-cc -analyze -checker-cfref --analyzer-store=basic -analyzer-constraints=basic --verify -fblocks %s &&
-// RUN: clang-cc -analyze -checker-cfref --analyzer-store=basic -analyzer-constraints=range --verify -fblocks %s &&
-// RUN: clang-cc -analyze -checker-cfref --analyzer-store=region -analyzer-constraints=basic --verify -fblocks %s &&
-// RUN: clang-cc -analyze -checker-cfref --analyzer-store=region -analyzer-constraints=range --verify -fblocks %s
-// XFAIL
-typedef struct objc_ivar *Ivar;
-typedef struct objc_selector *SEL;
-typedef signed char BOOL;
-typedef int NSInteger;
-typedef unsigned int NSUInteger;
-typedef struct _NSZone NSZone;
-@class NSInvocation, NSArray, NSMethodSignature, NSCoder, NSString, NSEnumerator;
-@protocol NSObject
-- (BOOL)isEqual:(id)object;
-- (id)autorelease;
-@end
-@protocol NSCopying
-- (id)copyWithZone:(NSZone *)zone;
-@end
-@protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end
-@protocol NSCoding
-- (void)encodeWithCoder:(NSCoder *)aCoder;
-@end
-@interface NSObject <NSObject> {}
-- (id)init;
-+ (id)allocWithZone:(NSZone *)zone;
-@end
-extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone);
-@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
-- (NSUInteger)length;
-+ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
-@end extern NSString * const NSBundleDidLoadNotification;
-@interface NSValue : NSObject <NSCopying, NSCoding>
-- (void)getValue:(void *)value;
-@end
-@interface NSNumber : NSValue
-- (char)charValue;
-- (id)initWithBool:(BOOL)value;
-@end
-@interface NSAssertionHandler : NSObject {}
-+ (NSAssertionHandler *)currentHandler;
-- (void)handleFailureInMethod:(SEL)selector object:(id)object file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format,...;
-@end
-extern NSString * const NSConnectionReplyMode;
-typedef float CGFloat;
-typedef struct _NSPoint {
- CGFloat x;
- CGFloat y;
-} NSPoint;
-typedef struct _NSSize {
- CGFloat width;
- CGFloat height;
-} NSSize;
-typedef struct _NSRect {
- NSPoint origin;
- NSSize size;
-} NSRect;
-
-// *** This case currently crashes for RegionStore ***
-// Reduced from a crash involving the cast of an Objective-C symbolic region to
-// 'char *'
-static NSNumber *test_ivar_offset(id self, SEL _cmd, Ivar inIvar) {
- return [[[NSNumber allocWithZone:((void*)0)] initWithBool:*(_Bool *)((char *)self + ivar_getOffset(inIvar))] autorelease];
-}
// RUN: clang-cc -analyze -checker-cfref --analyzer-store=region -analyzer-constraints=basic --verify -fblocks %s &&
// RUN: clang-cc -analyze -checker-cfref --analyzer-store=region -analyzer-constraints=range --verify -fblocks %s
+typedef struct objc_ivar *Ivar;
typedef struct objc_selector *SEL;
typedef signed char BOOL;
typedef int NSInteger;
typedef unsigned int NSUInteger;
typedef struct _NSZone NSZone;
@class NSInvocation, NSArray, NSMethodSignature, NSCoder, NSString, NSEnumerator;
-@protocol NSObject - (BOOL)isEqual:(id)object; @end
-@protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end
+@protocol NSObject
+- (BOOL)isEqual:(id)object;
+- (id)autorelease;
+@end
+@protocol NSCopying
+- (id)copyWithZone:(NSZone *)zone;
+@end
@protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end
-@protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end
-@interface NSObject <NSObject> {} - (id)init; @end
+@protocol NSCoding
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+@end
+@interface NSObject <NSObject> {}
+- (id)init;
++ (id)allocWithZone:(NSZone *)zone;
+@end
extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone);
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
- (NSUInteger)length;
+ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
@end extern NSString * const NSBundleDidLoadNotification;
+@interface NSValue : NSObject <NSCopying, NSCoding>
+- (void)getValue:(void *)value;
+@end
+@interface NSNumber : NSValue
+- (char)charValue;
+- (id)initWithBool:(BOOL)value;
+@end
@interface NSAssertionHandler : NSObject {}
+ (NSAssertionHandler *)currentHandler;
- (void)handleFailureInMethod:(SEL)selector object:(id)object file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format,...;
return;
}
+// Reduced from a crash involving the cast of an Objective-C symbolic region to
+// 'char *'
+static NSNumber *test_ivar_offset(id self, SEL _cmd, Ivar inIvar) {
+ return [[[NSNumber allocWithZone:((void*)0)] initWithBool:*(_Bool *)((char *)self + ivar_getOffset(inIvar))] autorelease];
+}