簡體   English   中英

如何證明 C 字符串的功能比較 function 與 Frama-C?

[英]How to prove the functionality of a C stringCompare function with Frama-C?

我想用 Frama-c 和 WP 插件表示,下面寫的 stringCompare function 充當“它應該” - 即:給定相同的輸入字符串,function 返回 0 並且結果不同於 0如果字符串不相同。 我已經注釋了如下所示的相關功能,並希望能夠證明 WP 產生的未經證實的目標,如何做到這一點?

output 我嘗試按原樣運行帶有注釋的插件,可以在下面的代碼中看到

#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 插件生成的 output:

[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)

這里有很多點。 首先,我想說的是,在遇到驗證問題時,可能不會首先嘗試使用 memory 模型(特別是,由於您沒有使用浮點運算,所以+real組件完全沒用)。 因此,我建議從等式中完全刪除-wp-model ,默認選擇通常足夠好。 另一方面,添加-wp-rte來檢查潛在的運行時錯誤可能會很有趣。

當您指定\valid_read(s1)時,您是說您可以訪問s1[0] ,但沒有別的。 如果您想談論幾個 memory 單元格的有效性,您可以使用\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)
  • s1的當前值實際上是\at(s1,Pre)+i (同上s2
  • 到目前為止, s1s2的所有元素都是相等的......
  • ...和非空

由於 Frama-C 所針對的默認架構使用 char 作為有符號,在return語句中轉換為unsigned char會使 WP 感到困惑。 這無疑是 WP 本身的一個弱點。

對於stringLength ,還需要像valid_string(str)這樣的東西。 但是,這一次您必須將strlen(str)限制為嚴格小於SIZE_MAX (假設您將返回類型和result聲明更改為size_t ),因為result必須 go 達到strlen(str)+1而不會溢出。

同樣,必須加強循環不變量: resultstrlen(str)為界,並且我們必須指出到目前為止所有字符都是非 0。

最后,在您的main function 中,WP 的另一個弱點阻止檢查 stringCompare 的先決條件是否得到滿足。 如果hellohelli被明確定義為 char arrays,那么一切都會好起來的。

tl; dr:下面的代碼可以完全用frama-c -wp -wp-rte file.c (Frama-C 22.0 Titanium 和 Alt-Ergo 2.2.0)

#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;
} 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM