当前位置: 首页 > news >正文

Preguss:结合大语言模型与形式化验证的运行时错误检测

1. Preguss:当运行时错误遇见大语言模型

在软件验证领域,我们长期面临一个核心矛盾:形式化方法能提供数学级别的可靠性保证,但规范编写需要耗费专家数月甚至数年的时间。以航天软件为例,Ariane 5火箭的爆炸事故调查报告显示,一个未捕获的运行时类型转换错误导致了7.5亿美元的损失——这正是传统测试方法难以发现的深层次问题。

Preguss的创新在于将静态分析的精确性、形式化验证的严谨性与大语言模型的生成能力相结合。其核心思想可以类比为"分而治之"的战术:面对一个包含数百个函数的代码库,不是试图一次性验证整个程序,而是通过运行时错误(Runtime Error,简称RTE)断言作为路标,将验证任务分解为多个可独立处理的验证单元(Verification Unit,简称V-Unit)。每个V-Unit包含:

  • 一个关键RTE断言(如数组越界检查)
  • 相关的函数调用链上下文
  • 需要生成的规范集合(前置条件、循环不变量等)

2. 技术架构解析

2.1 RTE引导的程序切片

传统静态分析工具(如Frama-C/Eva)会产生大量潜在错误报告,其中许多是误报。Preguss首先运行静态分析器获取RTE断言集合,然后执行关键的三阶段处理:

  1. 断言分类:将RTE分为内存安全(如空指针解引用)、数值安全(如整数溢出)等类别。例如对代码*(data + i)的检查会生成\initialized(data + i)的ACSL(ANSI/ISO C Specification Language)断言。

  2. 依赖图构建:建立RTE断言间的数据流和控制流依赖关系。如图1所示的TMProcess函数中,SendUartData的调用依赖于数组pkv的初始化状态。

  3. V-Unit生成:根据依赖关系切片代码,确保每个V-Unit包含验证特定断言所需的全部上下文,同时不超过LLM的上下文窗口限制(如Claude 3的200K token)。

关键技巧:通过#pragma slice指令标记关键代码区域,配合程序依赖图分析,可以提取出比传统过程内分析更精确的上下文。

2.2 规范合成策略

Preguss采用分层规范生成策略,其创新性体现在三个方面:

  1. 基于类型的模板初始化
// 对数组参数自动生成的基础模板 /*@ requires \valid(arr + (0..len-1)); */ void process_array(int* arr, size_t len);
  1. 反馈驱动的迭代优化
  • 首次生成规范后,用Z3/SMT求解器验证
  • 若验证失败,提取反例并构造新的Prompt: "之前的循环不变量无法证明数组访问安全,请考虑添加关于循环索引j的范围约束"
  1. 跨过程规范协调: 当验证SendUartData的调用时,Preguss会同时生成调用方的约束条件:
// 在调用方生成保持条件 /*@ loop invariant 0 <= i <= 32; loop invariant \forall integer j; 0 <= j < i ==> \initialized(&pkv[j]); */

3. 实战:验证Contiki网络协议栈

以物联网操作系统Contiki的AES-CCM*加密模块为例,演示Preguss的完整工作流:

3.1 准备阶段

# 使用Frama-C/RTE生成初始断言 frama-c -rte -metrics aes-ccm.c -then -report

3.2 验证单元处理

  1. 识别关键RTE:如memcpy操作可能存在的缓冲区溢出
  2. 生成规范草案
/*@ requires \valid(dest + (0..n-1)) && \valid_read(src + (0..n-1)); assigns dest[0..n-1]; ensures \forall integer i; 0 <= i < n ==> dest[i] == \old(src[i]); */ void safe_memcpy(char* dest, const char* src, size_t n);
  1. 交互式修正:当静态分析器报告dest可能为NULL时,Preguss自动强化前置条件:
/*@ requires dest != NULL && src != NULL && ... */

3.3 性能优化技巧

  • 缓存机制:对相似代码模式(如数组遍历)复用已验证规范
  • 并行验证:独立V-Unit可分布式处理
  • 增量更新:代码变更时仅重新验证受影响单元

4. 效果评估与调优

在X509-parser项目中的实测数据显示:

指标Preguss传统方法
验证覆盖率86.6%42.3%
人工修改规范数39251
平均验证时间151m>15h

典型问题解决方案

  1. 过约束问题:如将\valid误用为\valid_read时,添加约束松弛规则:

    if "over-constrained" in feedback: prompt += "请考虑将\valid替换为\valid_read"
  2. 循环不变量生成:对嵌套循环采用"由内而外"策略,先验证最内层循环。

  3. 外部库处理:对没有源码的标准库函数(如memset),使用合约数据库:

    // 预定义的合约模板 @library_contract("string.h", "memset")

5. 局限性与应对策略

当前版本在以下场景仍需人工干预:

  1. 复杂数据结构:如链表、树的递归约束需要手工提供归纳定义

    /*@ inductive linked_list(struct node* n) { case empty: linked_list(\null); case cons: \valid(n) ==> linked_list(n->next) ==> linked_list(n); } */
  2. 浮点精度问题:航天控制代码中的浮点断言需要特殊处理:

    /*@ requires \abs(f1 - f2) < 1e-6; */
  3. 并发场景:需扩展为/*@ concurrent_requires */注解

建议的渐进式验证路线:

  1. 先用Preguss验证所有RTE
  2. 对核心模块补充功能正确性规范
  3. 最后处理性能约束等非安全属性

6. 工具链集成建议

构建完整的CI/CD验证流水线:

graph LR A[代码提交] --> B{静态分析} B -->|RTE报告| C[Preguss生成规范] C --> D[形式化验证] D -->|成功| E[合并代码] D -->|失败| F[反馈优化]

实际工程中,建议:

  1. 对关键函数设置验证门禁
  2. 将生成的规范提交为代码审查的一部分
  3. 定期审计规范数据库的一致性

我在实际部署中发现,结合Git Hooks实现预提交验证,可以将运行时错误提前到开发阶段发现。例如添加pre-commit脚本:

#!/bin/sh frama-c -rte -wp -wp-prover z3 -wp-out $TMPDIR/wp "${changed_files[@]}" grep -q "Verified" $TMPDIR/wp/report.txt || exit 1

这种方法的优势在于,它把形式化验证从"奢侈品"变成了每个开发者都能使用的日常工具。正如我们在验证某航天器控制软件时发现的,一个隐藏多年的未初始化变量问题,正是通过这种自动化流程被捕获——而这可能避免了又一起"Ariane 5式"的灾难。

http://www.jsqmd.com/news/720152/

相关文章:

  • Obsidian Day Planner:2025年终极日程管理插件,打造高效时间管理系统
  • 3步轻松搞定Android设备预装软件清理:Universal Android Debloater完全指南
  • 常见的自动化测试工具,好学吗?
  • 从‘木牌’到‘木甲’:《饥荒》Mod开发中,如何用几行Lua代码解决合成系统的‘祖传痛点’?
  • 终极游戏文本提取指南:用Textractor轻松获取游戏对话与剧情
  • 你的Android设备为什么越来越慢?3个关键步骤让Universal Android Debloater帮你彻底解决
  • 从ViT到PVT:SRA模块如何让Transformer在CV任务上‘瘦身’成功?
  • STC15单片机定时器与计数器实战:拆解NE555测频代码,搞懂12T/1T模式到底怎么选
  • 芮洣舒面霜能不能长期用
  • Citra模拟器终极指南:如何免费在电脑上畅玩任天堂3DS游戏
  • RTranslator模型下载终极指南:5分钟搞定离线翻译,告别数小时等待
  • 从Nexus私服配置到Maven本地缓存:彻底搞懂依赖更新间隔(update interval)那点事
  • Winhance中文版:5个关键功能让Windows系统优化变得前所未有的简单
  • 国产替代之2SK3614-Q-TD-E与VBI1695参数对比报告
  • Windows 11经典游戏兼容终极指南:让老游戏重获新生
  • 还在熬夜肝本科终稿?Paperxie 这套「四步走」写作流程,帮你从选题到定稿少走 99% 弯路
  • TB6612驱动模块深度评测:对比L298N,在STM32项目里到底该选谁?附完整测试代码
  • PDFMathTranslate:如何让学术论文跨越语言障碍?三大痛点一站式解决方案
  • AI搜索引流公司有哪些?看完抓紧打造护城河 - FaiscoJeff
  • 3步精通Pixelle-Video:从零到高手的工作流自定义完全指南
  • Amplifier Research 150A220 220MHz 200W 功率放大器
  • Mediapipe进行头部姿态估计
  • 2026年数据集成厂商精选,覆盖地产物业资产主数据统一管理 - 品牌2026
  • 魔兽争霸3终极优化指南:5步解锁300帧流畅体验
  • Layerdivider:如何将单张图片智能分层为可编辑PSD文件
  • 被问懵了,加密后的数据如何进行模糊查询?
  • Winhance中文版:让你的Windows系统性能翻倍的终极优化指南
  • Platinum-MD终极指南:三分钟掌握高品质MiniDisc音乐传输
  • LFM2.5-1.2B-Instruct应用案例:如何用轻量模型搭建智能客服系统
  • 基于遗传算法的双层规划模型求解MATLAB实现