[pulse] Model Core Graphics create and copy functions

Summary:
This models all the Create and Copy functions from CoreGraphics, examples in the tests.
These functions all allocate memory that needs to be manually released.

The modelling of the release functions will happen in a following diff. Until then, we have some false positives in the tests.

This check is currently in biabduction, and we aim to move it to Pulse.

Reviewed By: jvillard

Differential Revision: D20626395

fbshipit-source-id: b39eae2d9
master
Dulma Churchill 5 years ago committed by Facebook GitHub Bot
parent 1e25cf2168
commit 6f2b52fcc7

@ -472,3 +472,11 @@ let is_override_of_java_lang_object_equals curr_pname =
in in
String.equal (Procname.get_method curr_pname) "equals" String.equal (Procname.get_method curr_pname) "equals"
&& is_only_param_of_object_type (Procname.get_parameters curr_pname) && is_only_param_of_object_type (Procname.get_parameters curr_pname)
module ObjectiveC = struct
let is_core_graphics_create_or_copy _ procname =
String.is_prefix ~prefix:"CG" procname
&& ( String.is_substring ~substring:"Create" procname
|| String.is_substring ~substring:"Copy" procname )
end

@ -160,3 +160,7 @@ val has_same_signature : Procname.t -> (Procname.t -> bool) Staged.t
val is_override_of_java_lang_object_equals : Procname.t -> bool val is_override_of_java_lang_object_equals : Procname.t -> bool
(** Whether the method is an override of `java.lang.Object.equals(Object)` or (** Whether the method is an override of `java.lang.Object.equals(Object)` or
`java.lang.Object.equals(Object)` itself *) `java.lang.Object.equals(Object)` itself *)
module ObjectiveC : sig
val is_core_graphics_create_or_copy : Tenv.t -> string -> bool
end

@ -213,8 +213,8 @@ module AddressAttributes = struct
map_post_attrs astate ~f:(BaseAddressAttributes.invalidate address invalidation location) map_post_attrs astate ~f:(BaseAddressAttributes.invalidate address invalidation location)
let allocate address location astate = let allocate procname address location astate =
map_post_attrs astate ~f:(BaseAddressAttributes.allocate address location) map_post_attrs astate ~f:(BaseAddressAttributes.allocate procname address location)
let add_one address attributes astate = let add_one address attributes astate =
@ -696,8 +696,9 @@ module PrePost = struct
(invalidation, add_call_to_trace proc_name call_location caller_history trace) (invalidation, add_call_to_trace proc_name call_location caller_history trace)
| CItv (arith, trace) -> | CItv (arith, trace) ->
Attribute.CItv (arith, add_call_to_trace proc_name call_location caller_history trace) Attribute.CItv (arith, add_call_to_trace proc_name call_location caller_history trace)
| Allocated trace -> | Allocated (procname, trace) ->
Attribute.Allocated (add_call_to_trace proc_name call_location caller_history trace) Attribute.Allocated
(procname, add_call_to_trace proc_name call_location caller_history trace)
| AddressOfCppTemporary _ | AddressOfCppTemporary _
| AddressOfStackVariable _ | AddressOfStackVariable _
| BoItv _ | BoItv _

@ -77,7 +77,7 @@ module AddressAttributes : sig
val invalidate : AbstractValue.t * ValueHistory.t -> Invalidation.t -> Location.t -> t -> t val invalidate : AbstractValue.t * ValueHistory.t -> Invalidation.t -> Location.t -> t -> t
val allocate : AbstractValue.t * ValueHistory.t -> Location.t -> t -> t val allocate : Procname.t -> AbstractValue.t * ValueHistory.t -> Location.t -> t -> t
val get_closure_proc_name : AbstractValue.t -> t -> Procname.t option val get_closure_proc_name : AbstractValue.t -> t -> Procname.t option

@ -28,7 +28,7 @@ module Attribute = struct
type t = type t =
| AddressOfCppTemporary of Var.t * ValueHistory.t | AddressOfCppTemporary of Var.t * ValueHistory.t
| AddressOfStackVariable of Var.t * Location.t * ValueHistory.t | AddressOfStackVariable of Var.t * Location.t * ValueHistory.t
| Allocated of Trace.t | Allocated of Procname.t * Trace.t
| CItv of CItv.t * Trace.t | CItv of CItv.t * Trace.t
| BoItv of Itv.ItvPure.t | BoItv of Itv.ItvPure.t
| Closure of Procname.t | Closure of Procname.t
@ -76,8 +76,11 @@ module Attribute = struct
F.fprintf f "t&%a (%a)" Var.pp var ValueHistory.pp history F.fprintf f "t&%a (%a)" Var.pp var ValueHistory.pp history
| AddressOfStackVariable (var, location, history) -> | AddressOfStackVariable (var, location, history) ->
F.fprintf f "s&%a (%a) at %a" Var.pp var ValueHistory.pp history Location.pp location F.fprintf f "s&%a (%a) at %a" Var.pp var ValueHistory.pp history Location.pp location
| Allocated trace -> | Allocated (procname, trace) ->
F.fprintf f "Allocated %a" (Trace.pp ~pp_immediate:(pp_string_if_debug "allocation")) trace F.fprintf f "Allocated %a"
(Trace.pp
~pp_immediate:(pp_string_if_debug ("allocation with " ^ Procname.to_string procname)))
trace
| BoItv bo_itv -> | BoItv bo_itv ->
F.fprintf f "BoItv (%a)" Itv.ItvPure.pp bo_itv F.fprintf f "BoItv (%a)" Itv.ItvPure.pp bo_itv
| Closure pname -> | Closure pname ->

@ -14,7 +14,8 @@ module ValueHistory = PulseValueHistory
type t = type t =
| AddressOfCppTemporary of Var.t * ValueHistory.t | AddressOfCppTemporary of Var.t * ValueHistory.t
| AddressOfStackVariable of Var.t * Location.t * ValueHistory.t | AddressOfStackVariable of Var.t * Location.t * ValueHistory.t
| Allocated of Trace.t | Allocated of Procname.t * Trace.t
(** the {!Procname.t} is the function causing the allocation, eg [malloc] *)
| CItv of CItv.t * Trace.t | CItv of CItv.t * Trace.t
| BoItv of Itv.ItvPure.t | BoItv of Itv.ItvPure.t
| Closure of Procname.t | Closure of Procname.t

@ -53,8 +53,8 @@ let invalidate (address, history) invalidation location memory =
add_one address (Attribute.Invalid (invalidation, Immediate {location; history})) memory add_one address (Attribute.Invalid (invalidation, Immediate {location; history})) memory
let allocate (address, history) location memory = let allocate procname (address, history) location memory =
add_one address (Attribute.Allocated (Immediate {location; history})) memory add_one address (Attribute.Allocated (procname, Immediate {location; history})) memory
let check_valid address attrs = let check_valid address attrs =

@ -22,7 +22,7 @@ val add_one : AbstractValue.t -> Attribute.t -> t -> t
val add : AbstractValue.t -> Attributes.t -> t -> t val add : AbstractValue.t -> Attributes.t -> t -> t
val allocate : AbstractValue.t * ValueHistory.t -> Location.t -> t -> t val allocate : Procname.t -> AbstractValue.t * ValueHistory.t -> Location.t -> t -> t
val fold : (AbstractValue.t -> Attributes.t -> 'a -> 'a) -> t -> 'a -> 'a val fold : (AbstractValue.t -> Attributes.t -> 'a -> 'a) -> t -> 'a -> 'a

@ -15,7 +15,7 @@ module ValueHistory = PulseValueHistory
type t = type t =
| AccessToInvalidAddress of | AccessToInvalidAddress of
{invalidation: Invalidation.t; invalidation_trace: Trace.t; access_trace: Trace.t} {invalidation: Invalidation.t; invalidation_trace: Trace.t; access_trace: Trace.t}
| MemoryLeak of {allocation_trace: Trace.t; location: Location.t} | MemoryLeak of {procname: Procname.t; allocation_trace: Trace.t; location: Location.t}
| StackVariableAddressEscape of {variable: Var.t; history: ValueHistory.t; location: Location.t} | StackVariableAddressEscape of {variable: Var.t; history: ValueHistory.t; location: Location.t}
let get_location = function let get_location = function
@ -67,7 +67,7 @@ let get_message = function
F.asprintf "%a%a" pp_access_trace access_trace F.asprintf "%a%a" pp_access_trace access_trace
(pp_invalidation_trace invalidation_line invalidation) (pp_invalidation_trace invalidation_line invalidation)
invalidation_trace invalidation_trace
| MemoryLeak {location; allocation_trace} -> | MemoryLeak {procname; location; allocation_trace} ->
let allocation_line = let allocation_line =
let {Location.line; _} = Trace.get_outer_location allocation_trace in let {Location.line; _} = Trace.get_outer_location allocation_trace in
line line
@ -75,7 +75,7 @@ let get_message = function
let pp_allocation_trace fmt (trace : Trace.t) = let pp_allocation_trace fmt (trace : Trace.t) =
match trace with match trace with
| Immediate _ -> | Immediate _ ->
F.fprintf fmt "by call to `malloc()`" F.fprintf fmt "by call to `%a`" Procname.pp procname
| ViaCall {f; _} -> | ViaCall {f; _} ->
F.fprintf fmt "by call to %a" CallEvent.describe f F.fprintf fmt "by call to %a" CallEvent.describe f
in in

@ -14,7 +14,7 @@ module ValueHistory = PulseValueHistory
type t = type t =
| AccessToInvalidAddress of | AccessToInvalidAddress of
{invalidation: Invalidation.t; invalidation_trace: Trace.t; access_trace: Trace.t} {invalidation: Invalidation.t; invalidation_trace: Trace.t; access_trace: Trace.t}
| MemoryLeak of {allocation_trace: Trace.t; location: Location.t} | MemoryLeak of {procname: Procname.t; allocation_trace: Trace.t; location: Location.t}
| StackVariableAddressEscape of {variable: Var.t; history: ValueHistory.t; location: Location.t} | StackVariableAddressEscape of {variable: Var.t; history: ValueHistory.t; location: Location.t}
val get_message : t -> string val get_message : t -> string

@ -89,8 +89,8 @@ module C = struct
let malloc access : model = let malloc access : model =
fun ~caller_summary:_ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun ~caller_summary:_ ~callee_procname location ~ret:(ret_id, _) astate ->
let astate = PulseOperations.allocate location access astate in let astate = PulseOperations.allocate callee_procname location access astate in
Ok [PulseOperations.write_id ret_id access astate] Ok [PulseOperations.write_id ret_id access astate]
end end
@ -245,6 +245,17 @@ module StdAtomicInteger = struct
[PulseOperations.write_id ret_id (old_int, event :: old_hist) astate] [PulseOperations.write_id ret_id (old_int, event :: old_hist) astate]
end end
module ObjectiveC = struct
let alloc _ : model =
fun ~caller_summary:_ ~callee_procname location ~ret:(ret_id, _) astate ->
let hist =
[ValueHistory.Call {f= Model (Procname.to_string callee_procname); location; in_call= []}]
in
let ret_addr = AbstractValue.mk_fresh () in
let astate = PulseOperations.allocate callee_procname location (ret_addr, []) astate in
Ok [PulseOperations.write_id ret_id (ret_addr, hist) astate]
end
module JavaObject = struct module JavaObject = struct
(* naively modeled as shallow copy. *) (* naively modeled as shallow copy. *)
let clone src_pointer_hist : model = let clone src_pointer_hist : model =
@ -513,7 +524,8 @@ module ProcNameDispatcher = struct
; ( +PatternMatch.implements_enumeration ; ( +PatternMatch.implements_enumeration
&:: "nextElement" <>$ capt_arg_payload &:: "nextElement" <>$ capt_arg_payload
$!--> fun x -> StdVector.at ~desc:"Enumeration.nextElement" x (AbstractValue.mk_fresh (), []) $!--> fun x -> StdVector.at ~desc:"Enumeration.nextElement" x (AbstractValue.mk_fresh (), [])
) ] )
; +PatternMatch.ObjectiveC.is_core_graphics_create_or_copy &++> ObjectiveC.alloc ]
end end
let dispatch tenv proc_name args = ProcNameDispatcher.dispatch tenv proc_name args let dispatch tenv proc_name args = ProcNameDispatcher.dispatch tenv proc_name args

@ -374,7 +374,9 @@ let havoc_field location addr_trace field trace_obj astate =
write_field location addr_trace field (AbstractValue.mk_fresh (), trace_obj) astate write_field location addr_trace field (AbstractValue.mk_fresh (), trace_obj) astate
let allocate location addr_trace astate = AddressAttributes.allocate addr_trace location astate let allocate procname location addr_trace astate =
AddressAttributes.allocate procname addr_trace location astate
let invalidate location cause addr_trace astate = let invalidate location cause addr_trace astate =
check_addr_access location addr_trace astate check_addr_access location addr_trace astate
@ -474,17 +476,17 @@ let check_memory_leak_unreachable unreachable_attrs location =
Attributes.fold attributes ~init:(None (* allocation trace *), false (* freed *)) Attributes.fold attributes ~init:(None (* allocation trace *), false (* freed *))
~f:(fun acc attr -> ~f:(fun acc attr ->
match (attr : Attribute.t) with match (attr : Attribute.t) with
| Allocated trace -> | Allocated (procname, trace) ->
(Some trace, snd acc) (Some (procname, trace), snd acc)
| Invalid (CFree, _) -> | Invalid (CFree, _) ->
(fst acc, true) (fst acc, true)
| _ -> | _ ->
acc ) acc )
in in
match allocated_not_freed_opt with match allocated_not_freed_opt with
| Some trace, false -> | Some (procname, trace), false ->
(* allocated but not freed *) (* allocated but not freed *)
Error (Diagnostic.MemoryLeak {location; allocation_trace= trace}) Error (Diagnostic.MemoryLeak {procname; location; allocation_trace= trace})
| _ -> | _ ->
result result
in in

@ -82,7 +82,7 @@ val invalidate :
Location.t -> Invalidation.t -> AbstractValue.t * ValueHistory.t -> t -> t access_result Location.t -> Invalidation.t -> AbstractValue.t * ValueHistory.t -> t -> t access_result
(** record that the address is invalid *) (** record that the address is invalid *)
val allocate : Location.t -> AbstractValue.t * ValueHistory.t -> t -> t val allocate : Procname.t -> Location.t -> AbstractValue.t * ValueHistory.t -> t -> t
val invalidate_deref : val invalidate_deref :
Location.t -> Invalidation.t -> AbstractValue.t * ValueHistory.t -> t -> t access_result Location.t -> Invalidation.t -> AbstractValue.t * ValueHistory.t -> t -> t access_result

@ -0,0 +1,49 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
@interface MemoryLeaks
@property(strong) UIView* backgroundCoveringView;
@property(strong) UIView* attachmentContainerView;
@end
@implementation MemoryLeaks
- (void)cg_path_create_with_rect_no_leak_good_FP {
UIView* attachmentContainerView = [UIView alloc];
CGPathRef shadowPath =
CGPathCreateWithRect(attachmentContainerView.bounds, NULL);
CGPathRelease(shadowPath);
}
- (void)cg_path_create_with_rect_leak_bad {
CGPathRef shadowPath =
CGPathCreateWithRect(self.backgroundCoveringView.bounds, NULL);
self.backgroundCoveringView.layer.shadowPath = shadowPath;
}
+ (void)cg_path_create_mutable_leak_bad:(CGRect)rect {
0.20f * CGRectGetHeight(rect);
CGPathCreateMutable();
}
+ (void)cg_path_create_mutable_no_leak_good_FP:(CGRect)rect {
CGFloat lineThickness = 0.20f * CGRectGetHeight(rect);
// One rectangle
CGMutablePathRef path1 = CGPathCreateMutable();
CFRelease(path1);
}
+ (void)cg_bitmap_context_create_image_no_leak_good_FP {
CGImageRef newImage = CGBitmapContextCreateImage(nil);
CGImageRelease(newImage);
}
@end

@ -1,2 +1,7 @@
codetoanalyze/objc/pulse/MemoryLeaks.m, MemoryLeaks::cg_bitmap_context_create_image_no_leak_good_FP, 2, PULSE_MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocation occurs here,memory becomes unreachable here]
codetoanalyze/objc/pulse/MemoryLeaks.m, MemoryLeaks::cg_path_create_mutable_leak_bad:, 2, PULSE_MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocation occurs here,memory becomes unreachable here]
codetoanalyze/objc/pulse/MemoryLeaks.m, MemoryLeaks::cg_path_create_mutable_no_leak_good_FP:, 4, PULSE_MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocation occurs here,memory becomes unreachable here]
codetoanalyze/objc/pulse/MemoryLeaks.m, MemoryLeaks::cg_path_create_with_rect_leak_bad, 3, PULSE_MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocation occurs here,memory becomes unreachable here]
codetoanalyze/objc/pulse/MemoryLeaks.m, MemoryLeaks::cg_path_create_with_rect_no_leak_good_FP, 4, PULSE_MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocation occurs here,memory becomes unreachable here]
codetoanalyze/objc/pulse/use_after_free.m, PulseTest::use_after_free_simple_in_objc_method_bad:, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of PulseTest::use_after_free_simple_in_objc_method_bad:,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of PulseTest::use_after_free_simple_in_objc_method_bad:,invalid access occurs here] codetoanalyze/objc/pulse/use_after_free.m, PulseTest::use_after_free_simple_in_objc_method_bad:, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of PulseTest::use_after_free_simple_in_objc_method_bad:,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of PulseTest::use_after_free_simple_in_objc_method_bad:,invalid access occurs here]
codetoanalyze/objc/pulse/use_after_free.m, use_after_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of use_after_free_simple_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of use_after_free_simple_bad,invalid access occurs here] codetoanalyze/objc/pulse/use_after_free.m, use_after_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of use_after_free_simple_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of use_after_free_simple_bad,invalid access occurs here]

Loading…
Cancel
Save