在下面的程序中,函数dec
使用scanf
来读取用户的任意输入。
dec
调用 main
并根据输入返回1或0,因此将执行操作。但是,值分析表明y
始终为0
,即使在调用scanf
之后也是如此。那是为什么?
答案 0 :(得分:2)
注意:以下评论适用于Frama-C 15之前的版本(Phosphorus,20170501);在Frama-C 15中,Variadic插件默认启用(其短名称现为-variadic
)。
-va
)之前启用Variadic(-val
),它将消除警告,程序将按预期运行。严格来说,Frama-C本身(内核)只进行解析;它取决于插件本身(例如Value / EVA)来评估程序。
根据您的描述,我相信您必须使用Value / EVA来分析程序。我不确切知道您使用的是哪个版本,因此我将描述Frama-C Silicon的行为。
ACSL(Frama-C使用的规范语言)的一个限制是,目前无法为scanf
等可变函数指定合约。因此,Frama-C标准库附带的规格不足。您可以在以下程序中注意到这一点:
#include <stdio.h>
int d;
int main() {
scanf("%d", &d);
Frama_C_show_each(d);
return 0;
}
运行frama-c -val file.c
将输出以下内容:
...
[value] using specification for function scanf
FRAMAC_SHARE/libc/stdio.h:150:[value] warning: no \from part for clause 'assigns *__fc_stdin;' of function scanf
[value] Done for function scanf
[value] Called Frama_C_show_each({0})
...
该警告意味着规范不正确,这解释了奇怪的行为。
在这种情况下的解决方案是使用Variadic插件(-va
或-va-help
获取更多详细信息),这将专门调用可变参数调用并为其添加规范,从而避免警告和表现如预期。这是在上面的示例中运行Variadic插件后生成的代码(-print
):
$ frama-c -va file.c -print
[... lots of definitions from stdio.h ...]
/*@ requires valid_read_string(format);
requires \valid(param0);
ensures \initialized(param0);
assigns \result, *__fc_stdin, *param0;
assigns \result
\from (indirect: *__fc_stdin), (indirect: *(format + (0 ..)));
assigns *__fc_stdin
\from (indirect: *__fc_stdin), (indirect: *(format + (0 ..)));
assigns *param0
\from (indirect: *__fc_stdin), (indirect: *(format + (0 ..)));
*/
int scanf_0(char const *format, int *param0);
int main(void)
{
int __retres;
scanf_0("%d",& d);
Frama_C_show_each(d);
__retres = 0;
return __retres;
}
在此示例中,scanf
专门用于scanf_0
,具有适当的ACSL注释。在此程序上运行EVA不会发出任何警告并产生预期的输出:
@ frama-c -va file.c -val
...
[value] Done for function scanf_0
[value] Called Frama_C_show_each([-2147483648..2147483647])
...
注意:Frama-C 14(Silicon)中的GUI不允许启用Variadic插件(即使在Analyses面板中勾选它后),所以必须在这种情况下使用命令行来获取预期结果并避免警告。从Frama-C 15(Phosphorus,将于2017年发布)开始,这不是必需的:默认情况下将启用Variadic,因此您的示例将从一开始就有效。