3131 let get_joined_threads man =
3232 man.ask Queries. MustJoinedThreads
3333
34- let warn_for_multi_threaded_access man ?(is_double_free = false ) (heap_var :varinfo ) behavior cwe_number =
34+ let warn_for_multi_threaded_access man ?(is_free = false ) (heap_var :varinfo ) behavior cwe_number =
3535 let freeing_threads = man.global heap_var in
3636 (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *)
3737 if man.ask (Queries. MustBeSingleThreaded { since_start = true }) || G. is_empty freeing_threads then ()
@@ -58,102 +58,33 @@ struct
5858 | `Top -> true
5959 | `Bot -> false
6060 in
61- let bug_name = if is_double_free then " Double Free" else " Use After Free" in
61+ let bug_name = if is_free then " Double Free" else " Use After Free" in
6262 match get_current_threadid man with
6363 | `Lifted current ->
6464 let possibly_started = G. exists (other_possibly_started current) freeing_threads in
6565 if possibly_started then begin
66- if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref ;
66+ if is_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref ;
6767 M. warn ~category: (Behavior behavior) ~tags: [CWE cwe_number] " There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. %s might occur" CilType.Varinfo. pretty heap_var bug_name
6868 end
6969 else begin
7070 let current_is_unique = ThreadId.Thread. is_unique current in
7171 let any_equal_current threads = G. exists (equal_current current) threads in
7272 if not current_is_unique && any_equal_current freeing_threads then begin
73- if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref ;
73+ if is_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref ;
7474 M. warn ~category: (Behavior behavior) ~tags: [CWE cwe_number] " Current thread is not unique and a %s might occur for heap variable %a" bug_name CilType.Varinfo. pretty heap_var
7575 end
7676 else if HeapVars. mem heap_var (snd man.local) then begin
77- if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref ;
77+ if is_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref ;
7878 M. warn ~category: (Behavior behavior) ~tags: [CWE cwe_number] " %s might occur in current unique thread %a for heap variable %a" bug_name ThreadIdDomain.Thread. pretty current CilType.Varinfo. pretty heap_var
7979 end
8080 end
8181 | `Top ->
82- if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref ;
82+ if is_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref ;
8383 M. warn ~category: (Behavior behavior) ~tags: [CWE cwe_number] " CurrentThreadId is top. %s might occur for heap variable %a" bug_name CilType.Varinfo. pretty heap_var
8484 | `Bot ->
8585 M. warn ~category: MessageCategory. Analyzer " CurrentThreadId is bottom"
8686 end
8787
88- let rec warn_lval_might_contain_freed ?(is_implicitly_derefed = false ) ?(is_double_free = false ) (transfer_fn_name :string ) man (lval :lval ) =
89- match is_implicitly_derefed, is_double_free, lval with
90- (* If we're not checking for a double-free and there's no deref happening, then there's no need to check for an invalid deref or an invalid free *)
91- | false , false , (Var _ , NoOffset) -> ()
92- | _ ->
93- let state = man.local in
94- let undefined_behavior = if is_double_free then Undefined DoubleFree else Undefined UseAfterFree in
95- let cwe_number = if is_double_free then 415 else 416 in
96- let rec offset_might_contain_freed offset =
97- match offset with
98- | NoOffset -> ()
99- | Field (f , o ) -> offset_might_contain_freed o
100- | Index (e , o ) -> warn_exp_might_contain_freed transfer_fn_name man e; offset_might_contain_freed o
101- in
102- let (lval_host, o) = lval in offset_might_contain_freed o; (* Check the lval's offset *)
103- let lval_to_query =
104- match lval_host with
105- | Var _ -> Lval lval
106- | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *)
107- in
108- begin match man.ask (Queries. MayPointTo lval_to_query) with
109- | ad when not (Queries.AD. is_top ad) ->
110- let warn_for_heap_var v =
111- if HeapVars. mem v (snd state) then begin
112- if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref ;
113- M. warn ~category: (Behavior undefined_behavior) ~tags: [CWE cwe_number] " lval (%s) in \" %s\" points to a maybe freed memory region" v.vname transfer_fn_name
114- end
115- in
116- let pointed_to_heap_vars =
117- Queries.AD. fold (fun addr vars ->
118- match addr with
119- | Queries.AD.Addr. Addr (v ,_ ) when man.ask (Queries. IsAllocVar v) -> v :: vars
120- | _ -> vars
121- ) ad []
122- in
123- (* Warn for all heap vars that the lval possibly points to *)
124- List. iter warn_for_heap_var pointed_to_heap_vars;
125- (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *)
126- List. iter (fun heap_var -> warn_for_multi_threaded_access man ~is_double_free heap_var undefined_behavior cwe_number) pointed_to_heap_vars
127- | _ -> ()
128- end
129-
130- and warn_exp_might_contain_freed ?(is_implicitly_derefed = false ) ?(is_double_free = false ) (transfer_fn_name :string ) man (exp :exp ) =
131- match exp with
132- (* Base recursion cases *)
133- | Const _
134- | SizeOf _
135- | SizeOfStr _
136- | AlignOf _
137- | AddrOfLabel _ -> ()
138- (* Non-base cases *)
139- | Real e
140- | Imag e
141- | SizeOfE e
142- | AlignOfE e
143- | UnOp (_, e, _)
144- | CastE (_ , e ) -> warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e
145- | BinOp (_ , e1 , e2 , _ ) ->
146- warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e1;
147- warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e2
148- | Question (e1 , e2 , e3 , _ ) ->
149- warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e1;
150- warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e2;
151- warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e3
152- (* Lval cases (need [warn_lval_might_contain_freed] for them) *)
153- | Lval lval
154- | StartOf lval
155- | AddrOf lval -> warn_lval_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man lval
156-
15788 let side_effect_mem_free man freed_heap_vars threadid joined_threads =
15889 let side_effect_globals_to_heap_var heap_var =
15990 let current_globals = man.global heap_var in
@@ -165,22 +96,8 @@ struct
16596
16697 (* TRANSFER FUNCTIONS *)
16798
168- let assign man (lval :lval ) (rval :exp ) : D.t =
169- warn_lval_might_contain_freed " assign" man lval;
170- warn_exp_might_contain_freed " assign" man rval;
171- man.local
172-
173- let branch man (exp :exp ) (tv :bool ) : D.t =
174- warn_exp_might_contain_freed " branch" man exp;
175- man.local
176-
177- let return man (exp :exp option ) (f :fundec ) : D.t =
178- Option. iter (fun x -> warn_exp_might_contain_freed " return" man x) exp;
179- man.local
180-
18199 let enter man (lval :lval option ) (f :fundec ) (args :exp list ) : (D.t * D.t) list =
182100 let caller_state = man.local in
183- List. iter (fun arg -> warn_exp_might_contain_freed " enter" man arg) args;
184101 (* TODO: The 2nd component of the callee state needs to contain only the heap vars from the caller state which are reachable from: *)
185102 (* * Global program variables *)
186103 (* * The callee arguments *)
@@ -195,24 +112,12 @@ struct
195112 let callee_combined_state = HeapVars. join callee_stack_state callee_heap_state in
196113 (caller_stack_state, HeapVars. join caller_heap_state callee_combined_state)
197114
198- let combine_assign man (lval :lval option ) fexp (f :fundec ) (args :exp list ) fc (callee_local :D.t ) (f_ask : Queries.ask ): D.t =
199- Option. iter (fun x -> warn_lval_might_contain_freed " enter" man x) lval;
200- man.local
201-
202115 let special man (lval :lval option ) (f :varinfo ) (arglist :exp list ) : D.t =
203116 let state = man.local in
204117 let desc = LibraryFunctions. find f in
205- let is_arg_implicitly_derefed arg =
206- let read_shallow_args = LibraryDesc.Accesses. find desc.accs { kind = Read ; deep = false } arglist in
207- let read_deep_args = LibraryDesc.Accesses. find desc.accs { kind = Read ; deep = true } arglist in
208- let write_shallow_args = LibraryDesc.Accesses. find desc.accs { kind = Write ; deep = false } arglist in
209- let write_deep_args = LibraryDesc.Accesses. find desc.accs { kind = Write ; deep = true } arglist in
210- List. mem arg read_shallow_args || List. mem arg read_deep_args || List. mem arg write_shallow_args || List. mem arg write_deep_args
211- in
212- Option. iter (fun x -> warn_lval_might_contain_freed (" special: " ^ f.vname) man x) lval;
213- List. iter (fun arg -> warn_exp_might_contain_freed ~is_implicitly_derefed: (is_arg_implicitly_derefed arg) ~is_double_free: (match desc.special arglist with Free _ -> true | _ -> false ) (" special: " ^ f.vname) man arg) arglist;
214118 match desc.special arglist with
215119 | Free ptr ->
120+ (* TODO: do this using Free Access events? *)
216121 begin match man.ask (Queries. MayPointTo ptr) with
217122 | ad when not (Queries.AD. is_top ad) ->
218123 let pointed_to_heap_vars =
@@ -239,7 +144,37 @@ struct
239144 let startstate v = D. bot ()
240145 let exitstate v = D. top ()
241146
147+ let event man e oman =
148+ match e with
149+ | Events. Access {exp; ad; kind; reach} ->
150+ (* must use original (pre-assign, etc) man queries *)
151+ let is_free = kind = Free in
152+ let freed_heap_vars = snd oman.local in
153+ let undefined_behavior = if is_free then Undefined DoubleFree else Undefined UseAfterFree in
154+ let cwe_number = if is_free then 415 else 416 in
155+ let warn_for_heap_var v =
156+ if HeapVars. mem v freed_heap_vars then begin
157+ set_mem_safety_flag InvalidDeref ;
158+ M. warn ~category: (Behavior undefined_behavior) ~tags: [CWE cwe_number] " lval (%s) points to a maybe freed memory region" v.vname
159+ end
160+ in
161+ if not (Queries.AD. is_top ad) then begin
162+ let pointed_to_heap_vars =
163+ Queries.AD. fold (fun addr vars ->
164+ match addr with
165+ | Queries.AD.Addr. Addr (v ,_ ) when oman.ask (Queries. IsAllocVar v) -> v :: vars
166+ | _ -> vars
167+ ) ad []
168+ in
169+ (* Warn for all heap vars that the lval possibly points to *)
170+ List. iter warn_for_heap_var pointed_to_heap_vars;
171+ (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *)
172+ List. iter (fun heap_var -> warn_for_multi_threaded_access oman ~is_free heap_var undefined_behavior cwe_number) pointed_to_heap_vars
173+ end ;
174+ man.local
175+ | _ ->
176+ man.local
242177end
243178
244179let _ =
245- MCP. register_analysis (module Spec : MCPSpec )
180+ MCP. register_analysis ~dep: [ " access " ] (module Spec : MCPSpec )
0 commit comments