Frama-c:函数调用和静态变量

Frama-c:函数调用和静态变量,frama-c,acsl,Frama C,Acsl,我目前正在发现frama-c的功能,尤其是WP&Value的分析工具。我的最终目标是能够在涉及以下几层的较大代码上使用frama-c: 很多函数调用 复杂数据结构的使用 静态和/或全局变量 到目前为止,我一直在尝试应用自底向上的方法,即开始指定不包含任何函数调用的函数,并通过使用-lib entry和-main内核选项隔离它们来分析它们的行为。通过这样做,我确保如果假设前提条件为真,那么整个功能契约就得到了验证。当我试图指定调用这些函数的上层时,事情就变得复杂了。首先,我经常必须指定被调用函



我目前正在发现frama-c的功能,尤其是WP&Value的分析工具。我的最终目标是能够在涉及以下几层的较大代码上使用frama-c:

  • 很多函数调用
  • 复杂数据结构的使用
  • 静态和/或全局变量
到目前为止,我一直在尝试应用自底向上的方法,即开始指定不包含任何函数调用的函数,并通过使用-lib entry和-main内核选项隔离它们来分析它们的行为。通过这样做,我确保如果假设前提条件为真,那么整个功能契约就得到了验证。当我试图指定调用这些函数的上层时,事情就变得复杂了。首先,我经常必须指定被调用函数的行为,这并不总是容易的,因为这些函数可能处理当前函数范围之外的变量/函数

让我给你一个简单的例子:

假设在file1.h中,我们定义了一个数据结构“my_struct”,其中包含一个字段号和一个字段奇偶校验

file1.c中,我有两个功能:

  • 第一个函数“check_parity”,只测试静态变量_sVar的奇偶校验字段是否正确
  • 第二个函数“correct_parity”调用第一个函数,并在字段不正确时更正奇偶校验
file2.c中,我有一个函数“outside\u caller”,它只调用correct\u parity()。我的目标是能够以指定正确的\u奇偶校验的方式指定外部\u调用方。下面是相应的源代码:

file1.h

/* parity = 0 => even ; 1 => odd */

typedef unsigned char TYP_U08;
typedef unsigned short TYP_U16;
typedef unsigned int TYP_U32;
typedef unsigned long TYP_U64;

typedef struct {
    unsigned char parity;
    unsigned int number;
} my_stuct;

typedef enum
{
    S_ERROR        =  -1
    ,S_OK          =  0
    ,S_WARNING     =  1
} TYPE_STATUS;

/*@ ghost my_stuct* g_sVar; */

/*@ predicate fc_pre_is_parity_ok{Labl}(my_stuct* i_sVar) =
        (
      \at(i_sVar->parity, Labl) == ((TYP_U08) (\at(i_sVar->number,Labl) % 2u))
    );

  @ predicate fc_pre_valid_parity{Labl}(my_stuct* i_sVar) = 
    (
        (\at(i_sVar->parity,Labl) == 0) ||
        (\at(i_sVar->parity, Labl) == 1)
    );

  @ predicate fc_pre_is_parity_readable(my_stuct* i_sVar) =
    (
        \valid_read(&i_sVar->parity)
    );

  @ predicate fc_pre_is_parity_writeable(my_stuct* i_sVar) =
    (
        \valid(&i_sVar->parity)
    );

  @ predicate fc_pre_is_number_readable(my_stuct* i_sVar) =
    (
        \valid_read(&i_sVar->number)
    );

  @ predicate fc_pre_is_number_writeable(my_stuct* i_sVar) =
    (
        \valid(&i_sVar->number)
    );
*/

TYPE_STATUS check_parity(void);
TYPE_STATUS correct_parity(void);
file1.c

static my_stuct* _sVar;

  /*@ requires check_req_parity_readable:
    fc_pre_is_parity_readable(_sVar);

    @ requires check_req_number_readable:
      fc_pre_is_number_readable(_sVar);

    @ assigns check_assigns:
      g_sVar;

    @ ensures check_ensures_error:
      !fc_pre_valid_parity{Post}(g_sVar) ==> \result == S_ERROR;

    @ ensures check_ensures_ok:
      (
        fc_pre_valid_parity{Post}(g_sVar) &&
        fc_pre_is_parity_ok{Post}(g_sVar) 
      ) ==> \result == S_OK;

    @ ensures check_ensures_warning:
      (
        fc_pre_valid_parity{Post}(g_sVar) &&
        !fc_pre_is_parity_ok{Post}(g_sVar)
      ) ==> \result == S_WARNING;

    @ ensures check_ensures_ghost_consistency:
      \at(g_sVar, Post) == _sVar;
  */
TYPE_STATUS check_parity(void)
{
    //@ ghost g_sVar = _sVar;
    TYPE_STATUS status = S_OK;
    if(!(_sVar->parity == 0 || _sVar->parity == 1)) {
        status = S_ERROR;
    } else if ( _sVar->parity == (TYP_U08)(_sVar->number % 2u) ){
        status = S_OK;
    } else {
        status = S_WARNING;
    }
  return status;
}


  /*@ requires correct_req_is_parity_writeable:
    fc_pre_is_parity_writeable(_sVar);

    @ requires correct_req_is_number_readable:
    fc_pre_is_number_readable(_sVar);

    @ assigns correct_assigns:
    _sVar->parity,
    g_sVar,
    g_sVar->parity;

    @ ensures correct_ensures_error :
    !fc_pre_valid_parity{Pre}(g_sVar) ==> \result == S_ERROR;

    @ ensures correct_ensures_ok :
    (
      fc_pre_valid_parity{Pre}(g_sVar) &&
      fc_pre_is_parity_ok{Pre}(g_sVar)
    ) ==> \result == S_OK;

    @ ensures correct_ensures_warning :
    (
      fc_pre_valid_parity{Pre}(g_sVar) &&
      !fc_pre_is_parity_ok{Pre}(g_sVar)
    ) ==> \result == S_WARNING;

    @ ensures correct_ensures_consistency :
      fc_pre_is_parity_ok{Post}(g_sVar);

    @ ensures correct_ensures_validity :
      fc_pre_valid_parity{Post}(g_sVar);

    @ ensures correct_ensures_ghost_consistency :
      \at(g_sVar, Post) == _sVar;
  */
TYPE_STATUS correct_parity(void)
{
    //@ ghost g_sVar = _sVar;
    TYPE_STATUS parity_status = check_parity();

    if(parity_status == S_ERROR || parity_status == S_WARNING) {
        _sVar->parity = (TYP_U08)(_sVar->number % 2u);
        /*@ assert (\at(g_sVar->parity,Here) == 0) ||
               (\at(g_sVar->parity, Here) == 1);
     */
        //@ assert \at(g_sVar->parity, Here) == (TYP_U08)(\at(g_sVar->number,Here) % 2u);
    }
    return parity_status;
}
/*@ requires out_req_parity_writable:
    fc_pre_is_parity_writeable(g_sVar);

  @ requires out_req_number_writeable:
    fc_pre_is_number_readable(g_sVar);

  @ assigns out_assigns:
    g_sVar,
    g_sVar->parity;

  @ ensures out_ensures_error:
    !fc_pre_valid_parity{Pre}(g_sVar) ==> \result == S_ERROR;

  @ ensures out_ensures_ok :
    (
      fc_pre_valid_parity{Pre}(g_sVar) &&
      fc_pre_is_parity_ok{Pre}(g_sVar)
    ) ==> \result == S_OK;

  @ ensures out_ensures_warning :
    (
      fc_pre_valid_parity{Pre}(g_sVar) &&
      !fc_pre_is_parity_ok{Pre}(g_sVar)
    ) ==> \result == S_WARNING;

  @ ensures out_ensures_consistency:
    fc_pre_is_parity_ok{Post}(g_sVar);

  @ ensures out_ensures_validity:
    fc_pre_valid_parity{Post}(g_sVar);
*/

TYPE_STATUS outside_caller(void)
{
    TYPE_STATUS status = correct_parity();
    //@ assert fc_pre_is_parity_ok{Here}(g_sVar) ==> status == S_OK;
    /*@ assert !fc_pre_is_parity_ok{Here}(g_sVar) && 
               fc_pre_valid_parity{Here}(g_sVar) ==> status == S_WARNING; */
    //@ assert !fc_pre_valid_parity{Here}(g_sVar) ==> status == S_ERROR;
    return status;
}
file2.c

static my_stuct* _sVar;

  /*@ requires check_req_parity_readable:
    fc_pre_is_parity_readable(_sVar);

    @ requires check_req_number_readable:
      fc_pre_is_number_readable(_sVar);

    @ assigns check_assigns:
      g_sVar;

    @ ensures check_ensures_error:
      !fc_pre_valid_parity{Post}(g_sVar) ==> \result == S_ERROR;

    @ ensures check_ensures_ok:
      (
        fc_pre_valid_parity{Post}(g_sVar) &&
        fc_pre_is_parity_ok{Post}(g_sVar) 
      ) ==> \result == S_OK;

    @ ensures check_ensures_warning:
      (
        fc_pre_valid_parity{Post}(g_sVar) &&
        !fc_pre_is_parity_ok{Post}(g_sVar)
      ) ==> \result == S_WARNING;

    @ ensures check_ensures_ghost_consistency:
      \at(g_sVar, Post) == _sVar;
  */
TYPE_STATUS check_parity(void)
{
    //@ ghost g_sVar = _sVar;
    TYPE_STATUS status = S_OK;
    if(!(_sVar->parity == 0 || _sVar->parity == 1)) {
        status = S_ERROR;
    } else if ( _sVar->parity == (TYP_U08)(_sVar->number % 2u) ){
        status = S_OK;
    } else {
        status = S_WARNING;
    }
  return status;
}


  /*@ requires correct_req_is_parity_writeable:
    fc_pre_is_parity_writeable(_sVar);

    @ requires correct_req_is_number_readable:
    fc_pre_is_number_readable(_sVar);

    @ assigns correct_assigns:
    _sVar->parity,
    g_sVar,
    g_sVar->parity;

    @ ensures correct_ensures_error :
    !fc_pre_valid_parity{Pre}(g_sVar) ==> \result == S_ERROR;

    @ ensures correct_ensures_ok :
    (
      fc_pre_valid_parity{Pre}(g_sVar) &&
      fc_pre_is_parity_ok{Pre}(g_sVar)
    ) ==> \result == S_OK;

    @ ensures correct_ensures_warning :
    (
      fc_pre_valid_parity{Pre}(g_sVar) &&
      !fc_pre_is_parity_ok{Pre}(g_sVar)
    ) ==> \result == S_WARNING;

    @ ensures correct_ensures_consistency :
      fc_pre_is_parity_ok{Post}(g_sVar);

    @ ensures correct_ensures_validity :
      fc_pre_valid_parity{Post}(g_sVar);

    @ ensures correct_ensures_ghost_consistency :
      \at(g_sVar, Post) == _sVar;
  */
TYPE_STATUS correct_parity(void)
{
    //@ ghost g_sVar = _sVar;
    TYPE_STATUS parity_status = check_parity();

    if(parity_status == S_ERROR || parity_status == S_WARNING) {
        _sVar->parity = (TYP_U08)(_sVar->number % 2u);
        /*@ assert (\at(g_sVar->parity,Here) == 0) ||
               (\at(g_sVar->parity, Here) == 1);
     */
        //@ assert \at(g_sVar->parity, Here) == (TYP_U08)(\at(g_sVar->number,Here) % 2u);
    }
    return parity_status;
}
/*@ requires out_req_parity_writable:
    fc_pre_is_parity_writeable(g_sVar);

  @ requires out_req_number_writeable:
    fc_pre_is_number_readable(g_sVar);

  @ assigns out_assigns:
    g_sVar,
    g_sVar->parity;

  @ ensures out_ensures_error:
    !fc_pre_valid_parity{Pre}(g_sVar) ==> \result == S_ERROR;

  @ ensures out_ensures_ok :
    (
      fc_pre_valid_parity{Pre}(g_sVar) &&
      fc_pre_is_parity_ok{Pre}(g_sVar)
    ) ==> \result == S_OK;

  @ ensures out_ensures_warning :
    (
      fc_pre_valid_parity{Pre}(g_sVar) &&
      !fc_pre_is_parity_ok{Pre}(g_sVar)
    ) ==> \result == S_WARNING;

  @ ensures out_ensures_consistency:
    fc_pre_is_parity_ok{Post}(g_sVar);

  @ ensures out_ensures_validity:
    fc_pre_valid_parity{Post}(g_sVar);
*/

TYPE_STATUS outside_caller(void)
{
    TYPE_STATUS status = correct_parity();
    //@ assert fc_pre_is_parity_ok{Here}(g_sVar) ==> status == S_OK;
    /*@ assert !fc_pre_is_parity_ok{Here}(g_sVar) && 
               fc_pre_valid_parity{Here}(g_sVar) ==> status == S_WARNING; */
    //@ assert !fc_pre_valid_parity{Here}(g_sVar) ==> status == S_ERROR;
    return status;
}
这里的主要问题是,为了指定outside_caller(),我需要访问file2.c中超出范围的_sVar。这意味着要处理一个ghost变量(g_sVar),该变量在file1.h中声明,并在正确的_奇偶校验函数中更新。为了使调用者(正确的_奇偶校验)能够使用被调用者的契约,必须在被调用者的契约中使用ghost变量g_sVar

以下是WP分析的结果:

(1)检查奇偶校验()

frama-c-wp-src/main.csrc/test.c-cpp命令'gcc-c-E-Isrc/' -主“检查奇偶校验”-库条目-wp超时1-wp fct检查奇偶校验-wp rte-wp fct检查奇偶校验-然后-报告
[rte]注释功能检查\u奇偶校验
[wp]计划的14个目标
[wp]已证实的目标:14/14
Qed:9(4ms)
Alt Ergo:5(8ms-12ms-20ms)(30)

(2)纠正奇偶校验()

frama-c-wp src/main.c src/test.c-cpp命令'gcc-c-E-Isrc/'-main'correct_parity'-lib entry-wp timeout 1-wp fct correct_parity-wp rte-wp fct correct_parity-then-报告
[rte]注释函数正确性\u奇偶校验 [wp]18个预定目标
[wp]证明目标:18/18
Qed:12(4ms)
Alt Ergo:6(4ms-37ms-120ms)(108)

(3)外部调用方()

frama-c-wp src/main.c src/test.c-cpp命令'gcc-c-E-Isrc/'-main'outside_caller'-lib entry-wp timeout 1-wp fct outside_caller-wp rte-wp fct outside_caller-then-report
[rte]在调用方外部注释函数
[wp]14个预定目标
[wp][Alt Ergo]目标类型的\外部\呼叫方\分配\出口:未知(Qed:4ms)(515ms)
[wp][Alt Ergo]目标类型\外部\呼叫方\呼叫方\正确\奇偶校验\预\正确\请求\为\标准\未知(636ms)
[wp][Alt Ergo]目标类型在调用方之外断言:超时
[wp][Alt Ergo]目标类型\u外部\u呼叫方\u分配\u正常\u第1部分:超时
[wp][Alt Ergo]目标类型\外部\呼叫方\呼叫方\正确\奇偶校验\预\正确\请求\为\数量\未知(205ms)
[wp]证明目标:9/14
Qed:9(4ms)
Alt Ergo:0(中断:2)(未知:3)

==>

在此配置中,被调用方使用g_sVar ghost变量指定,但requires和assings子句除外,原因有二:

  • 我需要用\valid&\valid\u read检查\u sVar R/W访问,因为它是指针
  • 当我试图用g_sVar指定被调用方的assigns子句时,我无法验证相应的子句
但通过这样做,我不知何故使调用方的规范无效,因为您可以在WP的输出上使用se

为什么我调用的函数越多,证明函数行为就越复杂?是否有适当的方法来处理多个函数调用和静态变量

提前非常感谢


PS:我正在使用MGAZH-20151002版本,在一台运行Ubuntu 14.04、64位机器的虚拟机上工作。我知道开始使用WhyML和Why3会对我有很大帮助,但到目前为止,我还无法在windows和Ubuntu上安装Why3 ide,遵循教程的每个步骤。首先,请注意,
-main
-lib entry
对WP没有多大用处(您提到您也对EVA/价值分析感兴趣,但您的问题是针对WP的)

静态变量的问题是已知的,处理它的最简单方法确实是在标题中声明一个ghost变量。但是,您必须用ghost变量而不是
静态
变量来表示契约。 否则,调用者将无法使用这些合同,因为他们对
\u sVar
一无所知。根据经验,最好将合同放在标题中:这样,您只能使用在翻译单元之外可见的标识符

关于函数调用,主要的一点是