4

I am trying to use Frama-C value analysis to study a large generated C code where the bound checks are done using a bitwise AND (&) instead of a logical AND (&&). For instance:

int t[3];
...
if ((0 <= x) & (x < 3)) 
  t[x] = 0;

Frama-C value analysis complains about the array access :

warning: accessing out of bounds index [-2147483648..2147483647]. assert 0 ≤ x < 3;

I managed to make it happy on small examples by adding assertions before the test:

//@ assert (x < 0 || 0<=x);
//@ assert (x < 3 || 3<=x);

and increasing the slevel but I can't do that in real code (too large !).

Does anybody have an idea of what I can do to remove this alarm ?

(BTW is there any reason to write the tests that way ?)

Anne
  • 1,270
  • 6
  • 15

2 Answers2

4

The patch below will make Value deal uniformly with e1 && e1 and c1 & c2, where c1 and c2 are conditions (but not arbitrary expressions).

Index: src/value/eval_exprs.ml
===================================================================
--- src/value/eval_exprs.ml (révision 21388)
+++ src/value/eval_exprs.ml (copie de travail)
@@ -1748,11 +1748,23 @@
         reduce_by_comparison ~with_alarms reduce_rel
           cond.positive exp1 binop exp2 state

-      | true, BinOp (LAnd, exp1, exp2, _)
-      | false, BinOp (LOr, exp1, exp2, _) ->
+      | true,
+        ( BinOp (LAnd, exp1, exp2, _)
+        | BinOp (BAnd, (* 'cond1 & cond2' can be treated as 'e1 && e2' *)
+                 ({ enode = BinOp ((Le|Ne|Eq|Gt|Lt|Ge), _, _, _)} as exp1),
+                 ({ enode = BinOp ((Le|Ne|Eq|Gt|Lt|Ge), _, _, _)} as exp2),
+                 _))
+      | false,
+        ( BinOp (LOr, exp1, exp2, _)
+        | BinOp (BOr, (* '!(cond1 | cond2)' can be treated as '!(e1 || e2)' *)
+                 ({ enode = BinOp ((Le|Ne|Eq|Gt|Lt|Ge), _, _, _)} as exp1),
+                 ({ enode = BinOp ((Le|Ne|Eq|Gt|Lt|Ge), _, _, _)} as exp2),
+                 _))
+          ->
           let new_state = aux {cond with exp = exp1} state in
           let result = aux {cond with exp = exp2} new_state in
           result
+
       | false, BinOp (LAnd, exp1, exp2, _)
       | true, BinOp (LOr, exp1, exp2, _) ->
           let new_v1 = try aux {cond with exp = exp1} state
byako
  • 3,372
  • 2
  • 21
  • 36
  • Thank you so much ! I'll try that and accept the answer as soon as I manage to check it on my real code. – Anne Feb 07 '13 at 06:49
1

In that example both sides of the & are already 0 or 1 so using & instead of && is ok in that case.

is there any reason to write the tests that way

No, I can't think of any reason they would do that on purpose. In general it is a bad idea because if the code is later changed and one side of the & is no-longer 0-1-valued then the code will break.

Now to the actual problem:

Is the int t[3]; also generated multiple times (eg within {}'s) or just once? If it is defined just once then the solution to your problem is to malloc it: int* t = malloc(3*sizeof(int)). The compiler will no-longer complain.

Bernd Elkemann
  • 23,242
  • 4
  • 37
  • 66
  • Thanks for the explanation, but I don't think that the solution you give is the right one. Frama-C is not a compiler but a static analyzer, and I do want that it checks whether x is between 0 and 3 when accessing to t[x]. – Anne Feb 06 '13 at 16:44
  • The thing is, that Frama does not catch the fact that `((0 <= x) & (x < 3))` is a valid 0<=x<3-check because it is written so badly. You have to either 1) help Frama by changing the code that gets generated into using &&'s (but you say you can't) or 2) circumvent the check for these cases by using malloc. – Bernd Elkemann Feb 06 '13 at 16:52
  • @eznme It is no more obvious that `t[x]` is a valid memory access when `t` points to a malloc-allocated block of size 3 ints than when `t` is an array, so using `malloc` will not circumvent any check. Frama-C will still warn about writing to `t[x]` as long as it isn't able to see that 0 ≤ x < 3. – Pascal Cuoq Feb 06 '13 at 17:30
  • Normally static analyser's dont look at what is in the parenthesis of malloc, strange. Maybe you have to be even more indirect: define a global (maybe even volatile), then assign it the count (3) somewhere else and then call malloc(thatGlobal). That way the static analyzer should no-longer be able to figure out how much memory is available. – Bernd Elkemann Feb 06 '13 at 18:00
  • 2
    @eznme The analyses within Frama-C strive to be correct: if no warning is emitted, your program is guaranteed to be safe. Conversely, they report a warning as soon as there is a doubt on the validity of an operation. It is not possible to make the warning reported by Anne disappear by obfuscating the program; you will only get more of them. – byako Feb 06 '13 at 18:11