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