Skip to content

What are the semantics of (repeated) declaration and DEAD for a single symbol? #8258

Open
@tautschnig

Description

@tautschnig

Consider the GOTO program produced from the following C input (this is about GOTO program semantics, not butchering C semantics), which fails to verify for any unwinding value greater or equal to 2:

__CPROVER_bool nondet_bool();

int main()
{
  for(int i = 0; i <= 1 || nondet_bool(); ++i)
  {
    int x = i;
    if(i > 0 && nondet_bool())
      break;
  }
  int escaped = __CPROVER_ID "main::1::1::1::x";
  __CPROVER_assert(escaped > 0, "should hold");
}

Running CBMC yields:

$ cbmc dead2.c --unwind 2 --no-standard-checks --verbosity 4
**** WARNING: Use --unwinding-assertions to obtain sound verification results

** Results:
dead2.c function main
[main.assertion.1] line 12 should hold: FAILURE
VERIFICATION FAILED

While this verification result is consistent for C programs, it should be possible to create a GOTO program in some different way such that main::1::1::1::x is not marked "DEAD" when executing break: GOTO programs don't have a concept of code blocks restricting scope other than as mandated by declarations and DEAD statements. With such a change, the verification failure still persists, but is now surprising for the GOTO program (as shown below for reference, minus instruction 17) will not have killed main::1::1::1::x when assigning to escaped. (The reason is that goto-symex does not make any attempt to merge L1 instances of the same symbol at phi nodes.) Before trying to hack a fix into goto-symex, I'd like to seek consensus on the following questions:

  • When is a fresh instance of some named symbol created, when is that not the case?
  • Does a declaration merely set an instance of an object to a non-deterministic value?
  • Does a (re-)declaration reset the value of __CPROVER_dead_object, or does this need to be left to an explicit GOTO program instruction?
  • Does a DEAD statement equally set an instance of an object to a non-deterministic value?
  • Does a DEAD statement alone non-deterministically update the value stored in __CPROVER_dead_object, or should that be done via a GOTO program statement?
  • Is a repeated DEAD statement (for the same symbol) harmful?

GOTO program for reference:

main /* main */
        // 0 file dead2.c line 5 function main
        DECL main::1::1::i : signedbv[32]
        // 1 file dead2.c line 5 function main
        ASSIGN main::1::1::i := 0
        // 2 file dead2.c line 5 function main
     1: DECL main::$tmp::tmp_if_expr : 𝔹
        // 3 file dead2.c line 5 function main
        IF ¬(main::1::1::i1) THEN GOTO 2
        // 4 file dead2.c line 5 function main
        ASSIGN main::$tmp::tmp_if_expr := true
        // 5 no location
        GOTO 3
        // 6 file dead2.c line 5 function main
     2: DECL main::$tmp::return_value_nondet_bool : 𝔹
        // 7 file dead2.c line 5 function main
        ASSIGN main::$tmp::return_value_nondet_bool := side_effect statement="nondet" #identifier="nondet_bool" is_nondet_nullable="1"
        // 8 file dead2.c line 5 function main
        ASSIGN main::$tmp::tmp_if_expr := (main::$tmp::return_value_nondet_bool ? true : false)
        // 9 file dead2.c line 5 function main
     3: IF ¬main::$tmp::tmp_if_expr THEN GOTO 5
        // 10 file dead2.c line 7 function main
        DECL main::1::1::1::x : signedbv[32]
        // 11 file dead2.c line 7 function main
        ASSIGN main::1::1::1::x := main::1::1::i
        // 12 file dead2.c line 8 function main
        IF ¬(main::1::1::i > 0) THEN GOTO 4
        // 13 file dead2.c line 8 function main
        DECL main::$tmp::return_value_nondet_bool$0 : 𝔹
        // 14 file dead2.c line 8 function main
        ASSIGN main::$tmp::return_value_nondet_bool$0 := side_effect statement="nondet" #identifier="nondet_bool" is_nondet_nullable="1"
        // 15 file dead2.c line 8 function main
        IF ¬main::$tmp::return_value_nondet_bool$0 THEN GOTO 4
        // 16 file dead2.c line 9 function main
        DEAD main::$tmp::return_value_nondet_bool$0
        // 17 file dead2.c line 9 function main
        DEAD main::1::1::1::x
        // 18 file dead2.c line 9 function main
        GOTO 5
        // 19 file dead2.c line 10 function main
     4: DEAD main::$tmp::return_value_nondet_bool$0
        // 20 file dead2.c line 10 function main
        DEAD main::1::1::1::x
        // 21 file dead2.c line 5 function main
        ASSIGN main::1::1::i := main::1::1::i + 1
        // 22 file dead2.c line 5 function main
        GOTO 1
        // 23 file dead2.c line 10 function main
     5: DEAD main::$tmp::tmp_if_expr
        // 24 file dead2.c line 10 function main
        DEAD main::$tmp::return_value_nondet_bool
        // 25 file dead2.c line 10 function main
        DEAD main::1::1::i
        // 26 file dead2.c line 11 function main
        DECL main::1::escaped : signedbv[32]
        // 27 file dead2.c line 11 function main
        ASSIGN main::1::escaped := main::1::1::1::x
        // 28 file dead2.c line 12 function main
        ASSERT main::1::escaped > 0 // should hold
        // 29 file dead2.c line 13 function main
        DEAD main::1::escaped
        // 30 file dead2.c line 13 function main
        SET RETURN VALUE side_effect statement="nondet" is_nondet_nullable="1"
        // 31 file dead2.c line 13 function main
        END_FUNCTION

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