Skip to content

Commit d663309

Browse files
author
Ronald Judin
committed
Added registering variables to analysis and new tests
1 parent a94cf1c commit d663309

File tree

7 files changed

+303
-9
lines changed

7 files changed

+303
-9
lines changed

src/analyses/base.ml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2759,6 +2759,34 @@ struct
27592759
set_many ~man st ((eval_lv ~man st lv, (Cilfacade.typeOfLval lv), VD.Address addr) :: blob_set)
27602760
| _ -> st
27612761
end
2762+
(* TODO: Rethink OCamlParam's placement *)
2763+
| OCamlParam {param1; param2; param3; param4; param5}, _ ->
2764+
begin match param1 with
2765+
| Some p1 ->
2766+
let _ = eval_rv ~man st p1 in
2767+
begin match param2 with
2768+
| Some p2 ->
2769+
let _ = eval_rv ~man st p2 in
2770+
begin match param3 with
2771+
| Some p3 ->
2772+
let _ = eval_rv ~man st p3 in
2773+
begin match param4 with
2774+
| Some p4 ->
2775+
let _ = eval_rv ~man st p4 in
2776+
begin match param5 with
2777+
| Some p5 ->
2778+
let _ = eval_rv ~man st p5 in
2779+
st
2780+
| _ -> st
2781+
end
2782+
| _ -> st
2783+
end
2784+
| _ -> st
2785+
end
2786+
| _ -> st
2787+
end
2788+
| _ -> st
2789+
end
27622790
| Calloc { count = n; size }, _ ->
27632791
begin match lv with
27642792
| Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *)

src/analyses/ocaml.ml

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,57 @@ struct
170170
(* caller_state *)
171171
let desc = LibraryFunctions.find f in
172172
match desc.special arglist with
173-
(* TODO: Add a source function that registers variables and add the non-buggy Bagnall code to the test. *)
173+
| OCamlParam params ->
174+
(* Variables are registered with a Param macro. *)
175+
(* TODO: Which pattern below do the params in fact match? Does it vary? *)
176+
(* The function extract_varinfo is from AI - the code can be shortened when we know what the params are. *)
177+
let rec extract_varinfo (e: Cil.exp): varinfo option = match e with
178+
(* Direct variable lvalue *)
179+
| Lval (Var v, _) -> Some v
180+
(* Dereferenced pointer - try to extract from inner expression *)
181+
| Lval (Mem inner, _) -> extract_varinfo inner
182+
(* Address of variable *)
183+
| AddrOf (Var v, _) -> Some v (* TODO: Maybe this is the only thing a param can be. *)
184+
(* Address of dereferenced - try to extract from inner *)
185+
| AddrOf (Mem inner, _) -> extract_varinfo inner
186+
(* Array start of variable *)
187+
| StartOf (Var v, _) -> Some v
188+
(* Array start of dereferenced *)
189+
| StartOf (Mem inner, _) -> extract_varinfo inner
190+
(* Type cast - extract from inner expression *)
191+
| CastE (_, inner) -> extract_varinfo inner
192+
(* Unary operations - extract from operand *)
193+
| UnOp (_, inner, _) -> extract_varinfo inner
194+
(* Real/imaginary parts *)
195+
| Real inner | Imag inner -> extract_varinfo inner
196+
(* Binary operations - try both operands *)
197+
| BinOp (_, e1, e2, _) ->
198+
(match extract_varinfo e1 with
199+
| Some v -> Some v
200+
| None -> extract_varinfo e2)
201+
(* Size and alignment - may contain var info in the type *)
202+
| SizeOfE inner | AlignOfE inner -> extract_varinfo inner
203+
(* Constants, SizeOf, AlignOf, SizeOfStr, AddrOfLabel - no varinfo *)
204+
| Const _ | SizeOf _ | AlignOf _ | SizeOfStr _ | AddrOfLabel _ -> None
205+
(* Question conditional *)
206+
| Question (cond, e1, e2, _) ->
207+
(match extract_varinfo cond with
208+
| Some v -> Some v
209+
| None ->
210+
(match extract_varinfo e1 with
211+
| Some v -> Some v
212+
| None -> extract_varinfo e2))
213+
in
214+
List.fold_left (fun state param ->
215+
match param with
216+
| Some e ->
217+
(match extract_varinfo e with
218+
| Some v -> D.add_r v state
219+
| None -> state)
220+
| None -> state) caller_state [params.param1; params.param2; params.param3; params.param4; params.param5]
174221
| OCamlAlloc size_exp ->
175222
(* Garbage collection may trigger here and overwrite unregistered variables *)
176-
let _ = M.info "Garbage collection triggers" in (match lval with
223+
let _ = M.debug "Garbage collection triggers" in (match lval with
177224
| Some (Var v, _) -> D.add_a v (D.after_gc caller_state)
178225
| _ ->
179226
(* if not (List.for_all (exp_accounted_for caller_state) arglist) then let _ = M.warn "GC might delete value" in D.empty () else *) D.empty ()
@@ -185,7 +232,7 @@ struct
185232
| _ -> caller_state
186233
else
187234
if is_sink f then (* Warns if unaccounted variables reach the function. Empties the state of unregistered variables. *)
188-
let _ = M.info "Garbage collection triggers" in match lval with
235+
let _ = M.debug "Garbage collection triggers" in match lval with
189236
| Some (Var v, _) -> D.add_a v (D.after_gc caller_state)
190237
| _ ->
191238
(* if not (List.for_all (exp_accounted_for caller_state) arglist) then let _ = M.warn "GC might delete value" in D.empty () else *)

src/util/library/libraryDesc.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ type special =
4848
| Alloca of Cil.exp
4949
| Malloc of Cil.exp
5050
| OCamlAlloc of Cil.exp
51+
(* TODO: Rethink OCamlParam's placement and make the params a list. *)
52+
| OCamlParam of { param1: Cil.exp option; param2: Cil.exp option; param3: Cil.exp option; param4: Cil.exp option; param5: Cil.exp option; }
5153
| Calloc of { count: Cil.exp; size: Cil.exp; }
5254
| Realloc of { ptr: Cil.exp; size: Cil.exp; }
5355
| Free of Cil.exp

src/util/library/libraryFunctions.ml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1243,9 +1243,15 @@ let legacy_libs_misc_list: (string * LibraryDesc.t) list = LibraryDsl.[
12431243
[@@coverage off]
12441244

12451245
let ocaml_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
1246-
("caml_copy_double", unknown [drop "nptr" []]);
1246+
("caml_copy_double", special [drop "nptr" []] (OCamlAlloc (GoblintCil.integer 1)));
12471247
(* Eeskuju: ("malloc", special [__ "size" []] @@ fun size -> Malloc size); *)
12481248
("caml_alloc_small", special [__ "wosize" []; __ "tag" []] @@ fun wosize tag -> OCamlAlloc wosize);
1249+
("__goblint_caml_param0", special [] @@ OCamlParam {param1 = None; param2 = None; param3 = None; param4 = None; param5 = None});
1250+
("__goblint_caml_param1", special [__ "param" []] @@ fun param -> OCamlParam {param1 = Some param; param2 = None; param3 = None; param4 = None; param5 = None});
1251+
("__goblint_caml_param2", special [__ "param1" []; __ "param2" []] @@ fun param1 param2 -> OCamlParam {param1 = Some param1; param2 = Some param2; param3 = None; param4 = None; param5 = None});
1252+
("__goblint_caml_param3", special [__ "param1" []; __ "param2" []; __ "param3" []] @@ fun param1 param2 param3 -> OCamlParam {param1 = Some param1; param2 = Some param2; param3 = Some param3; param4 = None; param5 = None});
1253+
("__goblint_caml_param4", special [__ "param1" []; __ "param2" []; __ "param3" []; __ "param4" []] @@ fun param1 param2 param3 param4 -> OCamlParam {param1 = Some param1; param2 = Some param2; param3 = Some param3; param4 = Some param4; param5 = None});
1254+
("__goblint_caml_param5", special [__ "param1" []; __ "param2" []; __ "param3" []; __ "param4" []; __ "param5" []] @@ fun param1 param2 param3 param4 param5 -> OCamlParam {param1 = Some param1; param2 = Some param2; param3 = Some param3; param4 = Some param4; param5 = Some param5});
12491255
]
12501256
[@@coverage off]
12511257

tests/regression/90-ocaml/01-bagnall.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
// Buggy code from https://github.com/xavierleroy/pringo/issues/6 where value v is not registered.
44

5-
// putchar from the standard library is marked as a sink
6-
75
#include <stdint.h>
86
#include <string.h>
97
#include <caml/mlvalues.h>
@@ -25,22 +23,22 @@ struct LXM_state { uint64_t a; uint64_t x[2]; uint64_t s; };
2523
CAMLprim value pringo_LXM_copy(value v)
2624
{
2725
value res = caml_alloc_small(Wsizeof(struct LXM_state), Abstract_tag);
28-
memcpy(LXM_val(res), LXM_val(v), sizeof(struct LXM_state));
26+
memcpy(LXM_val(res), LXM_val(v), sizeof(struct LXM_state)); // WARN
2927
return res;
3028
}
3129

3230
CAMLprim value pringo_LXM_copy_correct(value v)
3331
{
3432
CAMLparam1(v);
3533
value res = caml_alloc_small(Wsizeof(struct LXM_state), Abstract_tag);
36-
memcpy(LXM_val(res), LXM_val(v), sizeof(struct LXM_state));
34+
memcpy(LXM_val(res), LXM_val(v), sizeof(struct LXM_state)); // NOWARN
3735
CAMLreturn(res);
3836
}
3937

4038
CAMLprim value pringo_LXM_init_unboxed(uint64_t i1, uint64_t i2,
4139
uint64_t i3, uint64_t i4)
4240
{
43-
value v = caml_alloc_small(Wsizeof(struct LXM_state), Abstract_tag);
41+
value v = caml_alloc_small(Wsizeof(struct LXM_state), Abstract_tag); // NOWARN
4442
struct LXM_state * st = LXM_val(v);
4543
st->a = i1 | 1; /* must be odd */
4644
st->x[0] = i2 != 0 ? i2 : 1; /* must be nonzero */
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// PARAM: --set "ana.activated[+]" ocaml --set "mainfun[+]" "UNARY_OP" --set "mainfun[+]" "BINARY_OP" --set "mainfun[+]" "atof_double" --set "mainfun[+]" "atof_float" --set "mainfun[+]" "max_float" --set "mainfun[+]" "smallest_float" --set "mainfun[+]" "pi_float" --disable warn.imprecise --set "exp.extraspecials[+]" printInt
2+
3+
// Code from stubs.c in src/common/cdomains/floatOps that should be correct despite missing CAMLparam, as discussed in https://github.com/goblint/analyzer/issues/1371.
4+
5+
/* A small definition of the LXM state so sizeof works - from AI */
6+
//struct LXM_state { uint64_t a; uint64_t x[2]; uint64_t s; };
7+
8+
/* Minimal macros to mimic expected behaviour */
9+
#define Wsizeof(ty) ((sizeof(ty) + sizeof(value) - 1) / sizeof(value))
10+
#define LXM_val(v) ((struct LXM_state *) Data_abstract_val(v))
11+
12+
#define CAMLparam1(x) __goblint_caml_param1(&x)
13+
#define CAMLreturn(x) return (x) // From AI - CAMLreturn needs some variable named caml__frame, which is not available in our mock CAMLparam1, so we mock the return as well.
14+
15+
16+
#define _GNU_SOURCE // necessary for M_PI to be defined
17+
#include <stdio.h>
18+
#include <math.h>
19+
#include <float.h>
20+
#include <fenv.h>
21+
#include <assert.h>
22+
#include <caml/mlvalues.h>
23+
#include <caml/alloc.h>
24+
25+
// Order must match with round_mode in floatOps.ml
26+
enum round_mode
27+
{
28+
Nearest,
29+
ToZero,
30+
Up,
31+
Down
32+
};
33+
34+
static void change_round_mode(int mode)
35+
{
36+
switch (mode)
37+
{
38+
case Nearest:
39+
fesetround(FE_TONEAREST);
40+
break;
41+
case ToZero:
42+
fesetround(FE_TOWARDZERO);
43+
break;
44+
case Up:
45+
fesetround(FE_UPWARD);
46+
break;
47+
case Down:
48+
fesetround(FE_DOWNWARD);
49+
break;
50+
default:
51+
// Assert ignored to focus on the OCaml stubs.
52+
assert(0); // FAIL
53+
break;
54+
}
55+
}
56+
57+
#define UNARY_OP(name, type, op) \
58+
CAMLprim value name##_##type(value mode, value x) \
59+
{ \
60+
/* No need to use CAMLparam to keep mode and x as GC roots,
61+
because next GC poll point is at allocation in caml_copy_double.
62+
We have already read their values by then. */ \
63+
int old_roundingmode = fegetround(); \
64+
change_round_mode(Int_val(mode)); \
65+
volatile type r, x1 = Double_val(x); \
66+
r = op(x1); \
67+
fesetround(old_roundingmode); \
68+
return caml_copy_double(r); /* NOWARN */ \
69+
/* No need to use CAMLreturn because we don't use CAMLparam. */ \
70+
}
71+
72+
UNARY_OP(sqrt, double, sqrt);
73+
UNARY_OP(sqrt, float, sqrtf);
74+
UNARY_OP(acos, double, acos);
75+
UNARY_OP(acos, float, acosf);
76+
UNARY_OP(asin, double, asin);
77+
UNARY_OP(asin, float, asinf);
78+
UNARY_OP(atan, double, atan);
79+
UNARY_OP(atan, float, atanf);
80+
UNARY_OP(cos, double, cos);
81+
UNARY_OP(cos, float, cosf);
82+
UNARY_OP(sin, double, sin);
83+
UNARY_OP(sin, float, sinf);
84+
UNARY_OP(tan, double, tan);
85+
UNARY_OP(tan, float, tanf);
86+
87+
#define BINARY_OP(name, type, op) \
88+
CAMLprim value name##_##type(value mode, value x, value y) \
89+
{ \
90+
/* No need to use CAMLparam to keep mode, x and y as GC roots,
91+
because next GC poll point is at allocation in caml_copy_double.
92+
We have already read their values by then. */ \
93+
int old_roundingmode = fegetround(); \
94+
change_round_mode(Int_val(mode)); \
95+
volatile type r, x1 = Double_val(x), y1 = Double_val(y); \
96+
r = x1 op y1; \
97+
fesetround(old_roundingmode); \
98+
return caml_copy_double(r); /* NOWARN */ \
99+
/* No need to use CAMLreturn because we don't use CAMLparam. */ \
100+
}
101+
102+
BINARY_OP(add, double, +);
103+
BINARY_OP(add, float, +);
104+
BINARY_OP(sub, double, -);
105+
BINARY_OP(sub, float, -);
106+
BINARY_OP(mul, double, *);
107+
BINARY_OP(mul, float, *);
108+
BINARY_OP(div, double, /);
109+
BINARY_OP(div, float, /);
110+
111+
CAMLprim value atof_double(value mode, value str)
112+
{
113+
// No need to use CAMLparam to keep mode and str as GC roots,
114+
// because next GC poll point is at allocation in caml_copy_double.
115+
// We have already read their values by then.
116+
const char *s = String_val(str);
117+
volatile double r;
118+
int old_roundingmode = fegetround();
119+
change_round_mode(Int_val(mode));
120+
r = atof(s);
121+
fesetround(old_roundingmode);
122+
return caml_copy_double(r); // NOWARN
123+
// No need to use CAMLreturn because we don't use CAMLparam.
124+
}
125+
126+
CAMLprim value atof_float(value mode, value str)
127+
{
128+
// No need to use CAMLparam to keep mode and str as GC roots,
129+
// because next GC poll point is at allocation in caml_copy_double.
130+
// We have already read their values by then.
131+
const char *s = String_val(str); // Despite not being a value, this is a pointer into the OCaml heap and needs to be checked against GC. Doubles are also pointers.
132+
volatile float r;
133+
int old_roundingmode = fegetround(); // NOWARN
134+
change_round_mode(Int_val(mode)); // This makes a copy of the mode and no problems will arise.
135+
r = (float)atof(s);
136+
fesetround(old_roundingmode);
137+
return caml_copy_double(r); // NOWARN
138+
// No need to use CAMLreturn because we don't use CAMLparam.
139+
}
140+
141+
// These are only given for floats as these operations involve no rounding and their OCaml implementation (Float module) can be used
142+
143+
CAMLprim value max_float(value unit)
144+
{
145+
// No need to use CAMLparam to keep unit as GC root,
146+
// because we don't use it.
147+
return caml_copy_double(FLT_MAX); // NOWARN
148+
// No need to use CAMLreturn because we don't use CAMLparam.
149+
}
150+
151+
CAMLprim value smallest_float(value unit)
152+
{
153+
// No need to use CAMLparam to keep unit as GC root,
154+
// because we don't use it.
155+
return caml_copy_double(FLT_MIN); // NOWARN
156+
// No need to use CAMLreturn because we don't use CAMLparam.
157+
}
158+
159+
CAMLprim value pi_float(value unit)
160+
{
161+
return caml_copy_double(M_PI); // NOWARN
162+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// PARAM: --set "ana.activated[+]" ocaml --set "mainfun[+]" "caml_gc_counters" --set "mainfun[+]" "caml_gc_counters_correct" --disable warn.imprecise --set "exp.extraspecials[+]" printInt
2+
3+
// OCaml code fixed by Demi Marie Obenour in https://github.com/ocaml/ocaml/pull/13370, before and after the fix.
4+
5+
/* Minimal macros to mimic expected behaviour */
6+
#define Wsizeof(ty) ((sizeof(ty) + sizeof(value) - 1) / sizeof(value))
7+
#define LXM_val(v) ((struct LXM_state *) Data_abstract_val(v))
8+
9+
#define CAMLparam0() __goblint_caml_param0()
10+
#define CAMLparam1(x) __goblint_caml_param1(&x)
11+
#define CAMLreturn(x) return (x) // From AI - CAMLreturn needs some variable named caml__frame, which is not available in our mock CAMLparam1, so we mock the return as well.
12+
13+
14+
CAMLprim value caml_gc_counters(value v)
15+
{
16+
CAMLparam0 (); /* v is ignored */
17+
CAMLlocal1 (res);
18+
19+
/* get a copy of these before allocating anything... */
20+
double minwords = caml_gc_minor_words_unboxed();
21+
double prowords = (double)Caml_state->stat_promoted_words;
22+
double majwords = Caml_state->stat_major_words +
23+
(double) Caml_state->allocated_words;
24+
25+
res = caml_alloc_3(0,
26+
caml_copy_double (minwords),
27+
caml_copy_double (prowords),
28+
caml_copy_double (majwords)); // WARN
29+
CAMLreturn (res);
30+
}
31+
32+
CAMLprim value caml_gc_counters_correct(value v)
33+
{
34+
CAMLparam0 (); /* v is ignored */
35+
CAMLlocal3 (minwords_, prowords_, majwords_);
36+
37+
/* get a copy of these before allocating anything... */
38+
double minwords = caml_gc_minor_words_unboxed();
39+
double prowords = (double)Caml_state->stat_promoted_words;
40+
double majwords = Caml_state->stat_major_words +
41+
(double) Caml_state->allocated_words;
42+
43+
minwords_ = caml_copy_double(minwords);
44+
prowords_ = caml_copy_double(prowords);
45+
majwords_ = caml_copy_double(majwords);
46+
v = caml_alloc_small(3, 0);
47+
Field(v, 0) = minwords_;
48+
Field(v, 1) = prowords_;
49+
Field(v, 2) = majwords_;
50+
CAMLreturn(v);
51+
}

0 commit comments

Comments
 (0)