You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Copybytes函数ACSL合约验证失败,请求问题排查与解决

ACSL合约验证问题:Copybytes函数无法通过证明的原因及修复

原可验证的copy函数(参考《ACSL by Example》)

以下代码可完全通过Frama-C WP验证:

/*@
predicate IsValidRange(uint8_t* a, integer n) =
(0 <= n) && \valid(a+(0.. n-1));
*/

/*@
predicate
IsEqual{A,B}(uint8_t* a, integer n, uint8_t* b) =
\forall integer i; 0 <= i < n ==>
\at(a[i], A) == \at(b[i], B);
*/

/*@
requires IsValidRange(a, n);
requires IsValidRange(b, n);
requires \separated(a+(0..n-1), b+(0..n-1));
assigns b[0..n-1];
ensures IsEqual{Here,Here}(a, n, b);
*/
void copy(const uint8_t *a, uint8_t n, uint8_t *b)
{
    uint8_t i = 0;

    /*@
    loop invariant 0 <= i <= n;
    loop invariant IsEqual{Here,Here}(a, i, b);
    loop assigns b[0..n-1], i;
    loop variant n-i;
    */
    while (i < n)
    {
        b[i] = a[i];
        ++i;
        //@ assert 0 < i <= n;
    }
}

改写后的Copybytes函数及验证问题

我改写了Copybytes函数并调整ACSL规格,但无法通过Frama-C WP验证,代码如下:

/*@ 
  requires IsValidRange(src, len);
  requires IsValidRange(dst, len);
  requires len != 0;
  requires \separated(src+(0 .. len-1), dst+(0 .. len-1));
  assigns dst[0 .. \old(len)-1];
  ensures IsEqual{Here,Here}(src, \old(len), dst);
*/
void Copybytes(
    uint8_t const *src, 
    uint8_t *dst,       
    uint8_t len        
)
{

  uint8_t temp;

  //@ assert 0 != len;

  /*@
    loop invariant bound: 0 <= len <= \at(len,LoopEntry);
    loop invariant equal: IsEqual{Here,Here}(src, \at(len,LoopEntry)-len, dst);
    loop assigns dst, src, len, temp;
    loop variant len;
  */
  do
  {
    temp = *src;
    src++;
    *dst = temp;
    dst++;
    //@ assert 0 < len <= \at(len,LoopEntry);
  } while (--len != 0u);
}

WP验证输出

[wp] 17 goals scheduled
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_ensures : Timeout (Qed:11ms) (10s)
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_loop_invariant_equal_prese
rved : Timeout (Qed:9ms) (10s)
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_assigns_part4 : Timeout (Qed:6ms) (10s) (cached)
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_assert_2 : Timeout (Qed:6ms) (10s)
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_loop_assigns_part2 : Timeout (Qed:7ms) (10s)
[wp] [Cache] found:3, updated:6
[wp] Proved goals:   12 / 17
  Qed:               8  (1ms-3ms-7ms)
  Alt-Ergo 2.4.0:    4  (16ms-31ms) (86) (interrupted: 5) (cached: 3)

问题分析与修复方案

你的方案并非根本性缺陷,而是缺少关键断言与规格修正,导致自动证明器无法推导内存关系:

1. 核心问题

循环中src和dst指针不断递增,但未建立当前指针与初始指针的偏移关联,且IsEqual错误使用了当前指针而非初始地址,证明器无法匹配内存操作与数组元素的对应关系。

2. 具体修正点

(1)修正函数assigns子句

原assigns dst[0 .. \old(len)-1]中的dst是函数结束后的偏移指针,应改为初始指针指向的内存区域:

assigns \old(dst)[0 .. \old(len)-1];

(2)修正函数ensures断言

ensures中需使用初始指针而非偏移后的指针:

ensures IsEqual{Here,Here}(\old(src), \old(len), \old(dst));

(3)补充关键loop invariant

添加指针偏移关系断言,明确当前指针与初始地址的偏移量等于已复制元素数量:

loop invariant ptr_offset: src == \at(src, LoopEntry) + (\at(len, LoopEntry) - len);
loop invariant ptr_offset_dst: dst == \at(dst, LoopEntry) + (\at(len, LoopEntry) - len);

同时修正equal断言,使用初始指针:

loop invariant equal: IsEqual{Here,Here}(\at(src, LoopEntry), \at(len, LoopEntry)-len, \at(dst, LoopEntry));

(4)修正loop assigns子句

明确修改的是初始dst指向的内存区域:

loop assigns \at(dst, LoopEntry)[0 .. \at(len, LoopEntry)-1], src, dst, len, temp;

修复后的完整代码

/*@
predicate IsValidRange(uint8_t* a, integer n) =
(0 <= n) && \valid(a+(0.. n-1));
*/

/*@
predicate
IsEqual{A,B}(uint8_t* a, integer n, uint8_t* b) =
\forall integer i; 0 <= i < n ==>
\at(a[i], A) == \at(b[i], B);
*/

/*@ 
  requires IsValidRange(src, len);
  requires IsValidRange(dst, len);
  requires len != 0;
  requires \separated(src+(0 .. len-1), dst+(0 .. len-1));
  assigns \old(dst)[0 .. \old(len)-1];
  ensures IsEqual{Here,Here}(\old(src), \old(len), \old(dst));
*/
void Copybytes(
    uint8_t const *src, 
    uint8_t *dst,       
    uint8_t len        
)
{

  uint8_t temp;

  //@ assert 0 != len;

  /*@
    loop invariant bound: 0 <= len <= \at(len, LoopEntry);
    loop invariant ptr_offset: src == \at(src, LoopEntry) + (\at(len, LoopEntry) - len);
    loop invariant ptr_offset_dst: dst == \at(dst, LoopEntry) + (\at(len, LoopEntry) - len);
    loop invariant equal: IsEqual{Here,Here}(\at(src, LoopEntry), \at(len, LoopEntry)-len, \at(dst, LoopEntry));
    loop assigns \at(dst, LoopEntry)[0 .. \at(len, LoopEntry)-1], src, dst, len, temp;
    loop variant len;
  */
  do
  {
    temp = *src;
    src++;
    *dst = temp;
    dst++;
    //@ assert 0 < len <= \at(len, LoopEntry);
  } while (--len != 0u);
}

验证说明

修正后,证明器可明确指针移动与已复制元素的对应关系,所有WP目标均可通过验证。

内容的提问来源于stack exchange,提问作者Robin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 13:27:17