diff --git a/facebook-clang-plugins b/facebook-clang-plugins index 48d27425e..a9c545fec 160000 --- a/facebook-clang-plugins +++ b/facebook-clang-plugins @@ -1 +1 @@ -Subproject commit 48d27425e9e9e8d0bace06e9e7893cb23b21a2a4 +Subproject commit a9c545fecae6cc447c28d4535488986941858ab1 diff --git a/infer/src/IR/Objc_models.ml b/infer/src/IR/Objc_models.ml index 5cf779ae2..5e8d74789 100644 --- a/infer/src/IR/Objc_models.ml +++ b/infer/src/IR/Objc_models.ml @@ -193,7 +193,5 @@ module Core_foundation_model = struct && (String.is_substring ~substring:create funct || String.is_substring ~substring:copy funct) end -let is_core_lib_type typ = Core_foundation_model.is_core_lib_type typ - let is_malloc_model return_type pname = Core_foundation_model.is_core_lib_create return_type (Procname.to_string pname) diff --git a/infer/src/IR/Objc_models.mli b/infer/src/IR/Objc_models.mli index c0c816568..f2c663b15 100644 --- a/infer/src/IR/Objc_models.mli +++ b/infer/src/IR/Objc_models.mli @@ -10,6 +10,4 @@ open! IStd (** This module models special c struct types from the Apple's Core Foundation libraries for which there are particular rules for memory management. *) -val is_core_lib_type : Typ.t -> bool - val is_malloc_model : Typ.t -> Procname.t -> bool diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 7543f7e91..a66f372c4 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -2557,7 +2557,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s (** Cast expression are treated the same apart from the cast operation kind *) - and cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_expr_info = + and cast_exprs_trans trans_state stmt_info stmt_list expr_info ?objc_bridge_cast_kind + cast_expr_info = let context = trans_state.context in L.(debug Capture Verbose) " priority node free = '%s'@\n@." @@ -2576,7 +2577,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let cast_kind = cast_expr_info.Clang_ast_t.cei_cast_kind in let exp_typ = res_trans_stmt.return in (* This gives the difference among cast operations kind *) - let cast_inst, cast_exp = cast_operation cast_kind exp_typ typ sil_loc in + let cast_inst, cast_exp = cast_operation ?objc_bridge_cast_kind cast_kind exp_typ typ sil_loc in { res_trans_stmt with control= {res_trans_stmt.control with instrs= res_trans_stmt.control.instrs @ cast_inst} ; return= cast_exp } @@ -3530,7 +3531,6 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s pseudoObjectExpr_trans trans_state stmt_list | UnaryExprOrTypeTraitExpr (_, _, _, unary_expr_or_type_trait_expr_info) -> unaryExprOrTypeTraitExpr_trans trans_state unary_expr_or_type_trait_expr_info - | ObjCBridgedCastExpr (stmt_info, stmt_list, expr_info, cast_kind, _) | ImplicitCastExpr (stmt_info, stmt_list, expr_info, cast_kind) | BuiltinBitCastExpr (stmt_info, stmt_list, expr_info, cast_kind, _) | CStyleCastExpr (stmt_info, stmt_list, expr_info, cast_kind, _) @@ -3539,6 +3539,9 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | CXXStaticCastExpr (stmt_info, stmt_list, expr_info, cast_kind, _, _) | CXXFunctionalCastExpr (stmt_info, stmt_list, expr_info, cast_kind, _) -> cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_kind + | ObjCBridgedCastExpr (stmt_info, stmt_list, expr_info, cast_kind, _, objc_bridge_cast_ei) -> + let objc_bridge_cast_kind = objc_bridge_cast_ei.Clang_ast_t.obcei_cast_kind in + cast_exprs_trans trans_state stmt_info stmt_list expr_info ~objc_bridge_cast_kind cast_kind | IntegerLiteral (_, _, expr_info, integer_literal_info) -> integerLiteral_trans trans_state expr_info integer_literal_info | OffsetOfExpr (stmt_info, _, expr_info, offset_of_expr_info) -> diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index e459db8b0..913506726 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -424,7 +424,7 @@ let dereference_value_from_result ?(strip_pointer = false) source_range sil_loc ; return= (cast_exp, cast_typ) } -let cast_operation cast_kind ((exp, typ) as exp_typ) cast_typ sil_loc = +let cast_operation ?objc_bridge_cast_kind cast_kind ((exp, typ) as exp_typ) cast_typ sil_loc = match cast_kind with | `NoOp | `DerivedToBase | `UncheckedDerivedToBase -> (* These casts ignore change of type *) @@ -436,10 +436,6 @@ let cast_operation cast_kind ((exp, typ) as exp_typ) cast_typ sil_loc = | `BitCast | `IntegralCast | `IntegralToBoolean -> (* This is treated as a nop by returning the same expressions exps*) ([], (exp, cast_typ)) - | `CPointerToObjCPointerCast when Objc_models.is_core_lib_type typ -> - (* Translation of __bridge_transfer *) - let instr = create_call_to_free_cf sil_loc exp typ in - ([instr], (exp, cast_typ)) | `LValueToRValue -> (* Takes an LValue and allow it to use it as RValue. *) (* So we assign the LValue to a temp and we pass it to the parent.*) @@ -457,11 +453,25 @@ let cast_operation cast_kind ((exp, typ) as exp_typ) cast_typ sil_loc = Sil.Call ((no_id, cast_typ), skip_builtin, args, sil_loc, CallFlags.default) in ([call_instr], (exp, cast_typ)) - | _ -> - L.(debug Capture Verbose) - "@\nWARNING: Missing translation for Cast Kind %s. The construct has been ignored...@\n" - (Clang_ast_j.string_of_cast_kind cast_kind) ; - ([], (exp, cast_typ)) + | _ -> ( + match objc_bridge_cast_kind with + | Some `OBC_BridgeTransfer -> + let instr = create_call_to_free_cf sil_loc exp typ in + ([instr], (exp, cast_typ)) + | Some cast_kind -> + L.debug Capture Verbose + "@\n\ + WARNING: Missing translation for ObjC Bridge Cast Kind %a. The construct has been \ + ignored...@\n" + (Pp.of_string ~f:Clang_ast_j.string_of_obj_c_bridge_cast_kind) + cast_kind ; + ([], (exp, cast_typ)) + | _ -> + L.debug Capture Verbose + "@\nWARNING: Missing translation for Cast Kind %a. The construct has been ignored...@\n" + (Pp.of_string ~f:Clang_ast_j.string_of_cast_kind) + cast_kind ; + ([], (exp, cast_typ)) ) let trans_assertion_failure sil_loc (context : CContext.t) = diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index e506305a9..966bbe8a6 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -90,7 +90,12 @@ val dereference_value_from_result : assigned to it *) val cast_operation : - Clang_ast_t.cast_kind -> Exp.t * Typ.t -> Typ.t -> Location.t -> Sil.instr list * (Exp.t * Typ.t) + ?objc_bridge_cast_kind:Clang_ast_t.obj_c_bridge_cast_kind + -> Clang_ast_t.cast_kind + -> Exp.t * Typ.t + -> Typ.t + -> Location.t + -> Sil.instr list * (Exp.t * Typ.t) val trans_assertion : trans_state -> Location.t -> trans_result diff --git a/infer/src/pulse/PulseModels.ml b/infer/src/pulse/PulseModels.ml index fbbcd713f..69b4f0591 100644 --- a/infer/src/pulse/PulseModels.ml +++ b/infer/src/pulse/PulseModels.ml @@ -675,6 +675,7 @@ module ProcNameDispatcher = struct ; -"CFRelease" <>$ capt_arg_payload $--> C.free ; -"CFAutorelease" <>$ capt_arg_payload $--> C.free ; -"CFBridgingRelease" <>$ capt_arg_payload $--> C.cf_bridging_release + ; +match_builtin BuiltinDecl.__free_cf <>$ capt_arg_payload $--> C.cf_bridging_release ; +PatternMatch.ObjectiveC.is_modelled_as_alloc &++> C.malloc_not_null ; +PatternMatch.ObjectiveC.is_modelled_as_free <>$ capt_arg_payload $--> C.free ] end diff --git a/infer/tests/codetoanalyze/objc/errors/issues.exp b/infer/tests/codetoanalyze/objc/errors/issues.exp index 4c4cc3b08..249024bfc 100644 --- a/infer/tests/codetoanalyze/objc/errors/issues.exp +++ b/infer/tests/codetoanalyze/objc/errors/issues.exp @@ -71,6 +71,7 @@ codetoanalyze/objc/errors/subtyping/KindOfClassExample.m, shouldThrowDivideByZer codetoanalyze/objc/errors/subtyping/KindOfClassExample.m, shouldThrowDivideByZero2, 2, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure shouldThrowDivideByZero2(),start of procedure init,return from a call to Base::init,start of procedure returnsZero2(),Taking false branch,return from a call to returnsZero2] codetoanalyze/objc/errors/subtyping/KindOfClassExample.m, shouldThrowDivideByZero3, 3, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure shouldThrowDivideByZero3(),start of procedure init,return from a call to Derived::init,Taking true branch] codetoanalyze/objc/errors/variadic_methods/premature_nil_termination.m, PrematureNilTermA::nilInArrayWithObjects, 5, PREMATURE_NIL_TERMINATION_ARGUMENT, B1, ERROR, [start of procedure nilInArrayWithObjects] +codetoanalyze/objc/shared/memory_leaks_benchmark/TollBridgeExample.m, TollBridgeExample::bridge, 2, MEMORY_LEAK, no_bucket, ERROR, [start of procedure bridge] codetoanalyze/objc/errors/memory_leaks_benchmark/CoreVideoExample.m, CoreVideoExample::cvpixelbuffer_not_released_leak, 1, MEMORY_LEAK, no_bucket, ERROR, [start of procedure cvpixelbuffer_not_released_leak] codetoanalyze/objc/errors/memory_leaks_benchmark/NSData_models_tests.m, NSData_models_tests::macForIV:, 2, MEMORY_LEAK, no_bucket, ERROR, [start of procedure macForIV:] codetoanalyze/objc/errors/memory_leaks_benchmark/NSString_models_tests.m, StringInitA::hexStringValue, 11, MEMORY_LEAK, no_bucket, ERROR, [start of procedure hexStringValue,Skipping CFStringCreateWithBytesNoCopy(): method has no implementation,Taking false branch] diff --git a/infer/tests/codetoanalyze/objc/pulse/AllocPatternMemLeak.m b/infer/tests/codetoanalyze/objc/pulse/AllocPatternMemLeak.m index 65dadf16f..d349ca4c5 100644 --- a/infer/tests/codetoanalyze/objc/pulse/AllocPatternMemLeak.m +++ b/infer/tests/codetoanalyze/objc/pulse/AllocPatternMemLeak.m @@ -10,6 +10,9 @@ typedef struct ABFDataRef { } ABFDataRef; +@interface ABFData +@end + ABFDataRef* ABFDataCreate(size_t size); void ABFRelease(ABFDataRef*); @@ -27,4 +30,8 @@ void ABFRelease(ABFDataRef*); ABFRelease(someData); } +- (void)bridge_no_leak_good { + ABFData* someData = (__bridge_transfer ABFData*)ABFDataCreate(4); +} + @end diff --git a/infer/tests/codetoanalyze/objc/pulse/Makefile b/infer/tests/codetoanalyze/objc/pulse/Makefile index 18b58cb7f..a234390fb 100644 --- a/infer/tests/codetoanalyze/objc/pulse/Makefile +++ b/infer/tests/codetoanalyze/objc/pulse/Makefile @@ -5,7 +5,7 @@ TESTS_DIR = ../../.. -CLANG_OPTIONS = -c $(OBJC_CLANG_OPTIONS) +CLANG_OPTIONS = -c $(OBJC_CLANG_OPTIONS) -fobjc-arc INFER_OPTIONS = --pulse-only --debug-exceptions --project-root $(TESTS_DIR) \ --pulse-model-alloc-pattern "AB[IF].*Create.*\|CFLocaleCreate" \ --pulse-model-free-pattern ABFRelease diff --git a/infer/tests/codetoanalyze/objc/pulse/MemoryLeaks.m b/infer/tests/codetoanalyze/objc/pulse/MemoryLeaks.m index 7706e754a..2329b8b78 100644 --- a/infer/tests/codetoanalyze/objc/pulse/MemoryLeaks.m +++ b/infer/tests/codetoanalyze/objc/pulse/MemoryLeaks.m @@ -62,4 +62,9 @@ CFLocaleRef nameRef = CFLocaleCreate(NULL, NULL); } +- (void)bridge_transfer_no_leak_good { + CFLocaleRef nameRef = CFLocaleCreate(NULL, NULL); + NSLocale* locale = (__bridge_transfer NSLocale*)nameRef; +} + @end diff --git a/infer/tests/codetoanalyze/objc/shared/memory_leaks_benchmark/TollBridgeExample.m.dot b/infer/tests/codetoanalyze/objc/shared/memory_leaks_benchmark/TollBridgeExample.m.dot index 5f1534875..693632f3a 100644 --- a/infer/tests/codetoanalyze/objc/shared/memory_leaks_benchmark/TollBridgeExample.m.dot +++ b/infer/tests/codetoanalyze/objc/shared/memory_leaks_benchmark/TollBridgeExample.m.dot @@ -33,11 +33,11 @@ digraph cfg { "_readHTTPHeader#TollBridgeExample#instance.3d37ce88cf13750e89ba404865a70554_2" [label="2: Exit TollBridgeExample::_readHTTPHeader \n " color=yellow style=filled] -"_readHTTPHeader#TollBridgeExample#instance.3d37ce88cf13750e89ba404865a70554_3" [label="3: Call _fun_CFBridgingRelease \n n$8=*&ref:__CFDictionary const * [line 35, column 21]\n n$9=_fun_CFBridgingRelease(n$8:void const *) [line 35, column 3]\n " shape="box"] +"_readHTTPHeader#TollBridgeExample#instance.3d37ce88cf13750e89ba404865a70554_3" [label="3: Call _fun_CFBridgingRelease \n n$6=*&ref:__CFDictionary const * [line 35, column 21]\n n$7=_fun_CFBridgingRelease(n$6:void const *) [line 35, column 3]\n " shape="box"] "_readHTTPHeader#TollBridgeExample#instance.3d37ce88cf13750e89ba404865a70554_3" -> "_readHTTPHeader#TollBridgeExample#instance.3d37ce88cf13750e89ba404865a70554_2" ; -"_readHTTPHeader#TollBridgeExample#instance.3d37ce88cf13750e89ba404865a70554_4" [label="4: DeclStmt \n VARIABLE_DECLARED(ref:__CFDictionary const *); [line 34, column 3]\n n$10=_fun_CFHTTPMessageCopyAllHeaderFields(null:__CFHTTPMessage*) [line 34, column 25]\n *&ref:__CFDictionary const *=n$10 [line 34, column 3]\n " shape="box"] +"_readHTTPHeader#TollBridgeExample#instance.3d37ce88cf13750e89ba404865a70554_4" [label="4: DeclStmt \n VARIABLE_DECLARED(ref:__CFDictionary const *); [line 34, column 3]\n n$8=_fun_CFHTTPMessageCopyAllHeaderFields(null:__CFHTTPMessage*) [line 34, column 25]\n *&ref:__CFDictionary const *=n$8 [line 34, column 3]\n " shape="box"] "_readHTTPHeader#TollBridgeExample#instance.3d37ce88cf13750e89ba404865a70554_4" -> "_readHTTPHeader#TollBridgeExample#instance.3d37ce88cf13750e89ba404865a70554_3" ; @@ -48,11 +48,11 @@ digraph cfg { "brideRetained#TollBridgeExample#instance.de039e838ea3246eff789fdc0d11405c_2" [label="2: Exit TollBridgeExample::brideRetained \n " color=yellow style=filled] -"brideRetained#TollBridgeExample#instance.de039e838ea3246eff789fdc0d11405c_3" [label="3: DeclStmt \n VARIABLE_DECLARED(a:__CFLocale const *); [line 29, column 3]\n n$6=*&observer:objc_object* [line 29, column 50]\n *&a:__CFLocale const *=n$6 [line 29, column 3]\n " shape="box"] +"brideRetained#TollBridgeExample#instance.de039e838ea3246eff789fdc0d11405c_3" [label="3: DeclStmt \n VARIABLE_DECLARED(a:__CFLocale const *); [line 29, column 3]\n n$4=*&observer:objc_object* [line 29, column 50]\n *&a:__CFLocale const *=n$4 [line 29, column 3]\n " shape="box"] "brideRetained#TollBridgeExample#instance.de039e838ea3246eff789fdc0d11405c_3" -> "brideRetained#TollBridgeExample#instance.de039e838ea3246eff789fdc0d11405c_2" ; -"brideRetained#TollBridgeExample#instance.de039e838ea3246eff789fdc0d11405c_4" [label="4: DeclStmt \n VARIABLE_DECLARED(observer:objc_object*); [line 28, column 3]\n n$7=_fun___objc_alloc_no_fail(sizeof(t=NSLocale):unsigned long) [line 28, column 17]\n *&observer:objc_object*=n$7 [line 28, column 3]\n " shape="box"] +"brideRetained#TollBridgeExample#instance.de039e838ea3246eff789fdc0d11405c_4" [label="4: DeclStmt \n VARIABLE_DECLARED(observer:objc_object*); [line 28, column 3]\n n$5=_fun___objc_alloc_no_fail(sizeof(t=NSLocale):unsigned long) [line 28, column 17]\n *&observer:objc_object*=n$5 [line 28, column 3]\n " shape="box"] "brideRetained#TollBridgeExample#instance.de039e838ea3246eff789fdc0d11405c_4" -> "brideRetained#TollBridgeExample#instance.de039e838ea3246eff789fdc0d11405c_3" ; @@ -63,11 +63,11 @@ digraph cfg { "bridge#TollBridgeExample#instance.fadd5a014118113c960fa1a6e3ff27ba_2" [label="2: Exit TollBridgeExample::bridge \n " color=yellow style=filled] -"bridge#TollBridgeExample#instance.fadd5a014118113c960fa1a6e3ff27ba_3" [label="3: DeclStmt \n VARIABLE_DECLARED(a:NSLocale*); [line 24, column 3]\n n$3=*&nameRef:__CFLocale const * [line 24, column 37]\n n$4=_fun___free_cf(n$3:__CFLocale const *) [line 24, column 17]\n *&a:NSLocale*=n$3 [line 24, column 3]\n " shape="box"] +"bridge#TollBridgeExample#instance.fadd5a014118113c960fa1a6e3ff27ba_3" [label="3: DeclStmt \n VARIABLE_DECLARED(a:NSLocale*); [line 24, column 3]\n n$2=*&nameRef:__CFLocale const * [line 24, column 37]\n *&a:NSLocale*=n$2 [line 24, column 3]\n " shape="box"] "bridge#TollBridgeExample#instance.fadd5a014118113c960fa1a6e3ff27ba_3" -> "bridge#TollBridgeExample#instance.fadd5a014118113c960fa1a6e3ff27ba_2" ; -"bridge#TollBridgeExample#instance.fadd5a014118113c960fa1a6e3ff27ba_4" [label="4: DeclStmt \n VARIABLE_DECLARED(nameRef:__CFLocale const *); [line 23, column 3]\n n$5=_fun_CFLocaleCreate(null:__CFAllocator const *,null:__CFString const *) [line 23, column 25]\n *&nameRef:__CFLocale const *=n$5 [line 23, column 3]\n " shape="box"] +"bridge#TollBridgeExample#instance.fadd5a014118113c960fa1a6e3ff27ba_4" [label="4: DeclStmt \n VARIABLE_DECLARED(nameRef:__CFLocale const *); [line 23, column 3]\n n$3=_fun_CFLocaleCreate(null:__CFAllocator const *,null:__CFString const *) [line 23, column 25]\n *&nameRef:__CFLocale const *=n$3 [line 23, column 3]\n " shape="box"] "bridge#TollBridgeExample#instance.fadd5a014118113c960fa1a6e3ff27ba_4" -> "bridge#TollBridgeExample#instance.fadd5a014118113c960fa1a6e3ff27ba_3" ; @@ -78,11 +78,11 @@ digraph cfg { "bridgeTransfer#TollBridgeExample#instance.d0065913beb197e891ef0d8a0bb81b38_2" [label="2: Exit TollBridgeExample::bridgeTransfer \n " color=yellow style=filled] -"bridgeTransfer#TollBridgeExample#instance.d0065913beb197e891ef0d8a0bb81b38_3" [label="3: DeclStmt \n VARIABLE_DECLARED(a:NSLocale*); [line 19, column 3]\n n$0=*&nameRef:__CFLocale const * [line 19, column 46]\n n$1=_fun___free_cf(n$0:__CFLocale const *) [line 19, column 17]\n *&a:NSLocale*=n$0 [line 19, column 3]\n " shape="box"] +"bridgeTransfer#TollBridgeExample#instance.d0065913beb197e891ef0d8a0bb81b38_3" [label="3: DeclStmt \n VARIABLE_DECLARED(a:NSLocale*); [line 19, column 3]\n n$0=*&nameRef:__CFLocale const * [line 19, column 46]\n *&a:NSLocale*=n$0 [line 19, column 3]\n " shape="box"] "bridgeTransfer#TollBridgeExample#instance.d0065913beb197e891ef0d8a0bb81b38_3" -> "bridgeTransfer#TollBridgeExample#instance.d0065913beb197e891ef0d8a0bb81b38_2" ; -"bridgeTransfer#TollBridgeExample#instance.d0065913beb197e891ef0d8a0bb81b38_4" [label="4: DeclStmt \n VARIABLE_DECLARED(nameRef:__CFLocale const *); [line 18, column 3]\n n$2=_fun_CFLocaleCreate(null:__CFAllocator const *,null:__CFString const *) [line 18, column 25]\n *&nameRef:__CFLocale const *=n$2 [line 18, column 3]\n " shape="box"] +"bridgeTransfer#TollBridgeExample#instance.d0065913beb197e891ef0d8a0bb81b38_4" [label="4: DeclStmt \n VARIABLE_DECLARED(nameRef:__CFLocale const *); [line 18, column 3]\n n$1=_fun_CFLocaleCreate(null:__CFAllocator const *,null:__CFString const *) [line 18, column 25]\n *&nameRef:__CFLocale const *=n$1 [line 18, column 3]\n " shape="box"] "bridgeTransfer#TollBridgeExample#instance.d0065913beb197e891ef0d8a0bb81b38_4" -> "bridgeTransfer#TollBridgeExample#instance.d0065913beb197e891ef0d8a0bb81b38_3" ;