From: Fariborz Jahanian Date: Mon, 30 Jan 2012 21:40:37 +0000 (+0000) Subject: objc-arc: Perform null check on receiver before sending methods which X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3c267e6262bb319c8e56c9ea1283c7087f9ac107;p=clang objc-arc: Perform null check on receiver before sending methods which consume one or more of their arguments. If not done, this will cause a leak as method will not consume the argument when receiver is null. In this patch, the null path releases consumed argument. // rdar://10444474 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149279 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index ecf6181b1c..0133ad2dbd 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -1414,8 +1414,16 @@ struct NullReturnState { CGF.EmitBlock(callBB); } - RValue complete(CodeGenFunction &CGF, RValue result, QualType resultType) { + RValue complete(CodeGenFunction &CGF, RValue result, QualType resultType, + const CallArgList &CallArgs, + const ObjCMethodDecl *Method) { if (!NullBB) return result; + + llvm::Value *NullInitPtr = 0; + if (result.isScalar() && !resultType->isVoidType()) { + NullInitPtr = CGF.CreateTempAlloca(result.getScalarVal()->getType()); + CGF.Builder.CreateStore(result.getScalarVal(), NullInitPtr); + } // Finish the call path. llvm::BasicBlock *contBB = CGF.createBasicBlock("msgSend.cont"); @@ -1423,6 +1431,31 @@ struct NullReturnState { // Emit the null-init block and perform the null-initialization there. CGF.EmitBlock(NullBB); + + // Release consumed arguments along the null-receiver path. + if (Method) { + CallArgList::const_iterator I = CallArgs.begin(); + for (ObjCMethodDecl::param_const_iterator i = Method->param_begin(), + e = Method->param_end(); i != e; ++i, ++I) { + const ParmVarDecl *ParamDecl = (*i); + if (ParamDecl->hasAttr()) { + RValue RV = I->RV; + assert(RV.isScalar() && + "NullReturnState::complete - arg not on object"); + CGF.EmitARCRelease(RV.getScalarVal(), true); + } + } + } + + if (result.isScalar()) { + if (NullInitPtr) + CGF.EmitNullInitialization(NullInitPtr, resultType); + // Jump to the continuation block. + CGF.EmitBlock(contBB); + return NullInitPtr ? RValue::get(CGF.Builder.CreateLoad(NullInitPtr)) + : result; + } + if (!resultType->isAnyComplexType()) { assert(result.isAggregate() && "null init of non-aggregate result?"); CGF.EmitNullInitialization(result.getAggregateAddr(), resultType); @@ -1668,9 +1701,24 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper) : ObjCTypes.getSendFn(IsSuper); } + + bool requiresnullCheck = false; + if (CGM.getLangOptions().ObjCAutoRefCount && Method) + for (ObjCMethodDecl::param_const_iterator i = Method->param_begin(), + e = Method->param_end(); i != e; ++i) { + const ParmVarDecl *ParamDecl = (*i); + if (ParamDecl->hasAttr()) { + if (!nullReturn.NullBB) + nullReturn.init(CGF, Arg0); + requiresnullCheck = true; + break; + } + } + Fn = llvm::ConstantExpr::getBitCast(Fn, llvm::PointerType::getUnqual(FTy)); RValue rvalue = CGF.EmitCall(FnInfo, Fn, Return, ActualArgs); - return nullReturn.complete(CGF, rvalue, ResultType); + return nullReturn.complete(CGF, rvalue, ResultType, CallArgs, + requiresnullCheck ? Method : 0); } static Qualifiers::GC GetGCAttrTypeForType(ASTContext &Ctx, QualType FQT) { @@ -5785,7 +5833,8 @@ CGObjCNonFragileABIMac::EmitVTableMessageSend(CodeGenFunction &CGF, llvm::PointerType::getUnqual(fnType)); RValue result = CGF.EmitCall(fnInfo, callee, returnSlot, args); - return nullReturn.complete(CGF, result, resultType); + CallArgList CallArgs; + return nullReturn.complete(CGF, result, resultType, CallArgs, 0); } /// Generate code for a message send expression in the nonfragile abi. diff --git a/test/CodeGenObjC/arc.m b/test/CodeGenObjC/arc.m index c5ffb18731..97abb47243 100644 --- a/test/CodeGenObjC/arc.m +++ b/test/CodeGenObjC/arc.m @@ -1489,8 +1489,13 @@ void test66(void) { // CHECK-NEXT: [[T5:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T4]]) // CHECK-NEXT: [[T6:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES // CHECK-NEXT: [[T7:%.*]] = bitcast [[TEST66]]* [[T3]] to i8* -// CHECK-NEXT: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i8*)*)(i8* [[T7]], i8* [[T6]], i8* [[T5]]) -// CHECK-NEXT: [[T8:%.*]] = bitcast [[TEST66]]* [[T3]] to i8* +// CHECK-NEXT: [[SIX:%.*]] = icmp eq i8* [[T7]], null +// CHECK-NEXT: br i1 [[SIX]], label [[NULINIT:%.*]], label [[CALL:%.*]] +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i8*)*)(i8* [[T7]], i8* [[T6]], i8* [[T5]]) +// CHECK-NEXT: br label [[CONT:%.*]] +// CHECK: call void @objc_release(i8* [[T5]]) nounwind +// CHECK-NEXT: br label [[CONT:%.*]] +// CHECK: [[T8:%.*]] = bitcast [[TEST66]]* [[T3]] to i8* // CHECK-NEXT: call void @objc_release(i8* [[T8]]) // CHECK-NEXT: ret void