IOValve 精读:把沙箱边界推到网线上
把 I/O 沙箱的执法点从 CPU 核挪到一块与主机物理隔离的 DPU 上,出网流量全都要经过它——加密、阻断,再整形成恒定节拍(固定大小的包、固定间隔,没真数据就发假包)。于是沙箱里的恶意代码没有可调的信道。代价是 LLM 微调耗时增加 2.1%~4.1%,批量推理吞吐下降 1.4%。
把 I/O 沙箱的执法点从 CPU 核挪到一块与主机物理隔离的 DPU 上,出网流量全都要经过它——加密、阻断,再整形成恒定节拍(固定大小的包、固定间隔,没真数据就发假包)。于是沙箱里的恶意代码没有可调的信道。代价是 LLM 微调耗时增加 2.1%~4.1%,批量推理吞吐下降 1.4%。
把监测器做成 PCIe 端点里的一个旁听者:主机发往 GPU 的指令流被硬件复制一份,交给流水线化的多模式匹配引擎逐条扫过,命中挖矿程序的指令序列就报警。规则是数据挖掘从真实矿工的 GPU 汇编里挖出来的操作码序列,整套监测不占被监测程序的执行时间,代价是 2.3 瓦。
设备访问内存要先经 IOMMU 做地址翻译,翻译结果缓存在 IOTLB 里,而这块缓存被同一个 IOMMU 下的所有设备共用。于是攻击者不必跟受害者共享任何内存,只要让自己的 GPU 或网卡反复发 DMA 请求并给时延计时,就能从 IOTLB 的状态变化里看出隔壁设备在干什么——跨 VM 成立,隔着 RDMA 也能用。
设备要访问内存得先做地址翻译,翻译结果被 IOMMU 缓存进一块叫 IOTLB 的表里,而这表被挂在同一个 IOMMU 下的所有设备共用。于是控制一块可编程 PCIe 卡(比如 FPGA)的一方,只要盯着自己 DMA 访问的时延变化,就能知道隔壁那块设备——比如跑着 SQL 数据库的 GPU——有没有在干活,全程不需要任何内核权限。
把一块 FPGA 板插进 PCIe 链路中间、分别扮演链路两端的角色,它就能在不打断通信、不被两端察觉的前提下,把经过的每个包的类型、地址、长度记下来,还能在一个时钟周期里改写包的内容,或者把某个地址上的读请求替换成自己提前存好的副本——整套硬件逻辑占用不到 FPGA 资源的 1%,跑在 250 MHz、16 Gb/s 上。
和受害者租在同一颗物理 CPU 上的另一个进程,只要盯住 embedding 表里自己挑出来的那几百个 token 的缓存行、用 flush+reload 一圈圈轮询,就能在 LLM 生成的时候把落在这些位置上的 token 逐个记下来——在多租户部署里,这条路子能单次捞走 80%~90% 的高熵 API key。
GPU 里有一块叫 local memory 的高速暂存区,上一个程序用完不擦,下一个程序可以直接把它整块 dump 出来。对于跑在 GPU 上的 LLM,攻击者只要偷到最后一层的输入向量(4 KB),配上从开源模型库匹配到的权重,就能自己算出模型吐出的下一个 token。
把 LLM 部署到本地并不等于对话内容留在本地——同一台机器上一个没有特权的普通应用,只要盯住 CPU 缓存的访问模式和自回归解码的时序,就能把这次推理的输入与输出文本还原出来。
在 CPU 与 GPU 之间那条没有加密的 PCIe 总线上抓包,从 GPU 命令与数据搬运里反推出受害者 DNN 的结构、超参数和全部权重,重建出的模型推理精度与原模型完全一致。
链路上抓到一串整数时,它很可能就是 token id——大模型不认字,只认编号。要把这串编号变回人能读的文本,需要的只是一张 id 到字节 的对照表,本文称之为转换表;而这张表在不同模型之间并不通用。
本文的落点就是反查这一步。为了讲清它,前面会先把编码侧的形态走一遍(文字
→ UTF-8 字节 → token → id),中间说明 id 进 GPU
之后发生了什么——那部分只是背景,反查用不到模型内部的计算。文中数字全部来自本机真实跑出来的结果,词表取自
Qwen2.5-0.5B-Instruct 和 GPT-2 的公开 tokenizer.json。