0

I define device accesses as thus

volatile struct mydevice * const dev = (struct mydevice *)MY_DEVICE_ADDRESS;

I've modeled the accesses using

@ volatile dev->somereg reads somereg_read writes somereg_write;

Now the problem is that when I enable RTE checks, the generated validity checks can't be proved

/*@ assert rte: mem_access: \valid(dev->somereg); */

Is there any way to annotate my code such that MY_DEVICE_ADDRESS up to MY_DEVICE_ADDRESS+sizeof(struct mydevice) is considered valid?

EDIT: Here's an attempt that does not work

#include <stdint.h>

#define MY_DEVICE_ADDRESS (0x80000000)

struct mydevice {
  uint32_t somereg;
  uint32_t someotherreg;
};

volatile struct mydevice * const dev = (struct mydevice *)MY_DEVICE_ADDRESS;

/*@ axiomatic Physical_Memory {
     axiom Dev: \valid((struct mydevice*)MY_DEVICE_ADDRESS);
  } */

int main(int argc, const char *argv[]) {
  //@ assert \valid(dev);
  //@ assert \false;
  return 0;
}

Run with

frama-c-wp -wp-rte -wp-init-const -wp-model Typed test.c

Yifan
  • 4,867
  • 5
  • 25
  • 24
  • 1
    I somehow doubt that the problem you describe is related to the `volatile` nature of your memory location (even though this is indeed the source of other problems). From what you describe, I suspect that `MY_DEVICE_ADDRESS` is an absolute physical address (e.g. 0xdeadbeef). Could you provide a MWE and indicate which plug-in you intend to use (again, I suppose this is WP, which, I'm afraid, will not be able to prove anything about absolute memory address) – Virgile Jul 21 '17 at 06:55
  • Yes it's WP and yes it's an absolute physical address. That's what I'm afraid of, I'll see if the developers have a solution. – Yifan Jul 21 '17 at 15:45

2 Answers2

2

I think that the only way to have the assertion proved is to put an axiomatic block of the form

/*@ axiomatic Physical_Memory {
     axiom Dev: \valid((struct mydevice*)MY_DEVICE_ADDRESS);
     // add more if you have other physical memory accesses
  } */

There is an option in the kernel -absolute-valid-range <min-max> to indicate that dereferencing a pointer within the given interval is OK, but only EVA is able to take advantage of it (I'm afraid this is too low-level for WP's memory models).

Note in addition that you can pass option -wp-init-const to WP to indicate that it should add in its context the fact the global const variables are always equal to their initial value.

Edit

As mentioned in the comments, the proposed axiom is indeed inconsistent with WP's memory model. The bulk of the issue lies in the fact that in said model a numeric address 0xnnnn is apparently defined as shift(global(0),0xnnnn). As can be seen in e.g. $FRAMAC_SHARE/wp/ergo/Memory.mlw, global(0) has a base of 0, and so has the shift (which modifies only the offset). The definition of valid_rw imposes a strictly positive base, hence the contradiction.

This has to be fixed at WP's level. However, there are a few workarounds available while waiting for a new Frama-C release:

  • if you don't need to write to the physical location, you can replace \valid by \valid_read in the axiomatic. Definition of \valid_read in the model does not have the base>0 requirement, hence the axiom will not lead to a contradiction.
  • if you can afford modifying the sources, make dev an extern declaration (or define dev as being equal to an extern declaration abstract_dev), and use the abstract constant in the axiom: in this way, you won't have the equality dev.base==0 in the logic model, removing the contradiction.
  • finally, you can patch the memory model in $FRAMAC_SHARE/wp/ergo/Memory.mlw (and $FRAMAC_SHARE/wp/why3/Memory.why and $FRAMAC_SHARE/wp/coq/Memory.v depending on your choice of provers). The easiest way to do that would probably to make global rely on an abstract base, as in:
logic global_base: int

function global(b: int) : addr = { base = global_base + b; offset = 0 }

(Note of course that this answer does in no way guarantee that this does not introduce other issues in the model).

Virgile
  • 9,724
  • 18
  • 42
  • I thought it was working but then I realized it made things inconsistent. If I do "//@ assert \valid((struct mydevice*)MY_DEVICE_ADDRESS);" followed by "//@ assert \false", the proof goes through. – Yifan Jul 21 '17 at 23:33
  • I can't reproduce this behavior. I think that at this point you really must provide a MWE to indicate _exactly_ what leads to this situation. – Virgile Jul 24 '17 at 05:48
  • I just added it to my main post. – Yifan Jul 24 '17 at 16:19
  • Making `dev` `extern` seems to work and still be consistent. However, I have to manually do it with every register such as `axiom somereg_valid: \valid(&dev->somereg);` then `axiom someotherreg_valid: \valid(&dev-> someotherreg);`. Any way to do it all at once? – Yifan Jul 27 '17 at 00:01
  • Normally, `axiom dev_valid: \valid(dev)` should imply both `somereg_valid` and `someotherreg_valid`, at least as long as you can afford to represent your physical layout in a single `struct`. If you have different physical bases, you'll need one axiom for each of them. Feel free to open a new question if you have issues with this representation. – Virgile Aug 02 '17 at 15:01
0

Not that I know (I've tried a lot too).

The only workaround I found is:

struct mydevice MY_DEVICE_STRUCT;
volatile struct mydevice * const dev = & MY_DEVICE_STRUCT;

You need to "instantiate" the memory because this is what the underlying LLVM expects. For sure it would be nice to force this through an ACSL notation.

sdive
  • 2,047
  • 1
  • 20
  • 20
  • The problem is that I need to map the device registers to a specific physical address. I guess I can try some linker tricks and see if that works. – Yifan Jul 21 '17 at 15:48