首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何用Frama证明C stringCompare函数的功能?

如何用Frama证明C stringCompare函数的功能?
EN

Stack Overflow用户
提问于 2020-12-09 08:39:26
回答 1查看 197关注 0票数 6

我想用Frama-c和WP插件来表示,下面编写的stringCompare函数充当“它应该”的作用--即:给定相同的输入字符串,函数返回0,如果字符串不相同则返回与0不同的结果。我已经注释了如下所示的相关功能,并希望能够证明工作方案产生的未经证实的目标,这是如何做到的?

在下面的代码中可以看到我试图按原样运行带有注释的插件的输出。

代码语言:javascript
复制
#include <string.h>
#include <stdio.h>

/*@  
    requires validPointers: \valid_read(s1) && \valid_read(s2) ;
    requires validLengthS1: 100 >= strlen(s1) >= 0;
    requires validLengthS2: 100 >= strlen(s2) >= 0;
    assigns \nothing ;
    allocates \nothing ;  
    frees \nothing ;
    behavior allEqual:
        assumes \forall integer k; 0 <= k < n ==> s1[k] == s2[k];
        ensures \result == 0;
    behavior SomeDifferent:
        assumes \exists integer k; 0 <= k < n ==> s1[k] != s2[k];
        ensures \result != 0;
    
    disjoint behaviors;
    complete behaviors;
    */ 
int stringCompare(const char* s1, const char* s2, int n) {
    if (s1 == s2) 
        return 0;
    int i = 0;

    /*@ assert \valid_read(s1) ; */
    /*@ assert \valid_read(s2) ;*/
    /*@ loop invariant 0 <= i; 
        loop assigns i , s1, s2; */    
    while (*s1 == *(s2++))
    {
        /*@ assert i <= 2147483647 ; */
        ++i;
        if (*(s1++) == '\0')
            return 0;
    }
   
    return *(unsigned char*)s1 - *(unsigned char*)(--s2);
}

/*@ assigns \nothing ;
    ensures rightResult: \result == strlen(\old(str));
    ensures rightEndCharacter: str[\result] == '\0' ;  */
int stringLength(const char* str) {
    int result = 0;

    /*@ loop assigns result ;
        loop invariant 0 <= result ; */
    while (str[result++] != '\0');

    return --result;

}

/*@ assigns \nothing ;
    ensures \result == 0 ;*/
int main(void) {

   const char* hello = "hello";
   const char* helli = "helli";

   /*@ assert \valid_read(hello) && \valid_read(helli) ; */
   stringCompare(hello, helli, 5);
   return 0;
} 

WP正在使用以下命令运行:'frama-c -wp -wp-model“Typed+var+int+real -wp-timeout 20 strcmp.c‘

从WP插件生成的输出:

代码语言:javascript
复制
[wp] Warning: Missing RTE guards
[wp] strcmp.c:48: Warning:
  Cast with incompatible pointers types (source: sint8*) (target: uint8*)
[wp] strcmp.c:48: Warning:
  Cast with incompatible pointers types (source: sint8*) (target: uint8*)
[wp] 49 goals scheduled
[wp] [Alt-Ergo 2.2.0] Goal typed_real_main_call_stringCompare_requires_validLengthS1 : Timeout (Qed:2ms) (20s)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_main_call_stringCompare_requires_validLengthS2 : Timeout (Qed:2ms) (20s)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_main_call_stringCompare_2_requires_validLengthS1 : Timeout (Qed:4ms) (20s)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_main_call_stringCompare_2_requires_validLengthS2 : Timeout (Qed:3ms) (20s)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_main_call_stringCompare_3_requires_validLengthS1 : Timeout (Qed:8ms) (20s)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_main_call_stringCompare_3_requires_validLengthS2 : Timeout (Qed:8ms) (20s)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_main_call_stringCompare_4_requires_validLengthS1 : Timeout (Qed:11ms) (20s)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_main_call_stringCompare_4_requires_validLengthS2 : Timeout (Qed:12ms) (20s)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_stringCompare_disjoint_SomeDifferent_allEqual : Timeout (Qed:3ms) (20s)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_stringCompare_allEqual_ensures : Timeout (Qed:15ms) (20s) (Stronger, 2 warnings)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_stringCompare_SomeDifferent_ensures : Timeout (Qed:14ms) (20s) (Stronger, 2 warnings)
[wp] [Alt-Ergo 2.2.0] Goal typed_real_stringLength_ensures_rightResult : Timeout (Qed:5ms) (20s)
[wp] Proved goals:   37 / 49
  Qed:              30  (1ms-3ms-11ms)
  Alt-Ergo 2.2.0:    7  (8ms-43ms-126ms) (464) (interrupted: 12)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-12-09 14:10:44

这里有很多要点。首先,我想说,在遇到验证问题时,使用内存模型可能不是第一件尝试的事情(特别是,由于您没有使用浮点算法,+real组件是完全无用的)。因此,我建议将-wp-model从方程中完全删除,默认的选择通常足够好。另一方面,添加-wp-rte以检查潜在的运行时错误可能很有趣。

当您指定\valid_read(s1)时,您说您可以访问s1[0],但不能访问其他任何东西。如果要讨论几个内存单元格的有效性,可以使用\valid_read(s1 + (0 .. n)),或者,对于以空结尾的C字符串,可以使用\valid_string(s1)

stringCompare的两种行为中假设子句都是不正确的:我们只在strlen(s1) (包括)之前搜索差异,而不是直到n (顺便说一下,这是非常无用的,很可能会被删除:您希望指定strlen(s{1,2})是有界的,但是stdint.h中的SIZE_MAX应该起作用)。此外,\forall i, P(i) ==> Q(i)的反面是\exists i, P(i) && !Q(i) (即在\exists之后不要使用==> )。

最好将size_t用于用作偏移量的C变量。否则,奇怪的事情可能会发生在非常大的字符串。

你缺少一些不变式。基本上,在stringCompare中,您必须捕捉到以下事实:

  • i保持在0strlen(s1)之间。strlen(s2))
  • the当前的s1值实际上是\at(s1,Pre)+i ( s2)
  • so far也是这样,s1s2的所有元素都是equal...
  • ...and非空

)。

由于Frama所针对的默认体系结构使用char作为签名,因此在return语句中转换为return将混淆WP。这是可湿性粉剂本身的一个弱点。

对于stringLength,还需要类似于valid_string(str)的东西。但是,这一次您必须将strlen(str)严格限制为小于SIZE_MAX (假设您将返回类型和result声明更改为size_t),因为result必须在不溢出的情况下上升到strlen(str)+1

同样,必须加强循环不变量:result是由strlen(str)限定的,我们必须指出到目前为止所有的字符都是非0的。

最后,在您的main函数中,WP的另一个缺点是无法检查是否满足了stringCompare的先决条件。如果hellohelli被明确定义为char数组,那么一切都会好的。

tl;dr:下面的代码完全可以用frama-c -wp -wp-rte file.c来证明(Frama-C22.0钛和Alt 2.2.0)

代码语言:javascript
复制
#include <stdint.h>
#include <string.h>
#include <stdio.h>

/*@  
    requires validPointers: valid_read_string(s1) && valid_read_string(s2);
    requires validLengthS1: n >= strlen(s1) >= 0;
    requires validLengthS2: n >= strlen(s2) >= 0;
    assigns \nothing ;
    allocates \nothing ;  
    frees \nothing ;
    behavior allEqual:
        assumes \forall integer k; 0 <= k <= strlen(s1) ==> s1[k] == s2[k];
        ensures \result == 0;
    behavior SomeDifferent:
        assumes \exists integer k; 0 <= k <= strlen(s1) && s1[k] != s2[k];
        ensures \result != 0;
    
    disjoint behaviors;
    complete behaviors;
    */ 
int stringCompare(const char* s1, const char* s2, size_t n) {
    if (s1 == s2) 
        return 0;
    size_t i = 0;

    /*@ assert \valid_read(s1) ; */
    /*@ assert \valid_read(s2) ;*/
    /*@ loop invariant index: 0 <= i <= strlen(\at(s1,Pre));
        loop invariant index_1: 0<= i <= strlen(\at(s2,Pre));
        loop invariant s1_pos: s1 == \at(s1,Pre)+i;
        loop invariant s2_pos: s2 == \at(s2,Pre)+i;
        loop invariant equal: \forall integer j; 0<= j < i ==> \at(s1,Pre)[j] == \at(s2,Pre)[j];
        loop invariant not_eos: \forall integer j; 0<= j < i ==> \at(s1,Pre)[j] != 0;
        loop assigns i , s1, s2; */    
    while (*s1 == *(s2++))
    {
        /*@ assert i <= n ; */
        ++i;
        if (*(s1++) == '\0')
            return 0;
    }
   
    return *(s1) - *(--s2);
}

/*@
  requires valid_string(str);
  requires strlen(str) < SIZE_MAX;
  assigns \nothing ;
  ensures rightResult: \result == strlen(\old(str));
  ensures rightEndCharacter: str[\result] == '\0' ;  */
size_t stringLength(const char* str) {
    size_t result = 0;

    /*@ loop assigns result ;
        loop invariant 0 <= result <= strlen(str);
        loop invariant \forall integer i; 0<= i < result ==> str[i]!=0;
    */
    while (str[result++] != '\0');

    return --result;

}

/*@ assigns \nothing ;
    ensures \result == 0 ;*/
int main(void) {

   const char hello[] = { 'h', 'e', 'l', 'l', 'o', '\0'};
   const char helli[] =  { 'h', 'e', 'l', 'l', 'i', '\0'};

   /*@ assert \valid_read(&hello[0]) && \valid_read(&helli[0]) ; */
   stringCompare(hello, helli, 5);
   return 0;
} 
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65213284

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档