-
Notifications
You must be signed in to change notification settings - Fork 48
Bug: Ultimate C translation generates incorrect union field accesses #759
Description
The current C translation generates incorrect accesses to an union field.This issue can be reproduced with Ultimate Automizer (0.3.0-dev-23b7c4e042 with OpenJDK 21.0.9) using the following input:
union u {
int a;
int b;
};
int main()
{
union u foo;
foo.a = 10;
foo.b = 20;
//@ assert (foo.a == 20);
//@ assert (foo.b == 20);
return 0;
}The input program is not proven to be correct as expected, because the ACSL assertion foo.a == 20 is found to be violated. The reason for this erroneous result is the incorrect generation of accesses to the union fields. The generated Boogie code for the accesses looks like this:
[...]
main_~foo~0#1!a := 10;
main_~foo~0#1!b := 20;
assert 20 == main_~foo~0#1!a;
assert 20 == main_~foo~0#1!b;
[...]The incorrectly generated Boogie code shows that writing to the last field of a union does not invalidate the values written by previous accesses. Instead, reading all fields of the union should return the last written value (assuming, according to the C standard, that the value can be reinterpreted in a suitable representation, which is the case in this example with two identical types). This error only occurs when field accesses are generated based on a LocalLValue. If the access is based on a HeapLValue, the C translation for a union works as expected, as a slightly modified program of the example witnesses:
union u {
int a;
int b;
};
int main()
{
union u foo;
foo.a = 10;
foo.b = 20;
int a = foo.a;
int b = foo.b;
//@ assert (a == 20);
//@ assert (b == 20);
return 0;
}The modified program is proven to be correct, as expected. The correctly generated Boogie code for the accesses looks like this:
[...]
call write~int(10, { base: main_~#foo~0#1!base, offset: main_~#foo~0#1!offset }, 4);
call write~int(20, { base: main_~#foo~0#1!base, offset: main_~#foo~0#1!offset }, 4);
[...]
call main_#t~mem0#1 := read~int({ base: main_~#foo~0#1!base, offset: main_~#foo~0#1!offset }, 4);
main_~a~0#1 := main_#t~mem0#1;
[...]
call main_#t~mem1#1 := read~int({ base: main_~#foo~0#1!base, offset: main_~#foo~0#1!offset }, 4);
main_~b~0#1 := main_#t~mem1#1;
[...]
assert 20 == main_~a~0#1;
assert 20 == main_~b~0#1;
[...]Since access to the shared memory is granted for all fields of a union, the reinterpreted value of the last written field is always correctly returned.