As I am having my first steps in C formal verification with Frama-C, I am trying to formally verify an integer binary logarithm function written as follows:
//@ logic integer pow2(integer n) = (n == 0)? 1 : 2 * pow2(n - 1);
/*@
requires n > 0;
assigns \nothing;
ensures pow2(\result) <= \old(n) < pow2(\result + 1);
*/
unsigned int log2(size_t n)
{
unsigned int res = 0;
while (n > 1) {
n /= 2;
++res;
}
return res;
}
I am using Frama-C 20.0 (Calcium), with the command frama-c-gui -rte -wp file.c
(I do not have the Jessie plugin for some reason). I have checked the post-condition to hold for up to n = 100,000,000 (using standard-library asserts), but this function fails to verify formally despite my best efforts, and Frama-C tutorials typically involve trivial loop variants that decrement (instead of halving) every iteration, thus not that close to what I am trying to do.
I have tried the following code annotations, some of which are likely unnecessary:
//@ logic integer pow2(integer n) = (n == 0)? 1 : 2 * pow2(n - 1);
/*@
requires n > 0;
assigns \nothing;
ensures pow2(\result) <= \old(n) < pow2(\result + 1);
*/
unsigned int log2(size_t n)
{
unsigned int res = 0;
/*@
loop invariant 0 < n <= \at(n, Pre);
loop invariant \at(n, Pre) < n * pow2(res + 1);
loop invariant pow2(res) <= \at(n, Pre);
loop invariant res > 0 ==> 2 * n <= \at(n, Pre);
loop invariant n > 1 ==> pow2(res + 1) <= \at(n, Pre);
loop invariant res <= pow2(res);
loop assigns n, res;
loop variant n;
*/
while (n > 1) {
L:
n /= 2;
//@ assert 2 * n <= \at(n, L);
++res;
//@ assert res == \at(res, L) + 1;
}
//@ assert n == 1;
return res;
}
The annotations that fail to verify are loop invariants 2 and 5 (both Alt-Ergo 2.3.0 and Z3 4.8.7 timeout). As far as invariant 2 is concerned, the difficulty appears to be linked to integer division, but I am not sure what to add to make WP able to prove it. As for invariant 5, WP can prove that it’s established, but not that it’s preserved. It may require a property able to capture what happens when n becomes 1, but I am not sure what could work.
How may I specify the missing information to verify these loop invariants, and is there another Frama-C analysis that could allow me to find loop invariants more easily?
Thank you for your consideration.