diff options
-rw-r--r-- | clang/include/clang/Analysis/PathSensitive/MemRegion.h | 39 | ||||
-rw-r--r-- | clang/include/clang/Analysis/PathSensitive/Store.h | 3 | ||||
-rw-r--r-- | clang/include/clang/Analysis/PathSensitive/ValueManager.h | 10 | ||||
-rw-r--r-- | clang/lib/Analysis/MemRegion.cpp | 71 | ||||
-rw-r--r-- | clang/lib/Analysis/RegionStore.cpp | 346 | ||||
-rw-r--r-- | clang/lib/Analysis/Store.cpp | 143 | ||||
-rw-r--r-- | clang/lib/Analysis/SymbolManager.cpp | 10 | ||||
-rw-r--r-- | clang/lib/Analysis/ValueManager.cpp | 70 | ||||
-rw-r--r-- | clang/test/Analysis/misc-ps-xfail.m | 64 | ||||
-rw-r--r-- | clang/test/Analysis/misc-ps.m | 31 |
10 files changed, 507 insertions, 280 deletions
diff --git a/clang/include/clang/Analysis/PathSensitive/MemRegion.h b/clang/include/clang/Analysis/PathSensitive/MemRegion.h index 3cfcc5cd5d7..5d4c6137bc7 100644 --- a/clang/include/clang/Analysis/PathSensitive/MemRegion.h +++ b/clang/include/clang/Analysis/PathSensitive/MemRegion.h @@ -35,6 +35,10 @@ namespace clang { class MemRegionManager; class MemSpaceRegion; +//===----------------------------------------------------------------------===// +// Base region classes. +//===----------------------------------------------------------------------===// + /// MemRegion - The root abstract class for all memory regions. class MemRegion : public llvm::FoldingSetNode { public: @@ -134,13 +138,39 @@ 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'. @@ -523,6 +553,8 @@ public: return ElementType; } + RegionRawOffset getAsRawOffset() const; + void dumpToStream(llvm::raw_ostream& os) const; void Profile(llvm::FoldingSetNodeID& ID) const; @@ -531,7 +563,7 @@ public: return R->getKind() == ElementRegionKind; } }; - + template<typename RegionTy> const RegionTy* MemRegion::getAs() const { if (const RegionTy* RT = dyn_cast<RegionTy>(this)) @@ -627,6 +659,7 @@ public: /// object). ObjCIvarRegion* getObjCIvarRegion(const ObjCIvarDecl* ivd, const MemRegion* superRegion); + CodeTextRegion* getCodeTextRegion(SymbolRef sym, QualType t); CodeTextRegion* getCodeTextRegion(const FunctionDecl* fd, QualType t); diff --git a/clang/include/clang/Analysis/PathSensitive/Store.h b/clang/include/clang/Analysis/PathSensitive/Store.h index c897b374a17..759a80eff4b 100644 --- a/clang/include/clang/Analysis/PathSensitive/Store.h +++ b/clang/include/clang/Analysis/PathSensitive/Store.h @@ -198,7 +198,8 @@ public: 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? diff --git a/clang/include/clang/Analysis/PathSensitive/ValueManager.h b/clang/include/clang/Analysis/PathSensitive/ValueManager.h index 9a535b54155..711ac4a8bc5 100644 --- a/clang/include/clang/Analysis/PathSensitive/ValueManager.h +++ b/clang/include/clang/Analysis/PathSensitive/ValueManager.h @@ -50,11 +50,11 @@ class ValueManager { 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)); diff --git a/clang/lib/Analysis/MemRegion.cpp b/clang/lib/Analysis/MemRegion.cpp index 3c174df2820..ac633060fdd 100644 --- a/clang/lib/Analysis/MemRegion.cpp +++ b/clang/lib/Analysis/MemRegion.cpp @@ -15,6 +15,7 @@ #include "llvm/Support/raw_ostream.h" #include "clang/Analysis/PathSensitive/MemRegion.h" +#include "clang/Analysis/PathSensitive/ValueManager.h" using namespace clang; @@ -171,7 +172,8 @@ void CompoundLiteralRegion::dumpToStream(llvm::raw_ostream& os) const { } 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 { @@ -194,10 +196,18 @@ void VarRegion::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>(); @@ -306,7 +316,6 @@ AllocaRegion* MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt) { return getRegion<AllocaRegion>(E, cnt); } - const MemSpaceRegion *MemRegion::getMemorySpace() const { const MemRegion *R = this; const SubRegion* SR = dyn_cast<SubRegion>(this); @@ -381,7 +390,7 @@ const MemRegion *MemRegion::getBaseRegion() const { // 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; } @@ -391,3 +400,57 @@ const MemRegion *MemRegion::getBaseRegion() const { } 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); +} + diff --git a/clang/lib/Analysis/RegionStore.cpp b/clang/lib/Analysis/RegionStore.cpp index d79c4c5fcf0..6ca881d73b9 100644 --- a/clang/lib/Analysis/RegionStore.cpp +++ b/clang/lib/Analysis/RegionStore.cpp @@ -112,28 +112,43 @@ namespace clang { // 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); @@ -158,6 +173,14 @@ public: 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 { @@ -182,7 +205,9 @@ public: 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 @@ -247,6 +272,12 @@ public: 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, @@ -405,41 +436,91 @@ StoreManager *clang::CreateFieldsOnlyRegionStoreManager(GRStateManager &StMgr) { 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); @@ -465,12 +546,8 @@ const GRState *RegionStoreManager::InvalidateRegion(const GRState *state, 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); @@ -478,40 +555,22 @@ const GRState *RegionStoreManager::InvalidateRegion(const GRState *state, 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); } //===----------------------------------------------------------------------===// @@ -923,6 +982,7 @@ RegionStoreManager::Retrieve(const GRState *state, Loc L, QualType T) { // // 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(); @@ -931,6 +991,7 @@ RegionStoreManager::Retrieve(const GRState *state, Loc L, QualType T) { assert(Ctx.getCanonicalType(RTy) == Ctx.getCanonicalType(R->getValueType(Ctx))); } +#endif if (RTy->isStructureType()) return SValuator::CastResult(state, RetrieveStruct(state, R)); @@ -990,6 +1051,8 @@ RegionStoreManager::Retrieve(const GRState *state, Loc L, QualType T) { return SValuator::CastResult(state, ValMgr.getRegionValueSymbolValOrUnknown(R, RTy)); } + + SVal RegionStoreManager::RetrieveElement(const GRState* state, const ElementRegion* R) { @@ -1014,6 +1077,29 @@ SVal RegionStoreManager::RetrieveElement(const GRState* state, 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)) { @@ -1078,18 +1164,29 @@ SVal RegionStoreManager::RetrieveField(const GRState* state, 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? @@ -1260,17 +1357,39 @@ const GRState *RegionStoreManager::Bind(const GRState *state, Loc L, SVal V) { 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()); } @@ -1522,28 +1641,31 @@ Store RegionStoreManager::RemoveDeadBindings(const GRState *state, Stmt* Loc, 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(); @@ -1552,40 +1674,32 @@ Store RegionStoreManager::RemoveDeadBindings(const GRState *state, Stmt* Loc, 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. @@ -1597,7 +1711,13 @@ Store RegionStoreManager::RemoveDeadBindings(const GRState *state, Stmt* Loc, 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. @@ -1605,12 +1725,9 @@ Store RegionStoreManager::RemoveDeadBindings(const GRState *state, Stmt* Loc, // 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()); } @@ -1619,13 +1736,9 @@ Store RegionStoreManager::RemoveDeadBindings(const GRState *state, Stmt* Loc, // 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 @@ -1646,9 +1759,12 @@ Store RegionStoreManager::RemoveDeadBindings(const GRState *state, Stmt* Loc, 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; } @@ -1659,8 +1775,8 @@ Store RegionStoreManager::RemoveDeadBindings(const GRState *state, Stmt* Loc, 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; } diff --git a/clang/lib/Analysis/Store.cpp b/clang/lib/Analysis/Store.cpp index 68bb49cbe30..bfcb0f41ca3 100644 --- a/clang/lib/Analysis/Store.cpp +++ b/clang/lib/Analysis/Store.cpp @@ -22,17 +22,15 @@ StoreManager::StoreManager(GRStateManager &stateMgr) 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(); @@ -49,15 +47,10 @@ StoreManager::CastRegion(const GRState *state, const MemRegion* R, 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); @@ -79,6 +72,15 @@ StoreManager::CastRegion(const GRState *state, const MemRegion* 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: @@ -88,8 +90,7 @@ StoreManager::CastRegion(const GRState *state, const MemRegion* R, 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*, @@ -99,46 +100,94 @@ StoreManager::CastRegion(const GRState *state, const MemRegion* R, } 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); } } diff --git a/clang/lib/Analysis/SymbolManager.cpp b/clang/lib/Analysis/SymbolManager.cpp index 95edbb6dd11..b94551e31f2 100644 --- a/clang/lib/Analysis/SymbolManager.cpp +++ b/clang/lib/Analysis/SymbolManager.cpp @@ -65,7 +65,7 @@ void SymSymExpr::dumpToStream(llvm::raw_ostream& os) const { } void SymbolConjured::dumpToStream(llvm::raw_ostream& os) const { - os << "conj_$" << getSymbolID(); + os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}'; } void SymbolDerived::dumpToStream(llvm::raw_ostream& os) const { @@ -207,6 +207,14 @@ bool SymbolReaper::isLive(SymbolRef sym) { 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); diff --git a/clang/lib/Analysis/ValueManager.cpp b/clang/lib/Analysis/ValueManager.cpp index ee6b5cbeeb3..6e6187151ae 100644 --- a/clang/lib/Analysis/ValueManager.cpp +++ b/clang/lib/Analysis/ValueManager.cpp @@ -70,82 +70,80 @@ SVal ValueManager::convertToArrayIndex(SVal V) { } 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) { diff --git a/clang/test/Analysis/misc-ps-xfail.m b/clang/test/Analysis/misc-ps-xfail.m deleted file mode 100644 index 267abb3f2fa..00000000000 --- a/clang/test/Analysis/misc-ps-xfail.m +++ /dev/null @@ -1,64 +0,0 @@ -// 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]; -} diff --git a/clang/test/Analysis/misc-ps.m b/clang/test/Analysis/misc-ps.m index e49cdb0a962..1b16762c42e 100644 --- a/clang/test/Analysis/misc-ps.m +++ b/clang/test/Analysis/misc-ps.m @@ -3,22 +3,40 @@ // 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,...; @@ -468,3 +486,8 @@ void test_invalidate_cast_int() { 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]; +} |