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

Frama-C插件测试报错:logic variable未声明的原因与解决

问题描述

我实现了《Developer Manual》4.17.7章节的示例代码,这是一个复制访问者插件,可为程序中每个除法操作添加除数非零的断言。直接将插件应用于含除法的C源码时可得到预期注解,但使用带-sandbox参数的测试代码运行时,触发AST完整性检查错误,提示“logic variable b (25) is not declared”,导致Frama-C异常终止。

插件代码

open Cil_types
open Cil
module M = Plugin.Register

let syntax_alarm =
  Emitter.create "Syntactic check" [ Emitter.Code_annot ] ~correctness:[]
    ~tuning:[]

class non_zero_divisor prj =
  object (self)
    inherit Visitor.generic_frama_c_visitor (Visitor_behavior.copy prj)

    method! vexpr e =
      match e.enode with
      | BinOp ((Div | Mod), _, denom, _) ->
          let logic_denom = Logic_utils.expr_to_term ~coerce:false denom in
          let assertion = Logic_const.prel (Rneq, logic_denom, Cil.lzero ()) in
          let stmt =
            match self#current_kinstr with
            | Kglobal -> assert false
            | Kstmt s -> s
          in
          let kf = Option.get self#current_kf in
          let new_stmt = Visitor_behavior.Get.stmt self#behavior stmt in
          Options.Self.result "NewStmt: %a" Printer.pp_stmt new_stmt;
          let new_kf = Visitor_behavior.Get.kernel_function self#behavior kf in
          Queue.add
            (fun () ->
              Annotations.add_assert syntax_alarm ~kf:new_kf new_stmt assertion)
            self#get_filling_actions;
          DoChildren
      | _ -> DoChildren
  end

let execute () =
  ignore
    (File.create_project_from_visitor "syntactic check" (new non_zero_divisor))

测试代码

/* run.config
OPT: -autoload-plugins -sandbox
*/

void main() {
    int a, b, c;
    a = 4;
    b = 2;
    c = a/b;
}

错误输出

...
[kernel] tests/s1/test.c:9: Failure: 
  [AST Integrity Check]
  AST of syntactic check
  logic variable b (25) is not declared
[kernel] Current source was: tests/s1/test.c:5
  The full backtrace is:
  Raised at Project.on in file "src/libraries/project/project.ml", line 405, characters 59-66
  Called from File.init_project_from_visitor in file "src/kernel_services/ast_queries/file.ml", line 1818, characters 4-64
  Called from File.create_project_from_visitor in file "src/kernel_services/ast_queries/file.ml", line 1842, characters 2-43
  Called from Sandbox_visitor.execute in file "sandbox_visitor.ml", line 37, characters 4-79
  Called from Stdlib__Queue.iter.iter in file "queue.ml", line 121, characters 6-15
  Called from Boot.play_analysis in file "src/kernel_internals/runtime/boot.ml", line 36, characters 4-20
  Called from Cmdline.play_in_toplevel_one_shot in file "src/kernel_services/cmdline_parameters/cmdline.ml", line 846, characters 2-9
  Called from Cmdline.play_in_toplevel in file "src/kernel_services/cmdline_parameters/cmdline.ml", line 876, characters 18-64
  Called from Cmdline.catch_toplevel_run in file "src/kernel_services/cmdline_parameters/cmdline.ml", line 235, characters 4-8
  
  Frama-C aborted: internal error.
  Please report as 'crash' at https://git.frama-c.com/pub/frama-c/issues
  Your Frama-C version is 24.0 (Chromium).
  Note that a version and a backtrace alone often do not contain enough
  information to understand the bug. Guidelines for reporting bugs are at:
  https://git.frama-c.com/pub/frama-c/-/wikis/Guidelines-for-reporting-bugs
错误原因

使用-sandbox参数时,Frama-C会创建独立的项目副本,所有AST节点(变量、语句、函数等)都会被复制到新项目中。原代码直接使用原项目的denom表达式转换为逻辑项,再将这个逻辑项添加到新项目的断言里——但原项目的变量b并未注册到新项目的逻辑环境中,导致AST完整性检查时判定变量未声明。

核心问题是:跨项目引用AST节点,违反了Frama-C的项目隔离机制。

解决方法

必须确保断言中使用的逻辑项基于新项目中的节点创建。具体来说,不能直接使用原项目的denom表达式,需先通过Visitor_behavior.Get.expr获取该表达式在新项目中的副本,再用副本转换为逻辑项并构建断言。

修改后的插件代码

open Cil_types
open Cil
module M = Plugin.Register

let syntax_alarm =
  Emitter.create "Syntactic check" [ Emitter.Code_annot ] ~correctness:[]
    ~tuning:[]

class non_zero_divisor prj =
  object (self)
    inherit Visitor.generic_frama_c_visitor (Visitor_behavior.copy prj)

    method! vexpr e =
      match e.enode with
      | BinOp ((Div | Mod), _, denom, _) ->
          (* 获取denom在新项目中的副本 *)
          let new_denom = Visitor_behavior.Get.expr self#behavior denom in
          let logic_denom = Logic_utils.expr_to_term ~coerce:false new_denom in
          let assertion = Logic_const.prel (Rneq, logic_denom, Cil.lzero ()) in
          let stmt =
            match self#current_kinstr with
            | Kglobal -> assert false
            | Kstmt s -> s
          in
          let kf = Option.get self#current_kf in
          let new_stmt = Visitor_behavior.Get.stmt self#behavior stmt in
          Options.Self.result "NewStmt: %a" Printer.pp_stmt new_stmt;
          let new_kf = Visitor_behavior.Get.kernel_function self#behavior kf in
          Queue.add
            (fun () ->
              Annotations.add_assert syntax_alarm ~kf:new_kf new_stmt assertion)
            self#get_filling_actions;
          DoChildren
      | _ -> DoChildren
  end

let execute () =
  ignore
    (File.create_project_from_visitor "syntactic check" (new non_zero_divisor))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 18:37:13