Skip to content

Improve int domain bitwise operators, support struct bitfields #1739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 39 commits into
base: master
Choose a base branch
from

Conversation

H-Innos
Copy link

@H-Innos H-Innos commented May 5, 2025

Additions for my BSc thesis "Improving Bitwise Operation Analysis in Goblint", supervised by Simmo Saan. This includes making the logand, logor, logxor, lognot, and shift_right abstractions more precise in the interval, interval sets, defexc, and enums domains. Additionally, I added support for the analysis of bit-fields.

@sim642 sim642 added feature student-job sv-comp SV-COMP (analyses, results), witnesses precision labels May 5, 2025
@sim642 sim642 self-assigned this May 5, 2025
@sim642
Copy link
Member

sim642 commented May 5, 2025

I fixed the merge conflicts with #1623 and #1727, bringing this up to date with master.

Comment on lines 496 to 533
let logor ik x y = norm ik (match x,y with
| `Excluded (_, r), `Definite i
| `Definite i, `Excluded (_, r) ->
if Z.compare i Z.zero >= 0 then
`Excluded (S.empty (), R.join r (R.of_interval range_ikind (Int64.zero, Int64.of_int @@ Z.numbits i)))
else
(match R.minimal r, R.maximal r with
| None, _ | _, None -> top_of ik
| Some r1, Some r2 -> let b = Int.max (Z.numbits i) (Int64.to_int(Int64.max (Int64.abs r1) (Int64.abs r2))) in
`Excluded (S.empty (), R.of_interval range_ikind (Int64.of_int @@ -b, Int64.of_int b))
)
| `Excluded (_, r1), `Excluded (_, r2) -> `Excluded (S.empty (), R.join r1 r2)
| `Definite x, `Definite y ->
(try `Definite (Z.logor x y) with | Division_by_zero -> top_of ik)
| `Bot, `Bot -> `Bot
| _ ->
(* If only one of them is bottom, we raise an exception that eval_rv will catch *)
raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))))

let logor = lift2 Z.logor
let logxor = lift2 Z.logxor
let logxor ik x y = norm ik (match x,y with
| `Definite i, `Excluded (_, r)
| `Excluded (_, r), `Definite i -> begin
if Z.compare i Z.zero >= 0 then
`Excluded (S.empty (), R.join r (R.of_interval range_ikind (Int64.zero, Int64.of_int @@ Z.numbits i)))
else
match R.minimal r, R.maximal r with
| None, _ | _, None -> top_of ik
| Some r1, Some r2 -> let b = Int.max (Z.numbits i) (Int64.to_int(Int64.max (Int64.abs r1) (Int64.abs r2))) in
`Excluded (S.empty (), R.of_interval range_ikind (Int64.of_int @@ -b, Int64.of_int b))
end
| `Excluded (_, r1), `Excluded (_, r2) -> `Excluded (S.empty (), R.join r1 r2)
(* The good case: *)
| `Definite x, `Definite y ->
(try `Definite (Z.logxor x y) with | Division_by_zero -> top_of ik)
| `Bot, `Bot -> `Bot
| _ ->
(* If only one of them is bottom, we raise an exception that eval_rv will catch *)
raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a little like copy pasta 🍝

Comment on lines 239 to 295
let logand ikind u v =
handle_bot u v (fun () ->
norm ikind @@ match u, v with
| Inc x,Inc y when BISet.is_singleton x && BISet.is_singleton y -> Inc (BISet.singleton (Z.logand (BISet.choose x) (BISet.choose y)))
| Inc x, Exc (s,r)
| Exc (s,r), Inc x ->
let f = fun i ->
if Z.compare i Z.zero >= 0 then
R.join r (R.of_interval range_ikind (Int64.zero, Int64.of_int @@ Z.numbits i))
else
(match R.minimal r, R.maximal r with
| None, _ | _, None -> R.top ()
| Some r1, Some r2 -> let b = Int.max (Z.numbits i) (Int64.to_int(Int64.max (Int64.abs r1) (Int64.abs r2))) in
R.of_interval range_ikind (Int64.of_int @@ -b, Int64.of_int b)
) in
let r' = BISet.fold (fun i acc -> R.join (f i) acc) x (R.bot ()) in
Exc (BISet.empty (), r')
| Exc (_, r1), Exc (_, r2) -> Exc (BISet.empty (), R.meet r1 r2)
| _,_ -> top_of ikind)

let logor ikind u v = handle_bot u v (fun () ->
norm ikind @@ match u, v with
| Inc x,Inc y when BISet.is_singleton x && BISet.is_singleton y -> Inc (BISet.singleton (Z.logor (BISet.choose x) (BISet.choose y)))
| Inc x, Exc (_,r)
| Exc (_,r), Inc x ->
let f = fun i ->
if Z.compare i Z.zero >= 0 then
R.join r (R.of_interval range_ikind (Int64.zero, Int64.of_int @@ Z.numbits i))
else
(match R.minimal r, R.maximal r with
| None, _ | _, None -> R.top ()
| Some r1, Some r2 -> let b = Int.max (Z.numbits i) (Int64.to_int(Int64.max (Int64.abs r1) (Int64.abs r2))) in
R.of_interval range_ikind (Int64.of_int @@ -b, Int64.of_int b)
) in
let r' = BISet.fold (fun i acc -> R.join (f i) acc) x (R.bot ()) in
Exc (BISet.empty (), r')
| Exc (_, r1), Exc (_, r2) -> Exc (BISet.empty (), R.join r1 r2)
| _ -> top_of ikind)

let logxor ikind u v = handle_bot u v (fun () ->
norm ikind @@ match u, v with
| Inc x,Inc y when BISet.is_singleton x && BISet.is_singleton y -> Inc (BISet.singleton (Z.logxor (BISet.choose x) (BISet.choose y)))
| Inc x, Exc (_,r)
| Exc (_,r), Inc x ->
let f = fun i ->
if Z.compare i Z.zero >= 0 then
R.join r (R.of_interval range_ikind (Int64.zero, Int64.of_int @@ Z.numbits i))
else
match R.minimal r, R.maximal r with
| None, _ | _, None -> R.top ()
| Some r1, Some r2 -> let b = Int.max (Z.numbits i) (Int64.to_int(Int64.max (Int64.abs r1) (Int64.abs r2))) in
R.of_interval range_ikind (Int64.of_int @@ -b, Int64.of_int b)
in
let r' = BISet.fold (fun i acc -> R.join (f i) acc) x (R.bot ()) in
Exc (BISet.empty (), r')
| Exc (_, r1), Exc (_, r2) -> Exc (BISet.empty (), R.join r1 r2)
| _ -> top_of ikind)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These three functions look very similar?

@sim642 sim642 changed the title Bit operation additions Improve int domain bitwise operators, support struct bitfields May 5, 2025
@sim642
Copy link
Member

sim642 commented May 7, 2025

There seems to be a bug: the regression test 83/07 refine-with-interval crashes. It looks like & for intervals computes something unsound there.
EDIT: I added a unit test for the case. I'm surprised that the domain tests never construct such a case.

I also did a 60s sv-benchmarks run, which revealed 10 unsound unreach-call results:

  • hardware-verification-bv/btor2c-lazyMod.adding.1.prop1-func-interl.yml
  • hardware-verification-bv/btor2c-lazyMod.adding.2.prop1-func-interl.yml
  • hardware-verification-bv/btor2c-lazyMod.adding.3.prop1-func-interl.yml
  • hardware-verification-bv/btor2c-lazyMod.adding.4.prop1-func-interl.yml
  • hardware-verification-bv/btor2c-lazyMod.adding.5.prop1-func-interl.yml
  • hardware-verification-bv/btor2c-lazyMod.adding.6.prop1-func-interl.yml
  • hardware-verification-bv/btor2c-lazyMod.sw_sym_ex_v.yml
  • hardware-verification-bv/btor2c-lazyMod.unsafe_analog_estimation_convergence.yml
  • hardware-verification-bv/btor2c-lazyMod.unsafe_intersymbol_analog_estimation_convergence.yml
  • hardware-verification-bv/btor2c-lazyMod.vis_arrays_palu.yml

@sim642 sim642 force-pushed the bit-operation-additions branch from 27d26c4 to fc50485 Compare May 7, 2025 07:26
@sim642
Copy link
Member

sim642 commented May 8, 2025

@michael-schwarz
Copy link
Member

I saw the commit message

now correctness proven for all

and got curious. What exactly are you proving here and how are you going about it? Are these manual proofs or some sort of mechanized approach? If yes, can we use it for our other domains too?

@sim642
Copy link
Member

sim642 commented May 9, 2025

and got curious. What exactly are you proving here and how are you going about it? Are these manual proofs or some sort of mechanized approach? If yes, can we use it for our other domains too?

Since the thesis will be in Estonian, I can't say "wait a week". Various cases of each bitwise operation are formulated as Z3 (instead of OCaml code) queries over the bitvector SMT theory by bitblasting or whatever Z3 decides to do.
The point being that it's very difficult to reason about bits in two's complement representations of negative numbers and how those correspond to interval (or bit range) bounds. So it's an extra layer of confidence, but for manual reencoding of the OCaml implementation as Z3.

One probably could do this more widely, but the manual re-encoding is still error prone, so the guarantees aren't super strong.
In a broader context, it would be much more valuable to try to extract some domain implementations from Rocq or whatnot.

Nevertheless, I'm wondering if we should have these Z3 proof scripts added into this repository for the sake of it?

@michael-schwarz
Copy link
Member

Interesting!

Nevertheless, I'm wondering if we should have these Z3 proof scripts added into this repository for the sake of it?

Not sure if we want to add it to the repo as it may go out of sync, but I'm definitely curious what they look like!

@H-Innos
Copy link
Author

H-Innos commented May 11, 2025

Not sure if we want to add it to the repo as it may go out of sync, but I'm definitely curious what they look like!

All the proofs are in this repo: https://github.com/H-Innos/Z3

@sim642 sim642 added this to the SV-COMP 2026 milestone May 22, 2025
@H-Innos H-Innos marked this pull request as ready for review June 8, 2025 21:36
if Ints_t.equal n Ints_t.zero then
Ints_t.neg Ints_t.one
else
Ints_t.neg @@ Ints_t.shift_left Ints_t.one (Z.numbits (Z.sub (Z.abs @@ Ints_t.to_bigint n) Z.one))

Check warning

Code scanning / Semgrep OSS

Semgrep Finding: semgrep.z-sub-one Warning

use Z.pred instead
if Ints_t.equal n Ints_t.zero then
Ints_t.neg Ints_t.one
else
Ints_t.neg @@ Ints_t.shift_left Ints_t.one (Z.numbits (Z.sub (Z.abs @@ Ints_t.to_bigint n) Z.one))

Check warning

Code scanning / Semgrep OSS

Semgrep Finding: semgrep.z-sub-one Warning

use Z.pred instead
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature precision student-job sv-comp SV-COMP (analyses, results), witnesses
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants