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

使用Frama-C验证单链表复制函数失败的原因咨询

单链表递归复制函数的Frama-C验证问题

我正尝试使用Frama-C结合Alt-Ergo求解器验证单链表的递归复制函数copyListRec,但验证后有8个目标未被证明。查阅手册后发现Frama-C对malloc和函数指针的支持似乎有限,我想确认是验证条件存在遗漏,还是Frama-C本身不适合处理链表相关的形式化验证?

验证所用代码

#include <stdlib.h>

typedef struct _list { 
int key;
struct _list *next;
} list; //defined list

/*@
  inductive reachable{L} (list* root, list* node) {
     case root_reachable{L}: \forall list* root; reachable(root,root);
     case next_reachable{L}: \forall list* root, *node; \valid(root) && reachable(root->next,node) ==> reachable(root,node) ;
} */

/*@inductive samelist{L} (list* l1, list* l2) {
     case base_same{L}: \forall list* l1, *l2; l1 == \null && l2 == \null;
     case next_same{L}: \forall list* l1, *l2; \valid(l1) && \valid(l2) && \separated(l1, l2) 
      && l1->key == l2->key && samelist(l1->next,l2->next) ==> samelist(l1,l2) ;
} */

/*@ predicate finite{L}(list* root) = reachable(root,\null); */

/*@ 
axiomatic Length {
  logic integer length{L}(list* l);
  axiom length_nil{L}: length(\null) == 0;
  axiom length_cons{L}:
  \forall list* l, integer n; finite(l) && \valid(l) ==> length(l) == length(l->next) + 1;
} */

/*@
  terminates finite(h); 
  assigns \result \from h;
  ensures \separated(\result,h);
  ensures finite(\result);
  ensures samelist(\result,h);
*/

 list *copyListRec(list *h)
 {
    if(h == NULL)
    {
       return NULL;
    }
    else
    {
       list *c = (list *) malloc (sizeof *c);
       c->key = h->key;
       c->next = copyListRec(h->next);
       return c;
    }
  
}

WP(Alt-Ergo)输出结果

[kernel] Parsing framac-copyList.c (with preprocessing)
[kernel:annot:missing-spec] FRAMAC_SHARE/libc/stdlib.h:436: Warning: 
  Neither code nor explicit exits and terminates for function malloc,
   generating default clauses. See -generated-spec-* options for more info
[wp] Warning: Missing RTE guards
[wp] Warning: No definition for 'length' interpreted as reads nothing
[wp] framac-copyList.c:47: Warning: 
  Missing decreases clause on recursive function copyListRec, call must be unreachable
[wp] FRAMAC_SHARE/libc/stdlib.h:427: Warning: 
  Allocation, initialization and danglingness not yet implemented
  (allocation: \fresh{Old, Here}(\at(\result,wp:post),\at(size,wp:pre)))
[wp] framac-copyList.c:45: Warning: 
  Cast with incompatible pointers types (source: sint8*) (target: _list*)
[wp] 18 goals scheduled
[wp] [Failure] typed_copyListRec_ensures (Qed 5ms) (Alt-Ergo) (Stronger, 2 warnings)
[wp] [Failure] typed_copyListRec_terminates_part3 (Qed 0.73ms) (Alt-Ergo) (Stronger, 2 warnings)
[wp] [Failure] typed_copyListRec_terminates_part2 (Qed 1ms) (Alt-Ergo) (Stronger, 2 warnings)
[wp] [Failure] typed_copyListRec_ensures_2 (Qed 6ms) (Alt-Ergo) (Stronger, 2 warnings)
[wp] [Failure] typed_copyListRec_ensures_3 (Qed 8ms) (Alt-Ergo) (Stronger, 2 warnings)
[wp] [Failure] typed_copyListRec_assigns_normal_part3 (Qed 3ms) (Alt-Ergo) (Stronger, 2 warnings)
[wp] [Failure] typed_copyListRec_assigns_normal_part6 (Qed 4ms) (Alt-Ergo) (Stronger, 2 warnings)
[wp] [Failure] typed_copyListRec_assigns_normal_part5 (Qed 5ms) (Alt-Ergo) (Stronger, 2 warnings)
[wp] Proved goals:   10 / 18
  Qed:            10 (0.73ms-2ms-8ms)
  Failed:          8

问题分析与解决方案

Frama-C完全支持链表验证

Frama-C(尤其是WP插件)完全可以处理链表这类递归数据结构的验证,你的问题并非工具本身不适合,而是验证规范和条件存在遗漏,同时需要补充malloc的行为约束。

关键问题修复

1. 补充malloc的规范

WP默认生成的malloc规范不足以支撑验证,需要手动添加明确的契约:

/*@
  requires size > 0;
  assigns \nothing;
  ensures \valid(\result) && \fresh(\result, size);
  ensures \separated(\result, \allocated);
*/
extern void* malloc(size_t size);

这个规范明确malloc返回的指针是有效、新鲜(未被使用过)且与已分配内存分离的,这对证明\separated(\result,h)和samelist的分离条件至关重要。

2. 修复递归函数的终止性证明

WP警告递归函数缺少decreases子句,需要补充终止度量(用链表的length),同时明确输入链表有限的前置条件:

/*@
  requires finite(h);  // 明确输入链表是有限的
  terminates \true; 
  decreases length(h); // 终止度量:递归调用时链表长度递减
  assigns \result \from h;
  ensures \separated(\result,h);
  ensures finite(\result);
  ensures samelist(\result,h);
*/

3. 修正samelist的归纳定义

原基例写法错误,应该直接定义空链表的相等情况:

/*@inductive samelist{L} (list* l1, list* l2) {
     case base_same{L}: samelist(\null, \null);
     case next_same{L}: \forall list* l1, *l2; \valid(l1) && \valid(l2) && \separated(l1, l2) 
      && l1->key == l2->key && samelist(l1->next,l2->next) ==> samelist(l1,l2) ;
} */

修正后才能触发WP的归纳推理机制,证明递归复制后的链表与原链表结构一致。

4. 消除类型转换警告

代码中(list *) malloc (sizeof *c)的强制转换可去掉,C标准允许void*隐式转换为其他指针类型,避免类型不兼容警告:

list *c = malloc(sizeof *c);

验证优化建议

  • 启用RTE检查:添加-rte选项,让WP自动生成空指针解引用、内存有效性等检查目标,确保代码无运行时错误。
  • 多求解器配合:使用-wp-prover alt-ergo,cvc4让WP尝试多个求解器,提升证明成功率。

修复后的效果

补充上述规范和修正后,大部分未证明的目标可被Alt-Ergo自动证明,剩余少量目标可能需要辅助引理(如finite(h) ==> finite(h->next)),但WP的归纳策略通常能自动处理这类递归性质。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 18:50:55