1//== DynamicTypePropagation.cpp ----------------------------------- -*- C++ -*--=// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This checker defines the rules for dynamic type gathering and propagation. 11// 12//===----------------------------------------------------------------------===// 13 14#include "ClangSACheckers.h" 15#include "clang/Basic/Builtins.h" 16#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17#include "clang/StaticAnalyzer/Core/Checker.h" 18#include "clang/StaticAnalyzer/Core/CheckerManager.h" 19#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 22 23using namespace clang; 24using namespace ento; 25 26namespace { 27class DynamicTypePropagation: 28 public Checker< check::PreCall, 29 check::PostCall, 30 check::PostStmt<ImplicitCastExpr>, 31 check::PostStmt<CXXNewExpr> > { 32 const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, 33 CheckerContext &C) const; 34 35 /// \brief Return a better dynamic type if one can be derived from the cast. 36 const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, 37 CheckerContext &C) const; 38public: 39 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 40 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 41 void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; 42 void checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const; 43}; 44} 45 46static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, 47 CheckerContext &C) { 48 assert(Region); 49 assert(MD); 50 51 ASTContext &Ctx = C.getASTContext(); 52 QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent())); 53 54 ProgramStateRef State = C.getState(); 55 State = State->setDynamicTypeInfo(Region, Ty, /*CanBeSubclass=*/false); 56 C.addTransition(State); 57 return; 58} 59 60void DynamicTypePropagation::checkPreCall(const CallEvent &Call, 61 CheckerContext &C) const { 62 if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { 63 // C++11 [class.cdtor]p4: When a virtual function is called directly or 64 // indirectly from a constructor or from a destructor, including during 65 // the construction or destruction of the class’s non-static data members, 66 // and the object to which the call applies is the object under 67 // construction or destruction, the function called is the final overrider 68 // in the constructor's or destructor's class and not one overriding it in 69 // a more-derived class. 70 71 switch (Ctor->getOriginExpr()->getConstructionKind()) { 72 case CXXConstructExpr::CK_Complete: 73 case CXXConstructExpr::CK_Delegating: 74 // No additional type info necessary. 75 return; 76 case CXXConstructExpr::CK_NonVirtualBase: 77 case CXXConstructExpr::CK_VirtualBase: 78 if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) 79 recordFixedType(Target, Ctor->getDecl(), C); 80 return; 81 } 82 83 return; 84 } 85 86 if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { 87 // C++11 [class.cdtor]p4 (see above) 88 if (!Dtor->isBaseDestructor()) 89 return; 90 91 const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion(); 92 if (!Target) 93 return; 94 95 const Decl *D = Dtor->getDecl(); 96 if (!D) 97 return; 98 99 recordFixedType(Target, cast<CXXDestructorDecl>(D), C); 100 return; 101 } 102} 103 104void DynamicTypePropagation::checkPostCall(const CallEvent &Call, 105 CheckerContext &C) const { 106 // We can obtain perfect type info for return values from some calls. 107 if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { 108 109 // Get the returned value if it's a region. 110 const MemRegion *RetReg = Call.getReturnValue().getAsRegion(); 111 if (!RetReg) 112 return; 113 114 ProgramStateRef State = C.getState(); 115 const ObjCMethodDecl *D = Msg->getDecl(); 116 117 if (D && D->hasRelatedResultType()) { 118 switch (Msg->getMethodFamily()) { 119 default: 120 break; 121 122 // We assume that the type of the object returned by alloc and new are the 123 // pointer to the object of the class specified in the receiver of the 124 // message. 125 case OMF_alloc: 126 case OMF_new: { 127 // Get the type of object that will get created. 128 const ObjCMessageExpr *MsgE = Msg->getOriginExpr(); 129 const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C); 130 if (!ObjTy) 131 return; 132 QualType DynResTy = 133 C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0)); 134 C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false)); 135 break; 136 } 137 case OMF_init: { 138 // Assume, the result of the init method has the same dynamic type as 139 // the receiver and propagate the dynamic type info. 140 const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); 141 if (!RecReg) 142 return; 143 DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg); 144 C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType)); 145 break; 146 } 147 } 148 } 149 return; 150 } 151 152 if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { 153 // We may need to undo the effects of our pre-call check. 154 switch (Ctor->getOriginExpr()->getConstructionKind()) { 155 case CXXConstructExpr::CK_Complete: 156 case CXXConstructExpr::CK_Delegating: 157 // No additional work necessary. 158 // Note: This will leave behind the actual type of the object for 159 // complete constructors, but arguably that's a good thing, since it 160 // means the dynamic type info will be correct even for objects 161 // constructed with operator new. 162 return; 163 case CXXConstructExpr::CK_NonVirtualBase: 164 case CXXConstructExpr::CK_VirtualBase: 165 if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) { 166 // We just finished a base constructor. Now we can use the subclass's 167 // type when resolving virtual calls. 168 const Decl *D = C.getLocationContext()->getDecl(); 169 recordFixedType(Target, cast<CXXConstructorDecl>(D), C); 170 } 171 return; 172 } 173 } 174} 175 176void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE, 177 CheckerContext &C) const { 178 // We only track dynamic type info for regions. 179 const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); 180 if (!ToR) 181 return; 182 183 switch (CastE->getCastKind()) { 184 default: 185 break; 186 case CK_BitCast: 187 // Only handle ObjCObjects for now. 188 if (const Type *NewTy = getBetterObjCType(CastE, C)) 189 C.addTransition(C.getState()->setDynamicTypeInfo(ToR, QualType(NewTy,0))); 190 break; 191 } 192 return; 193} 194 195void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, 196 CheckerContext &C) const { 197 if (NewE->isArray()) 198 return; 199 200 // We only track dynamic type info for regions. 201 const MemRegion *MR = C.getSVal(NewE).getAsRegion(); 202 if (!MR) 203 return; 204 205 C.addTransition(C.getState()->setDynamicTypeInfo(MR, NewE->getType(), 206 /*CanBeSubclass=*/false)); 207} 208 209const ObjCObjectType * 210DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, 211 CheckerContext &C) const { 212 if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) { 213 if (const ObjCObjectType *ObjTy 214 = MsgE->getClassReceiver()->getAs<ObjCObjectType>()) 215 return ObjTy; 216 } 217 218 if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) { 219 if (const ObjCObjectType *ObjTy 220 = MsgE->getSuperType()->getAs<ObjCObjectType>()) 221 return ObjTy; 222 } 223 224 const Expr *RecE = MsgE->getInstanceReceiver(); 225 if (!RecE) 226 return 0; 227 228 RecE= RecE->IgnoreParenImpCasts(); 229 if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) { 230 const StackFrameContext *SFCtx = C.getStackFrame(); 231 // Are we calling [self alloc]? If this is self, get the type of the 232 // enclosing ObjC class. 233 if (DRE->getDecl() == SFCtx->getSelfDecl()) { 234 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl())) 235 if (const ObjCObjectType *ObjTy = 236 dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl())) 237 return ObjTy; 238 } 239 } 240 return 0; 241} 242 243// Return a better dynamic type if one can be derived from the cast. 244// Compare the current dynamic type of the region and the new type to which we 245// are casting. If the new type is lower in the inheritance hierarchy, pick it. 246const ObjCObjectPointerType * 247DynamicTypePropagation::getBetterObjCType(const Expr *CastE, 248 CheckerContext &C) const { 249 const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); 250 assert(ToR); 251 252 // Get the old and new types. 253 const ObjCObjectPointerType *NewTy = 254 CastE->getType()->getAs<ObjCObjectPointerType>(); 255 if (!NewTy) 256 return 0; 257 QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType(); 258 if (OldDTy.isNull()) { 259 return NewTy; 260 } 261 const ObjCObjectPointerType *OldTy = 262 OldDTy->getAs<ObjCObjectPointerType>(); 263 if (!OldTy) 264 return 0; 265 266 // Id the old type is 'id', the new one is more precise. 267 if (OldTy->isObjCIdType() && !NewTy->isObjCIdType()) 268 return NewTy; 269 270 // Return new if it's a subclass of old. 271 const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl(); 272 const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl(); 273 if (ToI && FromI && FromI->isSuperClassOf(ToI)) 274 return NewTy; 275 276 return 0; 277} 278 279void ento::registerDynamicTypePropagation(CheckerManager &mgr) { 280 mgr.registerChecker<DynamicTypePropagation>(); 281} 282