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                     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;
     38 public:
     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 
     46 static 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 
     60 void 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 
    104 void 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 
    176 void 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 
    195 void 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 
    209 const ObjCObjectType *
    210 DynamicTypePropagation::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 nullptr;
    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 nullptr;
    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.
    246 const ObjCObjectPointerType *
    247 DynamicTypePropagation::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 nullptr;
    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 nullptr;
    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 nullptr;
    277 }
    278 
    279 void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
    280   mgr.registerChecker<DynamicTypePropagation>();
    281 }
    282