From c7ecc43c33a21b82c49664910b19fcc1f555aa51 Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Mon, 6 Aug 2012 23:25:39 +0000 Subject: [PATCH] [analyzer] Add a checker to manage dynamic type propagation. Instead of sprinkling dynamic type info propagation throughout ExprEngine, the added checker would add the more precise type information on known APIs (Ex: ObjC alloc, new) and propagate the type info in other cases (ex: ObjC init method, casts (the second is not implemented yet)). Add handling of ObjC alloc, new and init to the checker. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161357 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Core/PathSensitive/CheckerContext.h | 9 ++ .../Core/PathSensitive/ProgramState.h | 14 ++- lib/StaticAnalyzer/Checkers/CMakeLists.txt | 1 + lib/StaticAnalyzer/Checkers/Checkers.td | 4 + .../Checkers/DynamicTypePropagation.cpp | 116 ++++++++++++++++++ lib/StaticAnalyzer/Core/CallEvent.cpp | 9 +- lib/StaticAnalyzer/Core/ExprEngineC.cpp | 3 - lib/StaticAnalyzer/Core/ProgramState.cpp | 4 +- .../inlining/InlineObjCInstanceMethod.m | 2 +- .../Analysis/inlining/ObjCDynTypePopagation.m | 75 +++++++++++ 10 files changed, 223 insertions(+), 14 deletions(-) create mode 100644 lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp create mode 100644 test/Analysis/inlining/ObjCDynTypePopagation.m diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index b051d33cbd..0669b05570 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -92,6 +92,10 @@ public: return Pred->getLocationContext(); } + const StackFrameContext *getCurrentStackFrame() const { + return getLocationContext()->getCurrentStackFrame(); + } + BugReporter &getBugReporter() { return Eng.getBugReporter(); } @@ -132,6 +136,11 @@ public: return 0; } + /// \brief Get the value of arbitrary expressions at this point in the path. + SVal getSVal(const Stmt *S) const { + return getState()->getSVal(S, getLocationContext()); + } + /// \brief Generates a new transition in the program state graph /// (ExplodedGraph). Uses the default CheckerContext predecessor node. /// diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index 9d82ec6983..b38f3ac311 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -331,10 +331,18 @@ public: bool isTainted(SymbolRef Sym, TaintTagType Kind = TaintTagGeneric) const; bool isTainted(const MemRegion *Reg, TaintTagType Kind=TaintTagGeneric) const; - /// Get dynamic type information for a region. + /// \brief Get dynamic type information for a region. DynamicTypeInfo getDynamicTypeInfo(const MemRegion *Reg) const; - /// Add dynamic type information to the region and return the new state. - ProgramStateRef addDynamicTypeInfo(const MemRegion *Reg, QualType NewTy)const; + + /// \brief Add dynamic type information to the region; return the new state. + ProgramStateRef addDynamicTypeInfo(const MemRegion *Reg, + DynamicTypeInfo NewTy) const; + + /// \brief Add dynamic type information to the region; return the new state. + ProgramStateRef addDynamicTypeInfo(const MemRegion *Reg, + QualType NewTy) const { + return addDynamicTypeInfo(Reg, DynamicTypeInfo(NewTy)); + } //==---------------------------------------------------------------------==// // Accessing the Generic Data Map (GDM). diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 66ee42f7e9..7fe51d3a4c 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -29,6 +29,7 @@ add_clang_library(clangStaticAnalyzerCheckers DebugCheckers.cpp DereferenceChecker.cpp DivZeroChecker.cpp + DynamicTypePropagation.cpp ExprInspectionChecker.cpp FixedAddressChecker.cpp GenericTaintChecker.cpp diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 959c44f60d..8110bd0e18 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -84,6 +84,10 @@ def StackAddrEscapeChecker : Checker<"StackAddressEscape">, HelpText<"Check that addresses to stack memory do not escape the function">, DescFile<"StackAddrEscapeChecker.cpp">; +def DynamicTypePropagation : Checker<"DynamicTypePropagation">, + HelpText<"Generate dynamic type information">, + DescFile<"DynamicTypePropagation.cpp">; + } // end "core" let ParentPackage = CoreExperimental in { diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp new file mode 100644 index 0000000000..56610b5818 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -0,0 +1,116 @@ +//== DynamicTypePropagation.cpp ----------------------------------- -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker defines the rules for dynamic type gathering and propagation. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/Basic/Builtins.h" + +using namespace clang; +using namespace ento; + +namespace { +class DynamicTypePropagation : public Checker< check::PostCall > { + const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, + CheckerContext &C) const; +public: + void checkPostCall(const CallEvent &CE, CheckerContext &C) const; +}; +} + +void DynamicTypePropagation::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + // We can obtain perfect type info for return values from some calls. + if (const ObjCMethodCall *Msg = dyn_cast(&Call)) { + + // Get the returned value if it's a region. + SVal Result = C.getSVal(Call.getOriginExpr()); + const MemRegion *RetReg = Result.getAsRegion(); + if (!RetReg) + return; + + ProgramStateRef State = C.getState(); + + switch (Msg->getMethodFamily()) { + default: + break; + + // We assume that the type of the object returned by alloc and new are the + // pointer to the object of the class specified in the receiver of the + // message. + case OMF_alloc: + case OMF_new: { + // Get the type of object that will get created. + const ObjCMessageExpr *MsgE = Msg->getOriginExpr(); + const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C); + if (!ObjTy) + return; + QualType DynResTy = + C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0)); + C.addTransition(State->addDynamicTypeInfo(RetReg, DynResTy)); + break; + } + case OMF_init: { + // Assume, the result of the init method has the same dynamic type as + // the receiver and propagate the dynamic type info. + const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); + assert(RecReg); + DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg); + C.addTransition(State->addDynamicTypeInfo(RetReg, RecDynType)); + break; + } + } + } +} + +const ObjCObjectType * +DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, + CheckerContext &C) const { + if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) { + if (const ObjCObjectType *ObjTy + = MsgE->getClassReceiver()->getAs()) + return ObjTy; + } + + if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) { + if (const ObjCObjectType *ObjTy + = MsgE->getSuperType()->getAs()) + return ObjTy; + } + + const Expr *RecE = MsgE->getInstanceReceiver(); + if (!RecE) + return 0; + + RecE= RecE->IgnoreParenImpCasts(); + if (const DeclRefExpr *DRE = dyn_cast(RecE)) { + const StackFrameContext *SFCtx = C.getCurrentStackFrame(); + // Are we calling [self alloc]? If this is self, get the type of the + // enclosing ObjC class. + if (DRE->getDecl() == SFCtx->getSelfDecl()) { + if (const ObjCMethodDecl *MD = dyn_cast(SFCtx->getDecl())) + if (const ObjCObjectType *ObjTy = + dyn_cast(MD->getClassInterface()->getTypeForDecl())) + return ObjTy; + } + } + return 0; +} + +void ento::registerDynamicTypePropagation(CheckerManager &mgr) { + mgr.registerChecker(); +} diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 05147670c1..396e0f6d8d 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -583,8 +583,8 @@ SVal ObjCMethodCall::getReceiverSVal() const { if (!isInstanceMessage()) return UnknownVal(); - if (const Expr *Base = getOriginExpr()->getInstanceReceiver()) - return getSVal(Base); + if (const Expr *RecE = getOriginExpr()->getInstanceReceiver()) + return getSVal(RecE); // An instance message with no expression means we are sending to super. // In this case the object reference is the same as 'self'. @@ -676,8 +676,8 @@ const Decl *ObjCMethodCall::getRuntimeDefinition() const { if (!Receiver) return 0; - DynamicTypeInfo TI = getState()->getDynamicTypeInfo(Receiver); - ReceiverT = dyn_cast(TI.getType().getTypePtr()); + QualType DynType = getState()->getDynamicTypeInfo(Receiver).getType(); + ReceiverT = dyn_cast(DynType.getTypePtr()); } // Lookup the method implementation. @@ -715,7 +715,6 @@ void ObjCMethodCall::getInitialStackFrameContents( } } - CallEventRef CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State, const LocationContext *LCtx) { diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 740dec5ec4..7ec151ef6d 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -308,9 +308,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, const LocationContext *LCtx = Pred->getLocationContext(); SVal V = state->getSVal(Ex, LCtx); V = svalBuilder.evalCast(V, T, ExTy); - if (const MemRegion *R = V.getAsRegion()) { - state = state->addDynamicTypeInfo(R, T); - } state = state->BindExpr(CastE, LCtx, V); Bldr.generateNode(CastE, Pred, state); continue; diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index b44c470a90..c916c1a020 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -762,12 +762,12 @@ DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const { } ProgramStateRef ProgramState::addDynamicTypeInfo(const MemRegion *Reg, - QualType NewTy) const { + DynamicTypeInfo NewTy) const { if (const SymbolicRegion *SR = dyn_cast(Reg)) { SymbolRef Sym = SR->getSymbol(); // TODO: Instead of resetting the type info, check the old type info and // merge and pick the most precise type. - ProgramStateRef NewState = set(Sym, DynamicTypeInfo(NewTy)); + ProgramStateRef NewState = set(Sym, NewTy); assert(NewState); return NewState; } diff --git a/test/Analysis/inlining/InlineObjCInstanceMethod.m b/test/Analysis/inlining/InlineObjCInstanceMethod.m index 651d7c5b5a..d7b184e002 100644 --- a/test/Analysis/inlining/InlineObjCInstanceMethod.m +++ b/test/Analysis/inlining/InlineObjCInstanceMethod.m @@ -29,7 +29,7 @@ // Method is called on inited object. + (int)testAllocInit2 { MyClass *a = [[MyClass alloc] init]; - return 5/[a getInt]; // todo + return 5/[a getInt]; // expected-warning {{Division by zero}} } // Method is called on a parameter. diff --git a/test/Analysis/inlining/ObjCDynTypePopagation.m b/test/Analysis/inlining/ObjCDynTypePopagation.m new file mode 100644 index 0000000000..89c05c9c56 --- /dev/null +++ b/test/Analysis/inlining/ObjCDynTypePopagation.m @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=dynamic -verify %s + +typedef signed char BOOL; + +void clang_analyzer_eval(BOOL); + +@protocol NSObject - (BOOL)isEqual:(id)object; @end +@interface NSObject {} ++(id)alloc; +-(id)init; ++(id)new; +-(id)autorelease; +-(id)copy; +- (Class)class; +-(id)retain; +@end + +@interface MyParent : NSObject +- (int)getZeroOverridden; +@end +@implementation MyParent +- (int) getZeroOverridden { + return 1; +} +- (int) getZero { + return 0; +} +@end + +@interface MyClass : MyParent +- (int) getZeroOverridden; +@end + +MyClass *getObj(); + +@implementation MyClass +- (int) getZeroOverridden { + return 0; +} + +/* Test that we get the right type from call to alloc. */ + ++ (void) testAllocSelf { + id a = [self alloc]; + clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} +} + + ++ (void) testAllocClass { + id a = [MyClass alloc]; + clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} +} + ++ (void) testAllocSuperOverriden { + id a = [super alloc]; + // Evaluates to 1 in the parent. + clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}} +} + ++ (void) testAllocSuper { + id a = [super alloc]; + clang_analyzer_eval([a getZero] == 0); // expected-warning{{TRUE}} +} + ++ (void) testAllocInit { + id a = [[self alloc] init]; + clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} +} + ++ (void) testNewSelf { + id a = [self new]; + clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} +} + +@end \ No newline at end of file -- 2.40.0