frama-c停止传播:“断言获得状态无效”

时间:2016-07-16 18:49:43

标签: c frama-c program-slicing

我想为所有断言切片文件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);时,为什么它会在这里停止传播?为什么它会起作用?

1 个答案:

答案 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在其抽象执行期间遇到的注释的有效性状态。更确切地说,每次抽象执行到达注释时,状态计算如下:

  • 如果注释对于当前抽象状态所代表的所有具体状态都为真,则会发出有效状态。
  • 如果所有此类具体状态的注释都为false,则会发出无效状态,并且此分支的抽象执行将停止(因为没有具体执行将超过注释)。
  • 否则状态未知:值无法知道注释评估为false的具体状态是否真的在实践中发生,或者只是到目前为止所做的近似的人工制品。但是,它会尝试减少其抽象状态,使其尽可能少地表示无效状态(例如,如果已知colors->colors/*@ assert 0<= x <= 10;*/位于x区间,则值将为断言后[3; 17]的间隔使用[3; 10]