• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
recordFixedType(const MemRegion * Region,const CXXMethodDecl * MD,CheckerContext & C)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 
checkPreCall(const CallEvent & Call,CheckerContext & C) const60 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 
checkPostCall(const CallEvent & Call,CheckerContext & C) const104 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 
checkPostStmt(const ImplicitCastExpr * CastE,CheckerContext & C) const176 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 
checkPostStmt(const CXXNewExpr * NewE,CheckerContext & C) const195 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 *
getObjectTypeForAllocAndNew(const ObjCMessageExpr * MsgE,CheckerContext & C) const210 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 *
getBetterObjCType(const Expr * CastE,CheckerContext & C) const247 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 
registerDynamicTypePropagation(CheckerManager & mgr)279 void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
280   mgr.registerChecker<DynamicTypePropagation>();
281 }
282