-
Notifications
You must be signed in to change notification settings - Fork 88
Expand file tree
/
Copy pathmemOutOfBounds.ml
More file actions
557 lines (518 loc) · 29.1 KB
/
memOutOfBounds.ml
File metadata and controls
557 lines (518 loc) · 29.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
(** An analysis for the detection of out-of-bounds memory accesses ([memOutOfBounds]).*)
open GoblintCil
open Analyses
open MessageCategory
open AnalysisStateUtil
module AS = AnalysisState
module VDQ = ValueDomainQueries
module ID = IntDomain.IntDomTuple
(*
Note:
* This functionality is implemented as an analysis solely for the sake of maintaining
separation of concerns, as well as for having the ablility to conveniently turn it on or off
* It doesn't track any internal state
*)
module Spec =
struct
include Analyses.IdentitySpec
module D = Lattice.Unit
include Analyses.ValueContexts(D)
let context man _ _ = ()
let name () = "memOutOfBounds"
(* HELPER FUNCTIONS *)
let intdom_of_int x =
ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x)
let size_of_type_in_bytes typ =
intdom_of_int (Cilfacade.bytesSizeOf typ)
let offs_lt_zero offs =
try ID.lt offs (intdom_of_int 0)
with IntDomain.ArithmeticOnIntegerBot _ -> None
let check_deref_offset_bounds ptr_size offs =
let ptr_size_lt_offs =
try
let one = intdom_of_int 1 in
let max_valid_offs = ID.sub ptr_size one in
ID.lt max_valid_offs offs
with IntDomain.ArithmeticOnIntegerBot _ -> None
in
offs_lt_zero offs, ptr_size_lt_offs
let check_ptr_offset_bounds ptr_size offs =
let ptr_size_lt_offs =
try ID.lt ptr_size offs
with IntDomain.ArithmeticOnIntegerBot _ -> None
in
offs_lt_zero offs, ptr_size_lt_offs
let rec exp_contains_a_ptr (exp:exp) =
match exp with
| Const _
| SizeOf _
| SizeOfStr _
| AlignOf _
| AddrOfLabel _ -> false
| Real e
| Imag e
| SizeOfE e
| AlignOfE e
| UnOp (_, e, _)
| CastE (_, _, e) -> exp_contains_a_ptr e
| BinOp (_, e1, e2, _) ->
exp_contains_a_ptr e1 || exp_contains_a_ptr e2
| Question (e1, e2, e3, _) ->
exp_contains_a_ptr e1 || exp_contains_a_ptr e2 || exp_contains_a_ptr e3
| Lval lval
| AddrOf lval
| StartOf lval -> lval_contains_a_ptr lval
and lval_contains_a_ptr (lval:lval) =
let (host, offset) = lval in
let host_contains_a_ptr = function
| Var v -> isPointerType v.vtype
| Mem e -> exp_contains_a_ptr e
in
let rec offset_contains_a_ptr = function
| NoOffset -> false
| Index (e, o) -> exp_contains_a_ptr e || offset_contains_a_ptr o
| Field (f, o) -> isPointerType f.ftype || offset_contains_a_ptr o
in
host_contains_a_ptr host || offset_contains_a_ptr offset
let points_to_alloc_only man ptr =
match man.ask (Queries.MayPointTo ptr) with
| a when not (Queries.AD.is_top a)->
Queries.AD.for_all (function
| Addr (v, o) -> man.ask (Queries.IsAllocVar v)
| _ -> false
) a
| _ -> false
let get_size_of_ptr_target man ptr = (* TODO: deduplicate with base *)
if points_to_alloc_only man ptr then
(* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *)
man.ask (Queries.BlobSize {exp = ptr; base_address = true})
else
match man.ask (Queries.MayPointTo ptr) with
| a when not (Queries.AD.is_top a) ->
let pts_list = Queries.AD.elements a in
let pts_elems_to_sizes (addr: Queries.AD.elt) =
begin match addr with
| Addr (v, _) ->
if hasAttribute "goblint_cil_nested" v.vattr then (
set_mem_safety_flag InvalidDeref;
M.warn "Var %a is potentially accessed out-of-scope. Invalid memory access may occur" CilType.Varinfo.pretty v;
Checks.warn Checks.Category.InvalidMemoryAccess "Var %a is potentially accessed out-of-scope. Invalid memory access may occur" CilType.Varinfo.pretty v
);
begin match Cil.unrollType v.vtype with
| TArray (item_typ, _, _) ->
let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in
begin match man.ask (Queries.EvalLength ptr) with
| `Lifted arr_len ->
let arr_len_casted = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) arr_len in (* TODO: proper castkind *)
begin
try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted)
with IntDomain.ArithmeticOnIntegerBot _ -> `Bot
end
| `Bot -> `Bot
| `Top -> `Top
end
| _ ->
let type_size_in_bytes = size_of_type_in_bytes v.vtype in
`Lifted type_size_in_bytes
end
| _ -> `Top
end
in
(* Map each points-to-set element to its size *)
let pts_sizes = List.map pts_elems_to_sizes pts_list in
(* Take the smallest of all sizes that ptr's contents may have *)
begin match pts_sizes with
| [] -> `Bot
| [x] -> x
| x::xs -> List.fold_left VDQ.ID.join x xs
end
| _ ->
(set_mem_safety_flag InvalidDeref;
M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr;
Checks.warn Checks.Category.InvalidMemoryAccess "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr;
`Top)
let get_ptr_deref_type ptr_typ =
match Cil.unrollType ptr_typ with
| TPtr (t, _) -> Some t
| _ -> None
let eval_ptr_offset_in_binop man exp ptr_contents_typ =
let eval_offset = man.ask (Queries.EvalInt exp) in
let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in
match eval_offset with
| `Lifted eo ->
let casted_eo = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) eo in (* TODO: proper castkind *)
begin
try `Lifted (ID.mul casted_eo ptr_contents_typ_size_in_bytes)
with IntDomain.ArithmeticOnIntegerBot _ -> `Bot
end
| `Top -> `Top
| `Bot -> `Bot
let rec offs_to_idx typ offs =
match offs with
| `NoOffset -> intdom_of_int 0
| `Field (field, o) ->
let bytes_offset = Cilfacade.fieldBytesOffsetOnly field in
let bytes_offset = intdom_of_int bytes_offset in
let remaining_offset = offs_to_idx field.ftype o in
begin
try ID.add bytes_offset remaining_offset
with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind ()
end
| `Index (x, o) ->
begin try
let typ_size_in_bytes = size_of_type_in_bytes typ in
let bytes_offset = ID.mul typ_size_in_bytes x in
let remaining_offset = offs_to_idx typ o in
ID.add bytes_offset remaining_offset
with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind ()
end
let cil_offs_to_idx man typ offs =
(* TODO: Some duplication with convert_offset in base.ml, unclear how to immediately get more reuse *)
let rec convert_offset (ofs: offset) =
match ofs with
| NoOffset -> `NoOffset
| Field (fld, ofs) -> `Field (fld, convert_offset ofs)
| Index (exp, ofs) when Offset.Index.Exp.is_any exp -> (* special offset added by convertToQueryLval *)
`Index (ID.top (), convert_offset ofs)
| Index (exp, ofs) ->
let i = match man.ask (Queries.EvalInt exp) with
| `Lifted x -> ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) x (* TODO: proper castkind *)
| _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind ()
in
`Index (i, convert_offset ofs)
in
PreValueDomain.Offs.to_index (convert_offset offs)
let check_unknown_addr_deref man ptr =
let may_contain_unknown_addr =
match man.ask (Queries.EvalValue ptr) with
| a when not (Queries.VD.is_top a) ->
begin match a with
| Address a -> ValueDomain.AD.may_be_unknown a
| _ -> false
end
(* Intuition: if ptr evaluates to top, it could potentially evaluate to the unknown address *)
| _ -> true
in
if may_contain_unknown_addr then begin
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior (Undefined Other)) "Pointer %a contains an unknown address. Invalid dereference may occur" d_exp ptr;
Checks.warn Checks.Category.InvalidMemoryAccess "Pointer %a contains an unknown address. Invalid dereference may occur" d_exp ptr
end else
Checks.safe Checks.Category.InvalidMemoryAccess
let ptr_only_has_str_addr man ptr =
match man.ask (Queries.EvalValue ptr) with
| a when not (Queries.VD.is_top a) ->
begin match a with
| Address a -> ValueDomain.AD.for_all (fun addr -> match addr with | StrPtr _ -> true | _ -> false) a
| _ -> false
end
(* Intuition: if ptr evaluates to top, it could all sorts of things and not only string addresses *)
| _ -> false
let rec get_addr_offs man ptr =
match man.ask (Queries.MayPointTo ptr) with
| a when not (VDQ.AD.is_top a) ->
let ptr_deref_type = get_ptr_deref_type @@ typeOf ptr in
begin match ptr_deref_type with
| Some t ->
begin match VDQ.AD.is_empty a with
| true ->
M.warn "Pointer %a has an empty points-to-set" d_exp ptr;
Checks.warn Checks.Category.InvalidMemoryAccess "Pointer %a has an empty points-to-set" d_exp ptr;
ID.top_of @@ Cilfacade.ptrdiff_ikind ()
| false ->
if VDQ.AD.exists (function
| Addr (_, o) -> ID.is_bot @@ offs_to_idx t o
| _ -> false
) a then (
set_mem_safety_flag InvalidDeref;
M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr;
Checks.warn Checks.Category.InvalidMemoryAccess "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr
) else if VDQ.AD.exists (function
| Addr (_, o) -> ID.is_top_of (Cilfacade.ptrdiff_ikind ()) (offs_to_idx t o)
| _ -> false
) a then (
set_mem_safety_flag InvalidDeref;
M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr;
Checks.warn Checks.Category.InvalidMemoryAccess "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr
) else (
Checks.safe Checks.Category.InvalidMemoryAccess
);
(* Get the address offsets of all points-to set elements *)
let addr_offsets =
VDQ.AD.filter (function Addr (v, o) -> true | _ -> false) a
|> VDQ.AD.to_mval
|> List.map (fun (_, o) -> offs_to_idx t o)
in
begin match addr_offsets with
| [] -> ID.bot_of @@ Cilfacade.ptrdiff_ikind ()
| [x] -> x
| x::xs -> List.fold_left ID.join x xs
end
end
| None ->
M.error "Expression %a doesn't have pointer type" d_exp ptr;
ID.top_of @@ Cilfacade.ptrdiff_ikind ()
end
| _ ->
set_mem_safety_flag InvalidDeref;
M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr;
Checks.warn Checks.Category.InvalidMemoryAccess "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr;
ID.top_of @@ Cilfacade.ptrdiff_ikind ()
and check_lval_for_oob_access man ?(is_implicitly_derefed = false) lval =
(* If the lval does not contain a pointer or if it does contain a pointer, but only points to string addresses, then no need to WARN *)
if (not @@ lval_contains_a_ptr lval) || ptr_only_has_str_addr man (Lval lval) then ()
else
(* If the lval doesn't indicate an explicit dereference, we still need to check for an implicit dereference *)
(* An implicit dereference is, e.g., printf("%p", ptr), where ptr is a pointer *)
match lval, is_implicitly_derefed with
| (Var _, _), false -> ()
| (Var v, _), true -> check_no_binop_deref man (Lval lval)
| (Mem e, o), _ ->
let ptr_deref_type = get_ptr_deref_type @@ typeOf e in
let offs_intdom = begin match ptr_deref_type with
| Some t -> cil_offs_to_idx man t o
| None -> ID.bot_of @@ Cilfacade.ptrdiff_ikind ()
end in
let e_size = get_size_of_ptr_target man e in
begin match e_size with
| `Top ->
(set_mem_safety_flag InvalidDeref;
M.warn "Size of lval dereference expression %a is top. Out-of-bounds memory access may occur" d_exp e);
Checks.warn Checks.Category.InvalidMemoryAccess "Size of lval dereference expression %a is top. Out-of-bounds memory access may occur" d_exp e
| `Bot ->
(set_mem_safety_flag InvalidDeref;
M.warn "Size of lval dereference expression %a is bot. Out-of-bounds memory access may occur" d_exp e);
Checks.warn Checks.Category.InvalidMemoryAccess "Size of lval dereference expression %a is bot. Out-of-bounds memory access may occur" d_exp e
| `Lifted es ->
let casted_es = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) es in (* TODO: proper castkind *)
let casted_offs = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) offs_intdom in (* TODO: proper castkind *)
let behavior = Undefined MemoryOutOfBoundsAccess in
let cwe_number = 823 in
begin match check_deref_offset_bounds casted_es casted_offs with
| Some true, _
| _, Some true ->
(set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of lval dereference expression is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" ID.pretty casted_es ID.pretty casted_offs);
Checks.warn Checks.Category.InvalidMemoryAccess "Size of lval dereference expression is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" ID.pretty casted_es ID.pretty casted_offs
| Some false, Some false ->
Checks.safe Checks.Category.InvalidMemoryAccess
| _ ->
(set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of lval dereference expression (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_es ID.pretty casted_offs);
Checks.warn Checks.Category.InvalidMemoryAccess "Could not compare size of lval dereference expression (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_es ID.pretty casted_offs
end
end;
begin match e with
| Lval (Var v, _) as lval_exp -> check_no_binop_deref man lval_exp
| BinOp (binop, e1, e2, t) when binop = PlusPI || binop = MinusPI || binop = IndexPI ->
check_binop_exp man binop e1 e2 t;
check_exp_for_oob_access man ~is_implicitly_derefed e1;
check_exp_for_oob_access man ~is_implicitly_derefed e2
| _ -> check_exp_for_oob_access man ~is_implicitly_derefed e
end
and check_no_binop_deref man lval_exp =
check_unknown_addr_deref man lval_exp;
let behavior = Undefined MemoryOutOfBoundsAccess in
let cwe_number = 823 in
let ptr_size = get_size_of_ptr_target man lval_exp in
let addr_offs = get_addr_offs man lval_exp in
let ptr_type = typeOf lval_exp in
let ptr_contents_type = get_ptr_deref_type ptr_type in
match ptr_contents_type with
| Some t ->
begin match ptr_size, addr_offs with
| `Top, _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is top. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of pointer %a is top. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp
| `Bot, _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is bot. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of pointer %a is bot. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp
| `Lifted ps, ao ->
let casted_ps = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) ps in (* TODO: proper castkind *)
let casted_ao = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) ao in (* TODO: proper castkind *)
begin match check_ptr_offset_bounds casted_ps casted_ao with
| Some true, _
| _, Some true ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" ID.pretty casted_ps ID.pretty casted_ao;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" ID.pretty casted_ps ID.pretty casted_ao
| Some false, Some false ->
Checks.safe Checks.Category.InvalidMemoryAccess
| _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of pointer (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_ps ID.pretty casted_ao;
Checks.warn Checks.Category.InvalidMemoryAccess "Could not compare size of pointer (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_ps ID.pretty casted_ao
end
end
| _ -> M.error "Expression %a is not a pointer" d_exp lval_exp
and check_exp_for_oob_access man ?(is_implicitly_derefed = false) exp =
match exp with
| Const _
| SizeOf _
| SizeOfStr _
| AlignOf _
| AddrOfLabel _ -> ()
| Real e
| Imag e
| SizeOfE e
| AlignOfE e
| UnOp (_, e, _)
| CastE (_, _, e) -> check_exp_for_oob_access man ~is_implicitly_derefed e
| BinOp (bop, e1, e2, t) ->
check_exp_for_oob_access man ~is_implicitly_derefed e1;
check_exp_for_oob_access man ~is_implicitly_derefed e2
| Question (e1, e2, e3, _) ->
check_exp_for_oob_access man ~is_implicitly_derefed e1;
check_exp_for_oob_access man ~is_implicitly_derefed e2;
check_exp_for_oob_access man ~is_implicitly_derefed e3
| Lval lval
| StartOf lval
| AddrOf lval -> check_lval_for_oob_access man ~is_implicitly_derefed lval
and check_binop_exp man binop e1 e2 t =
check_unknown_addr_deref man e1;
let binopexp = BinOp (binop, e1, e2, t) in
let behavior = Undefined MemoryOutOfBoundsAccess in
let cwe_number = 823 in
match binop with
| PlusPI
| IndexPI
| MinusPI ->
let ptr_size = get_size_of_ptr_target man e1 in
let addr_offs = get_addr_offs man e1 in
let ptr_type = typeOf e1 in
let ptr_contents_type = get_ptr_deref_type ptr_type in
begin match ptr_contents_type with
| Some t ->
let offset_size = eval_ptr_offset_in_binop man e2 t in
(* Make sure to add the address offset to the binop offset *)
let offset_size_with_addr_size = match offset_size with
| `Lifted os ->
let casted_os = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) os in (* TODO: proper castkind *)
let casted_ao = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) addr_offs in (* TODO: proper castkind *)
begin
try `Lifted (ID.add casted_os casted_ao)
with IntDomain.ArithmeticOnIntegerBot _ -> `Bot
end
| `Top -> `Top
| `Bot -> `Bot
in
begin match ptr_size, offset_size_with_addr_size with
| `Top, _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is top. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of pointer %a in expression %a is top. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp
| _, `Top ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is top. Memory out-of-bounds access might occur" d_exp binopexp;
Checks.warn Checks.Category.InvalidMemoryAccess "Operand value for pointer arithmetic in expression %a is top. Memory out-of-bounds access might occur" d_exp binopexp
| `Bot, _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is bottom. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of pointer %a in expression %a is bottom. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp
| _, `Bot ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is bottom. Memory out-of-bounds access might occur" d_exp binopexp;
Checks.warn Checks.Category.InvalidMemoryAccess "Operand value for pointer arithmetic in expression %a is bottom. Memory out-of-bounds access might occur" d_exp binopexp
| `Lifted ps, `Lifted o ->
let casted_ps = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) ps in (* TODO: proper castkind *)
let casted_o = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) o in (* TODO: proper castkind *)
begin match check_ptr_offset_bounds casted_ps casted_o with
| Some true, _
| _, Some true ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp ID.pretty casted_ps ID.pretty casted_o;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp ID.pretty casted_ps ID.pretty casted_o
| Some false, Some false ->
Checks.safe Checks.Category.InvalidMemoryAccess
| _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare pointer size (%a) with offset (%a). Memory out-of-bounds access may occur" ID.pretty casted_ps ID.pretty casted_o;
Checks.warn Checks.Category.InvalidMemoryAccess "Could not compare pointer size (%a) with offset (%a). Memory out-of-bounds access may occur" ID.pretty casted_ps ID.pretty casted_o
end
end
| _ -> M.error "Binary expression %a doesn't have a pointer" d_exp binopexp
end
| _ -> ()
(* For memset() and memcpy() *)
let check_count man fun_name ptr n =
let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in
let cwe_number = 823 in
let ptr_size = get_size_of_ptr_target man ptr in
let eval_n = man.ask (Queries.EvalInt n) in
let addr_offs = get_addr_offs man ptr in
match ptr_size, eval_n with
| `Top, _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp ptr fun_name;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp ptr fun_name
| _, `Top ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name;
Checks.warn Checks.Category.InvalidMemoryAccess "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name
| `Bot, _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp ptr fun_name;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp ptr fun_name
| _, `Bot ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name;
Checks.warn Checks.Category.InvalidMemoryAccess "Count parameter, passed to function %s is bottom" fun_name
| `Lifted ds, `Lifted en ->
let casted_ds = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) ds in (* TODO: proper castkind *)
let casted_en = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) en in (* TODO: proper castkind *)
let casted_ao = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) addr_offs in (* TODO: proper castkind *)
let dest_size_lt_count = ID.lt casted_ds (ID.add casted_en casted_ao) in
begin match dest_size_lt_count with
| Some true ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of %a in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" d_exp ptr fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of %a in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" d_exp ptr fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en
| Some false ->
Checks.safe Checks.Category.InvalidMemoryAccess
| None ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of dest (%a) with address offset (%a) count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en fun_name;
Checks.warn Checks.Category.InvalidMemoryAccess "Could not compare size of dest (%a) with address offset (%a) count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en fun_name
end
(* TRANSFER FUNCTIONS *)
let assign man (lval:lval) (rval:exp) : D.t =
check_lval_for_oob_access man lval;
check_exp_for_oob_access man rval;
man.local
let branch man (exp:exp) (tv:bool) : D.t =
check_exp_for_oob_access man exp;
man.local
let return man (exp:exp option) (f:fundec) : D.t =
Option.iter (fun x -> check_exp_for_oob_access man x) exp;
man.local
let special man (lval:lval option) (f:varinfo) (arglist:exp list) : D.t =
let desc = LibraryFunctions.find f in
let is_arg_implicitly_derefed arg =
let read_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = false } arglist in
let read_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = true } arglist in
let write_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in
let write_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in
List.mem arg read_shallow_args || List.mem arg read_deep_args || List.mem arg write_shallow_args || List.mem arg write_deep_args
in
Option.iter (fun x -> check_lval_for_oob_access man x) lval;
List.iter (fun arg -> check_exp_for_oob_access man ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) arg) arglist;
(* Check calls to memset and memcpy for out-of-bounds-accesses *)
match desc.special arglist with
| Memset { dest; ch; count; } -> check_count man f.vname dest count;
| Memcpy { dest; src; n = count; } ->
(check_count man f.vname src count;
check_count man f.vname dest count;)
| _ -> man.local
let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list =
List.iter (fun arg -> check_exp_for_oob_access man arg) args;
[man.local, man.local]
let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t =
Option.iter (fun x -> check_lval_for_oob_access man x) lval;
man.local
let startstate v = ()
let exitstate v = ()
end
let _ =
MCP.register_analysis (module Spec : MCPSpec)