我想为所有断言切片文件test.c
。
test.c
如下所示:
#include <stdlib.h>
typedef struct {
float r;
float g;
float b;
} Color;
typedef struct {
int k;
Color* colors;
} Colors;
void foo(int* a, int k, Colors *colors)
{
int b=0;
colors->colors = (Color*) malloc(k*sizeof(Color));
//@ assert (colors == NULL);
if (colors==NULL)
return;
colors->k = k;
int c=10;
*a=8;
//@ assert(*a>b);
}
我使用以下命令运行frama-c:
frama-c -slice-assert @all -main foo test.c -then-on 'Slicing export' -print -ocode slice.c
结果slice.c
如下所示:
/* Generated by Frama-C */
typedef unsigned int size_t;
struct __anonstruct_Color_1 {
float r ;
float g ;
float b ;
};
typedef struct __anonstruct_Color_1 Color;
struct __anonstruct_Colors_2 {
int k ;
Color *colors ;
};
typedef struct __anonstruct_Colors_2 Colors;
/*@ ghost extern int __fc_heap_status __attribute__((__FRAMA_C_MODEL__)); */
/*@
axiomatic dynamic_allocation {
predicate is_allocable{L}(size_t n)
reads __fc_heap_status;
}
*/
void foo(int *a, Colors *colors)
{
int b;
/*@ assert colors ≡ (Colors *)((void *)0); */ ;
return;
}
查看切片函数foo
,似乎处理在某种程度上是不完整的。 frama-c输出告诉我:
test.c:19:[kernel] warning: out of bounds write. assert \valid(&colors->colors);
test.c:20:[value] Assertion got status invalid (stopping propagation).
什么是“状态无效”应该是什么意思?当我将第一个断言更改为//@ assert (colors != NULL);
时,为什么它会在这里停止传播?为什么它会起作用?
答案 0 :(得分:3)
Slicing插件依赖于Value Analysis计算的信息。在运行期间,Value Analysis尝试评估它探索的分支上存在的任何ACSL注释。对于您的示例中的assert colors == NULL;
尤其如此,这确实被视为无效。
为什么?首先,我们可以注意到colors
是主要切入点的正式论证。默认情况下,Value Analysis将创建一个初始状态,其中此指针为NULL
或指向两个元素块的指针(有关分析的默认初始状态以及如何更多信息,请参阅Value's user manual它可以根据需要定制)。因此,似乎断言只会消除第二种可能性并将NULL
作为colors
的唯一可能性。
但是,在到达colors
之前,该函数还有另一件事assert
:它在colors->colors
中分配了一些内容。要使此分配有效,colors
需要指向有效的内存位置。因此,您在第19行看到的警告(由于历史原因看似由内核发出,但实际上是由Value发出),通过生成另一个断言\valid(&colors->colors)
来实现,如果你启动frama-c-gui
就可以看到你的选择。
在发出警报后,Value会尝试根据它来减少其内部状态,因为如果验证给定条件,则具体执行只能进一步(不在未定义行为中冒险)。在我们的示例中,这意味着删除NULL
的{{1}}可能值集。因此,当我们遇到断言时,我们只有colors
的一种可能性,并且由于它与断言不匹配,状态无效并且传播被停止:没有具体的执行可以达到这一点并验证断言。
更新如果您将第一个断言更改为colors
,则值分析会发现它是有效的,因为如上所述,到达评估断言的点只能完成由于前一条指令中的//@ assert (colors != NULL);
,指针有效colors
。因此,值继续进行分析,切片在正常终止的程序上完成,从而产生预期的结果。
关于您的注释,如果在通过注释的程序的任何具体执行期间,其评估为true,则其无效(并且如果注释评估为false则执行应该停止),则ACSL注释是有效的。在实践中,通常不可能(至少在合理的时间和/或记忆中没有)执行所有这样的评估,因此未知状态,意味着插件不能决定。请注意,在任何情况下,给定插件发出的状态取决于此插件的配置。例如,对于Value,所选入口点和初始配置会影响Value在其抽象执行期间遇到的注释的有效性状态。更确切地说,每次抽象执行到达注释时,状态计算如下:
colors->colors
和/*@ assert 0<= x <= 10;*/
位于x
区间,则值将为断言后[3; 17]
的间隔使用[3; 10]
。