Skip to content

--liveness-to-safety reports PROVED for a false liveness property with >=2 fairness assumptions #2007

Description

@tautschnig

--liveness-to-safety reports PROVED for a false liveness property in the presence of ≥2 fairness assumptions

Summary

ebmc --liveness-to-safety reports PROVED for an s_eventually property that is
plainly false, when the module contains two or more assume property (always s_eventually …)
fairness assumptions. The --buechi engine and plain bounded model checking both correctly
report REFUTED on the same input. A single fairness assumption (or none) does not trigger
the problem, so the issue is specific to how --liveness-to-safety combines multiple liveness
assumptions.

Environment

  • ebmc: hw-cbmc at commit 7b05b8d (built from source, make -C src)
  • Spot: ltl2tgba (spot) 2.14.3 (for --buechi)
  • OS: Ubuntu 24.04, x86_64

Minimal reproducer

// x is a register stuck at 0 forever, so "s_eventually x" is FALSE.
module min_fair2(input clk, input act);
  reg x; initial x = 1'b0;
  always @(posedge clk) x <= x;                 // x never changes
  assume property (always s_eventually act);    // fairness 1
  assume property (always s_eventually (!act));  // fairness 2
  p_live: assert property (s_eventually x);      // FALSE
endmodule

Observed vs expected

command result expected
ebmc min.v --systemverilog --top min_fair2 --liveness-to-safety --bdd PROVED REFUTED
ebmc min.v --systemverilog --top min_fair2 --liveness-to-safety --bound 40 PROVED up to bound 40 REFUTED
ebmc min.v --systemverilog --top min_fair2 --buechi REFUTED REFUTED
ebmc min.v --systemverilog --top min_fair2 --bound 40 REFUTED REFUTED

x is initialized to 0 and never assigned anything else, so s_eventually x can never hold;
REFUTED is correct. --liveness-to-safety returning PROVED is unsound.

Minimization / trigger isolation

  • No fairness assumption — correct:
    module min_stuck(input clk);
      reg x; initial x = 1'b0;
      always @(posedge clk) x <= x;
      p_live: assert property (s_eventually x);   // l2s, buechi, bmc all REFUTE (correct)
    endmodule
  • One fairness assumption — correct:
    module min_fair1(input clk, input act);
      reg x; initial x = 1'b0;
      always @(posedge clk) x <= x;
      assume property (always s_eventually act);
      p_live: assert property (s_eventually x);   // l2s REFUTES (correct)
    endmodule
  • Two fairness assumptions (min_fair2 above) — --liveness-to-safety wrongly PROVES.

So the trigger is ≥2 always s_eventually fairness assumptions.

Hypothesis

The liveness-to-safety translation appears to mis-combine multiple always s_eventually
fairness assumptions into its loop/acceptance condition, erroneously excluding the fair
counterexample lasso (the state where x stays 0 while act toggles — which satisfies both
s_eventually act and s_eventually !act). As a result the safety translation is vacuously
discharged and the false liveness property is reported PROVED.

Impact

Any --liveness-to-safety proof of a liveness property in a model with multiple fairness
assumptions cannot be trusted; it can report PROVED for false properties. --buechi is a
sound alternative.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions