diff --git a/src/plugins/value/domains/octagons.ml b/src/plugins/value/domains/octagons.ml index 9248aeda41d0850d220596a0dd6b8dd5afbf606a..b89f97df060966d1df4dcc2bd2fef14aad679321 100644 --- a/src/plugins/value/domains/octagons.ml +++ b/src/plugins/value/domains/octagons.ml @@ -132,22 +132,26 @@ type octagon = Use Ival.t to evaluate expressions. *) module Rewriting = struct - (* Checks that the interval [ival] fits in the C type [typ]. + (* Checks that the interval [ival] fits in the C range [range]. This is used to ensure that an expression cannot overflow: this module uses the mathematical semantics of arithmetic operations, and cannot soundly translate overflows in the C semantics. *) - let fits_in_type typ ival = + let fits_in_range range ival = + let min = Eval_typ.range_lower_bound range in + let max = Eval_typ.range_upper_bound range in + let range = Ival.inject_range (Some min) (Some max) in + Ival.is_included ival range + + let may_overflow typ ival = let open Eval_typ in match classify_as_scalar typ with | None -> assert false (* This should not happen here. *) - | Some (TSFloat _) -> true - | Some (TSInt ir | TSPtr ir) -> - (ir.i_signed && Kernel.SignedOverflow.get ()) || - (not ir.i_signed && Kernel.UnsignedOverflow.get ()) || - let min = range_lower_bound ir in - let max = range_upper_bound ir in - let range = Ival.inject_range (Some min) (Some max) in - Ival.is_included ival range + | Some (TSFloat _) -> false + | Some (TSInt range | TSPtr range) -> + not + ((range.i_signed && Kernel.SignedOverflow.get ()) || + (not range.i_signed && Kernel.UnsignedOverflow.get ()) || + fits_in_range range ival) (* Simplified form [±X-coeff] for expressions, where X is a variable and coeff an interval. *) @@ -176,9 +180,9 @@ module Rewriting = struct evaluate e2 >> fun v2 -> let typ_e1 = Cil.typeOf e1 in let result = Arith.apply op typ_e1 v1 v2 in - if fits_in_type typ_e1 result && fits_in_type typ result - then f typ_e1 v1 v2 - else [] + if may_overflow typ_e1 result || may_overflow typ result + then [] + else f typ_e1 v1 v2 (* Rewrites the Cil expression [expr] into the simplified form [±x-coeff], where [x] is a non-singleton variable and [coeff] is an interval. The @@ -200,9 +204,8 @@ module Rewriting = struct | UnOp (Neg, e, typ) -> evaluate e >> fun v -> - if fits_in_type typ (Arith.neg v) - then List.map neg (rewrite evaluate e) - else [] + if may_overflow typ (Arith.neg v) + then [] else List.map neg (rewrite evaluate e) | BinOp ((PlusA | MinusA as binop), e1, e2, typ) -> let op = if binop = PlusA then Add else Sub in @@ -223,7 +226,7 @@ module Rewriting = struct | CastE (typ, e) -> if Cil.(isIntegralType typ && isIntegralType (typeOf e)) then evaluate e >> fun v -> - if fits_in_type typ v then rewrite evaluate e else [] + if may_overflow typ v then [] else rewrite evaluate e else [] | Info (e, _) -> rewrite evaluate e @@ -295,6 +298,28 @@ module Rewriting = struct make_octagons_from_binop typ e1 Sub e2 range | _ -> [] + let overflow_alarms typ expr ival = + match Eval_typ.classify_as_scalar typ with + | Some (Eval_typ.TSInt range) -> + let signed = range.Eval_typ.i_signed in + let overflow = if signed then Alarms.Signed else Alarms.Unsigned in + let max_bound = Eval_typ.range_upper_bound range in + let min_bound = Eval_typ.range_lower_bound range in + let ival_range = Ival.inject_range (Some min_bound) (Some max_bound) in + let aux has_better_bound bound bound_kind alarms = + if has_better_bound ival ival_range >= 0 + then + let alarm = Alarms.Overflow (overflow, expr, bound, bound_kind) in + Alarmset.set alarm Alarmset.True alarms + else alarms + in + let alarms = Alarmset.all in + let alarms = + aux Ival.has_greater_min_bound min_bound Alarms.Lower_bound alarms + in + aux Ival.has_smaller_max_bound max_bound Alarms.Upper_bound alarms + | _ -> Alarmset.all + (* Evaluates the Cil expression [expr], by rewriting it into octagonal constraints using [evaluate_expr] to evaluate sub-expressions, and then using [evaluate_octagon] to evaluate the octagons. *) @@ -309,12 +334,13 @@ module Rewriting = struct let evaluate_octagons octagons = List.fold_left evaluate_octagon Ival.top octagons in + let default = Ival.top, Alarmset.all in match expr.enode with | BinOp ((PlusA | MinusA as binop), e1, e2, typ) -> let op = if binop = PlusA then Add else Sub in let octagons = rewrite_binop evaluate_expr e1 op e2 in let ival = evaluate_octagons octagons in - if Ival.(equal top ival) then ival else + if Ival.(equal top ival) then default else let typ_e1 = Cil.typeOf e1 in let ival2 = match evaluate_expr e1, evaluate_expr e2 with @@ -322,9 +348,9 @@ module Rewriting = struct | _, _ -> Ival.top in let ival = Ival.narrow ival ival2 in - if fits_in_type typ ival && fits_in_type typ_e1 ival - then ival - else Ival.top + if may_overflow typ ival || may_overflow typ_e1 ival + then default + else ival, overflow_alarms typ expr ival | BinOp ((Lt | Gt | Le | Ge | Eq as binop), e1, e2, _typ) when Cil.isIntegralType (Cil.typeOf e1) -> let comp = Value_util.conv_comp binop in @@ -333,9 +359,10 @@ module Rewriting = struct let range = comparison_range comp in let octagons = rewrite_binop evaluate_expr e1 Sub e2 in let ival = evaluate_octagons octagons in - if Ival.is_included ival range then Ival.one - else if not (Ival.intersects ival range) then Ival.zero else Ival.top - | _ -> Ival.top + if Ival.is_included ival range then Ival.one, Alarmset.all + else if not (Ival.intersects ival range) + then Ival.zero, Alarmset.all else default + | _ -> default end @@ -949,14 +976,14 @@ module Domain = struct with Cvalue.V.Not_based_on_null -> `Top in let evaluate_octagon octagon = Octagons.evaluate octagon state.octagons in - let ival = + let ival, alarms = Rewriting.evaluate_through_octagons evaluate_expr evaluate_octagon expr in if Ival.(equal ival top) then top_value else if Ival.is_bottom ival then `Bottom, Alarmset.all - else `Value (Cvalue.V.inject_ival ival, ()), Alarmset.all + else `Value (Cvalue.V.inject_ival ival, ()), alarms let extract_lval _oracle _t _lval _typ _loc = top_value