Skip to content

Commit 51ce3e9

Browse files
committed
Validate integer constants on input and when folding
Paranoid check.
1 parent 9abbb86 commit 51ce3e9

3 files changed

Lines changed: 50 additions & 7 deletions

File tree

lib/Checker.ml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,11 @@ and infer' env e =
646646
| EQualified lid ->
647647
lookup_global env lid
648648

649-
| EConstant (w, _) ->
649+
| EConstant ((w, s) as k) ->
650+
(* Validate integers to make sure we are not accepting constants
651+
like "-1" as unsigned. *)
652+
if not (is_float w) && not (is_valid_int k) then
653+
checker_error env "constant %s is not a valid %a" s ptyp (TInt w);
650654
TInt w
651655

652656
| EStandaloneComment _ ->

lib/Constant.ml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,53 @@ let unsigned_of_signed = function
6969
| CInt | UInt8 | UInt16 | UInt32 | UInt64 | SizeT
7070
| Float32 | Float64 -> raise (Invalid_argument "unsigned_of_signed")
7171

72+
let is_int = function
73+
| UInt8 | UInt16 | UInt32 | UInt64
74+
| Int8 | Int16 | Int32 | Int64
75+
| SizeT -> true
76+
| _ -> false
77+
7278
let is_signed = function
7379
| Int8 | Int16 | Int32 | Int64 | CInt | PtrdiffT -> true
7480
| UInt8 | UInt16 | UInt32 | UInt64 | SizeT -> false
7581
| Float32 | Float64 -> raise (Invalid_argument "is_signed: float")
7682

7783
let is_unsigned w = not (is_signed w)
7884

85+
(* The range of the values representable at a given width, when known. *)
86+
let bounds_of_width (w: width) : (Z.t * Z.t) option =
87+
let two = Z.of_int 2 in
88+
let unsigned bits = Some (Z.zero, Z.pred (Z.pow two bits)) in
89+
let signed bits =
90+
Some (Z.neg (Z.pow two (bits - 1)), Z.pred (Z.pow two (bits - 1)))
91+
in
92+
match w with
93+
| UInt8 -> unsigned 8
94+
| UInt16 -> unsigned 16
95+
| UInt32 -> unsigned 32
96+
| UInt64 -> unsigned 64
97+
(* size_t is platform-dependent (at least 16 bits, at most 64 in practice),
98+
we use the loosest bound *)
99+
| SizeT -> unsigned 64
100+
| Int8 -> signed 8
101+
| Int16 -> signed 16
102+
| Int32 -> signed 32
103+
| Int64 -> signed 64
104+
| CInt | PtrdiffT -> None
105+
| Float32 | Float64 -> None
106+
107+
(* Whether this is a well-formed integer constant. *)
108+
let is_valid_int ((w, s): t) : bool =
109+
if is_float w then
110+
false
111+
else
112+
match Z.of_string s with
113+
| exception Invalid_argument _ -> false
114+
| z ->
115+
match bounds_of_width w with
116+
| None -> true
117+
| Some (lo, hi) -> Z.leq lo z && Z.leq z hi
118+
79119
let without_wrap = function
80120
| AddW -> Add
81121
| SubW -> Sub

lib/Simplify.ml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -466,14 +466,10 @@ let constant_fold = object (self)
466466
| EOp (K.Add, TInt w), [ e1; e2 ] -> (
467467
let e1 = self#visit_expr env e1 in
468468
let e2 = self#visit_expr env e2 in
469-
let is_int w =
470-
match w with
471-
| K.UInt8 | K.UInt16 | K.UInt32 | K.UInt64 | K.Int8 | K.Int16 | K.Int32 | K.Int64 | K.SizeT -> true
472-
| _ -> false
473-
in
474469
match e1.node, e2.node with
475470
(* Sum literals *)
476-
| EConstant (w1, s1), EConstant (w2, s2) when is_int w && w = w1 && w1 = w2 ->
471+
| EConstant (w1, s1), EConstant (w2, s2) when K.is_int w && w = w1 && w1 = w2 ->
472+
assert (K.is_valid_int (w1, s1) && K.is_valid_int (w2, s2));
477473
EConstant (w1, op_on_strings Z.add w s1 s2)
478474
(* 0+x, x+0 ~> x *)
479475
| EConstant (w1, "0"), e2 when w = w1 -> e2
@@ -487,6 +483,7 @@ let constant_fold = object (self)
487483
match e1.node, e2.node with
488484
(* Multiply literals *)
489485
| EConstant (w1, s1), EConstant (w2, s2) when w = w1 && w1 = w2 ->
486+
assert (K.is_valid_int (w1, s1) && K.is_valid_int (w2, s2));
490487
EConstant (w1, op_on_strings Z.mul w s1 s2)
491488
(* 0*x, x*0 ~> 0 *)
492489
| EConstant (w1, "0"), _ when w = w1 && is_readonly_c_expression e2 -> EConstant(w1, "0");
@@ -503,6 +500,7 @@ let constant_fold = object (self)
503500
match e1.node, e2.node with
504501
(* Division of literals. Note, ZArith div/rem coincides with C semantics. *)
505502
| EConstant (w1, s1), EConstant (w2, s2) when w = w1 && w1 = w2 ->
503+
assert (K.is_valid_int (w1, s1) && K.is_valid_int (w2, s2));
506504
EConstant (w1, op_on_strings Z.div w s1 s2)
507505
(* 0/x ~> 0 *)
508506
| EConstant (w2, "0"), _ when w = w2 && is_readonly_c_expression e2 -> EConstant(w2, "0")
@@ -517,6 +515,7 @@ let constant_fold = object (self)
517515
match e1.node, e2.node with
518516
(* Mod of literals. Note, ZArith div/rem coincides with C semantics. *)
519517
| EConstant (w1, s1), EConstant (w2, s2) when w = w1 && w1 = w2 ->
518+
assert (K.is_valid_int (w1, s1) && K.is_valid_int (w2, s2));
520519
EConstant (w1, op_on_strings Z.rem w s1 s2)
521520
(* 0 % x ~> 0 *)
522521
| EConstant (w2, "0"), _ when w = w2 && is_readonly_c_expression e2 -> EConstant(w2, "0")

0 commit comments

Comments
 (0)