Skip to content

Unsound address set equality with mismatching offsets #564

@sim642

Description

@sim642

Program

Consider the following program:

#include <assert.h>

typedef struct {
  int x;
} a;

int main() {
  a z;
  a *y = &z;

  int *m = &y->x; // {&z.x}
  a *n = &y[0]; // {&z[def_exc:0]}

  int b = m == n;
  assert(b);
  return 0;
}

In concrete the assertion actually holds, but Goblint unsoundly reports FAIL.

Reason

Base analysis implements address set equality as follows:

| Eq ->
`Int (if AD.is_bot (AD.meet p1 p2) then ID.of_int ik BI.zero else match eq p1 p2 with Some x when x -> ID.of_int ik BI.one | _ -> bool_top ik)

AD.meet applies meet to the offsets as follows:
let rec merge cop x y =
let op = match cop with `Join -> Idx.join | `Meet -> Idx.meet | `Widen -> Idx.widen | `Narrow -> Idx.narrow in
match x, y with
| `NoOffset, `NoOffset -> `NoOffset
| `NoOffset, x
| x, `NoOffset -> (match cop, cmp_zero_offset x with
| (`Join | `Widen), (`MustZero | `MayZero) -> x
| (`Meet | `Narrow), (`MustZero | `MayZero) -> `NoOffset
| _ -> raise Lattice.Uncomparable)
| `Field (x1,y1), `Field (x2,y2) when CilType.Fieldinfo.equal x1 x2 -> `Field (x1, merge cop y1 y2)
| `Index (x1,y1), `Index (x2,y2) -> `Index (op x1 x2, merge cop y1 y2)
| _ -> raise Lattice.Uncomparable

Since one is Field and the other Index, it raises Uncomparable, which in HoarePO results in an empty address set. Hence base analysis considers the address sets disjoint, concludes that they cannot equal and returns 0.

Problem

Our operations on mismatching offset constructors are sketchy and the behavior isn't well-defined, as already pointed out in #559. There's some special logic for equating no offset with [0] and the first field, but there are also inconsistencies in this handling. Moreover, the logic for this is already quite involved, as seen from the above snippet.

Even if the zero index and first field handling worked perfectly, there are still cases where there's no obvious answer. For example, what should be the meet of [5] and .g (not first field)? It should not be Uncomparable or empty, because that leads to the unsound address sets. To be sound, it should be something because we don't know for sure that they cannot alias. But it also cannot be an unknown offset (index), because that would be their join. So join and meet would be equal, which is nonsense.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions