跳转到内容

seL4 — 第一个被数学证明"代码和规范完全一致"的操作系统内核

是什么

seL4 是一个通用操作系统内核,特别在于:它的 C 代码每一行都被机器检查的数学证明钉死——证明这段 C 代码”做的事和设计文档上写的一字不差”。

日常类比:盖房子时拿到建筑图纸,工人凭良心照图建。seL4 的事情是——有一个机器审查员,把每一颗螺丝、每一根钢筋都和图纸对照过一遍,最后给你一张盖章证书:图纸怎么写的,房子就是怎么盖的,没有任何”工人凭感觉”。

具体保证的内容(只要硬件和编译器没 bug):

  • 内核不会 NULL 指针解引用
  • 不会缓冲区溢出
  • 不会算术溢出
  • 不会陷入死循环
  • 每个系统调用都按规范返回

这是 2009 年 SOSP 的论文,证明在 Isabelle/HOL 这个证明助手里完成,是史上第一次对通用 OS 内核做出这种保证。

为什么重要

不理解 seL4,下面这些事都没法解释:

  • 为什么航空、自动驾驶、医疗设备会愿意用一个澳洲实验室出来的内核——因为这是唯一有数学证明的通用内核
  • 为什么”形式化验证”从 1970 年代喊到 2009 年才真正在大型系统里跑通——seL4 给出了第一个”可行性证明”
  • 为什么 Linux 至今每年还在修内存安全 bug,而 seL4 这一类问题被一次性消除
  • 为什么”微内核 vs 宏内核”的辩论在 2009 年之后悄悄改变了风向——证明工作量大约和代码量平方相关,微内核才证得动

核心要点

seL4 的关键思路可以拆成 三层精化(refinement):

  1. 抽象规范层:用纯数学描述内核要做什么——“系统调用 X 接收参数 Y 后,状态从 A 变到 B”。不关心怎么实现。

  2. Haskell 可执行原型层:把抽象规范翻译成可运行的 Haskell 程序。能跑、能测,但太慢、不能直接当内核用。

  3. C 实现层:手写优化过的 C 代码(约 8700 行)。这才是真正的内核。

然后证明:第 3 层的每个行为,都能精确对应到第 2 层;第 2 层又精确对应到第 1 层。两次”精化证明”链起来,就得到:“C 代码满足最初的抽象规范”。

证明本身约 20 万行 Isabelle/HOL 脚本,由约 25 人年的人力完成。这意味着——1 行 C 大约对应 20 行证明

为什么是三层而不是直接两层(规范 → C)? 因为抽象规范是数学,C 是工程,鸿沟太大一步跨不过去。中间的 Haskell 原型起两个作用:

  • 是”可执行的规范”——能跑测试,开发期快速迭代
  • 是”翻译的中点”——把”集合、关系”这种数学结构先翻译成”列表、记录”,再从 Haskell 的”列表、记录”翻译成 C 的”数组、struct”,每一步都比”一步到位”易证

这套三层精化方法不是 seL4 发明的(来自 Refinement Calculus 的传统),但 seL4 是第一次在真实大型系统上跑通它。

实践案例

案例 1:seL4 证明的”是什么”和”不是什么”

证明保证的:

  • 内核的功能正确性——每个系统调用按规范行为
  • 关键安全性——无内存越界、无算术溢出、无死循环
  • 后续 2014 年扩展:信息流安全(机密数据不会泄漏到非授权进程)

证明不保证的:

  • 硬件 bug(比如 CPU 的 Spectre 漏洞)
  • 编译器 bug(不过后来用 CompCert 这种被验证的编译器把这块也补上了)
  • 应用层程序的安全性
  • 性能(证明只管对错,不管快慢——但 seL4 的 IPC 也是世界最快之一)

案例 2:为什么是微内核

宏内核(Linux)把文件系统、网络栈、驱动全塞进内核,几千万行代码——证明工作量爆炸。seL4 走 L4 微内核 路线:

  • 内核做:进程调度、地址空间、IPC(进程间通信)、中断
  • 文件系统、网络、驱动全在用户态
  • 内核 ≈ 8700 行 C,刚好在”人力可证”的边界上

类比:要给一座城市的每一栋楼做安检不可能;但给”地基 + 承重梁”做安检还行——其他楼的安全性可以通过”它们都坐落在已验证的承重梁上”间接保证。

案例 3:seL4 实际用在哪

  • NSA 的跨域系统:让”机密网”和”非机密网”的数据流转有数学保证
  • 波音的无人直升机:DARPA HACMS 项目,把直升机的飞控放在 seL4 上。曾做过红队测试——把无人机交给攻击者一周,攻击者拿到 root 权限的”应用层”也无法穿透 seL4 隔离边界
  • Cruise / 一些自动驾驶公司的安全关键模块
  • 医疗设备:植入式设备的固件层
  • 加密货币硬件钱包:把签名密钥放在 seL4 隔离的分区里

踩过的坑(如果你想自己做形式化验证)

  1. 证明工作量爆炸式增长:1 行代码 ≈ 20 行证明,且代码改一点证明可能整片重写。所以 seL4 团队先冻结设计、再写证明——这与”小步快跑、迭代”的常规软工背道而驰。

  2. C 不是天然可证的:C 有未定义行为(UB),seL4 团队不得不写一个 C 子集的形式语义,把”哪些 C 写法允许、哪些禁用”明确下来。

  3. 编译器是信任根:早期 seL4 假设 GCC 正确——这是个洞。后来用二进制级验证补上:直接证 ARM 机器码满足规范,不再信任 GCC。

  4. 证明助手学习曲线极陡:Isabelle/HOL 不是”按按钮就出证明”,要写战术(tactic)、引理、辅助定义。一个新人半年到一年才能贡献证明。

适用 vs 不适用场景

适用

  • 安全关键系统(航空、医疗、汽车)——一次失败代价 > 验证成本
  • 安全关键模块(密码学库、虚拟机监视器、信息流隔离)
  • 用户量极小但单点失败极昂贵的场景(卫星固件、武器系统)

不适用

  • 快速迭代的应用层软件——需求一周一变,证明跟不上
  • 大型代码库(千万行级别)——人力成本不经济
  • 需要频繁集成第三方组件——每个新组件都要重新证
  • 性能调优为主、对正确性容错的场景(推荐系统、游戏)

历史小故事(可跳过)

  • 1970 年代:形式化验证理论起步(Hoare Logic、Floyd),但没人敢动真实操作系统
  • 2001 年前后:澳大利亚 NICTA 实验室决定挑战”证明一个真内核”
  • 2004–2009 年:约 25 人年。中间多次”证明卡住 → 改设计 → 重证”
  • 2009 年 SOSP:第一篇论文,业界震惊——很多人此前认为”通用 OS 形式化验证”理论上可行、工程上不可能
  • 2014 年:扩展到信息流安全,证明机密数据不会被偏路泄漏
  • 2018 年:seL4 基金会成立,开源治理;NVIDIA、波音等加入
  • 2020 年代:自动驾驶把 seL4 用在量产车的安全关键模块上

学到什么

  1. 形式化验证从”科幻”走到”工程”——不是不能做,是要选对场景(小内核 + 高代价失败)
  2. 微内核不是因为”哲学好看”才赢——是因为它证得动**——这是工程现实
  3. 三层精化(抽象 → 原型 → 实现)是把”一次性大证明”拆成”每层小证明”的关键技巧
  4. 信任根分析:再强的证明也基于某个假设(硬件、编译器)。把假设挪得越靠物理底层,整个系统越可信
  5. 代价 vs 收益:1 行 C ≈ 20 行证明,这告诉你形式化验证用在哪里——核心、稳定、高价值的小模块

延伸阅读

关联

  • isabelle-hol-2002 —— Isabelle/HOL:seL4 证明的工具
  • hoare-logic —— Hoare Logic:把”程序对不对”变成数学证明
  • cakeml —— CakeML:从源码到机器码每一步被证明的 ML 编译器,思路相通
  • lean-prover —— 现代证明助手,与 Isabelle/HOL 同代
  • lamport-tla-1994 —— TLA+:另一种形式化方法(侧重并发系统的状态机)
  • turing-1936 —— 可计算性:形式化验证的最远祖先

一句话记忆

seL4 不是”更快的内核”,而是第一个把”代码确实做了规范说要做的事”这件事变成数学定理的内核——当一段代码的失败代价是人命或一架飞机时,“我测过没 bug”和”我有数学证明”是两个量级的可信度。

反向链接

  • amoeba-1990 —— Amoeba — 把整个机房当一台操作系统
  • cakeml —— CakeML — 从源码到机器码每一步都被数学证明的 ML 编译器
  • denali-2002 —— Denali — 在一台机器上同时跑上千个轻量 VM 的早期实验
  • exokernel-1995 —— Exokernel — 把抽象推到用户态的极致设计
  • haven-2014 —— Haven — 把整个应用装进 CPU 黑盒,让云服务商也看不见
  • hoare-logic —— Hoare Logic — 把”程序对不对”变成”数学证明对不对”
  • lamport-tla-1994 —— TLA — 把状态机和时序逻辑捏成一个公式
  • lean-prover —— Lean 4 — 用 Lean 重写的 Lean,让数学家和程序员共用一种语言
  • mirage-2013 —— MirageOS Unikernels — 应用即内核,把操作系统编译掉
  • snap-2019 —— Snap 2019 — Google 把网络栈搬进用户空间的微内核实践
  • turing-1936 —— Turing 1936 可计算性
  • v-system-1988 —— V 分布式系统 — 把局域网当成一台机器,内核只剩进程加 IPC