ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

第 24-3 VQF-Enc 单遍解密:mprotect + MAP_PRIVATE 写时复制

第 24-3 VQF-Enc 单遍解密:mprotect + MAP_PRIVATE 写时复制 1. 知识点mmap 的两个变脸为什么能原地解密VQF 明文加载从 Day 初就是同一招mmap后指针直挂权重结构页由内核按需从磁盘载入vllm_platform.h 144 行mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0)。mmap 有两个常被忽略的性质正是 VQF-Enc 的地基映射权限可以事后升级mprotect能把一段PROT_READ映射改成PROT_READ | PROT_WRITE——同一段地址先只读、后放写。MAP_PRIVATE 写时复制COW进程第一次写某个映射页时内核先把该页从文件读进来并复制一份私有副本之后你写的都是副本磁盘原页纹丝不动。映射上发生再多的写文件本身永远不会被改掉。两条合起来原位解密就安全了解密结果写进各页的私有副本进程内存从密文变成明文磁盘上始终是一整块密文——进程崩溃、掉电、下次冷启动拿到的还是密文。这也回答了 24-2 预告里的密文存哪、何时解密密文永远在磁盘明文只存在于进程私有内存COW 保证二者不互相污染。再解释单遍HMAC 覆盖头部 目录 全部密文数据区24-2解密也必须覆盖整个数据区——两者可以在同一个流式循环里完成每读一块先把密文原值喂进 HMAC再对同一块原位解密。磁盘只读一遍。若是两遍方案先全量算完 HMAC 验完、再从头解密磁盘要读两遍加载时间直接翻倍。2. 对应代码vqf.c 的 vqf_load 解密段591–639代码主干行号按仓库src/model/vqf.cst_mmap_open(m, 0, path); /* vllm_platform.h 139–147: O_RDONLY PROT_READ MAP_PRIVATE */ ... if (enc) { /* 591: VQF-Enc 分支 */ vqf_enc_derive(pass, sm4key, hmackey); /* 597: 口令 → SM4 16B ‖ HMAC 16B */ vc_hmac_init(hc, hmackey, 16); VQFHeader hdr_auth *h; hdr_auth.file_len 0; memset(hdr_auth.sig, 0, sizeof(hdr_auth.sig)); vc_hmac_update(hc, hdr_auth, sizeof(hdr_auth)); /* 609: 头部回填字段归 0 */ vc_hmac_update(hc, dir, n_tensors * sizeof(VQFTensor)); /* 610–611: 目录明文 */ if (mprotect(m.data, m.len, PROT_READ | PROT_WRITE) ! 0) /* 613: 只读 → 放写 */ { fprintf(stderr, [VQF] mprotect failed\n); ... } while (done dlen) { /* 624: 单遍循环 */ vc_hmac_update(hc, dp done, k); /* 627: 先喂【密文】 */ vc_sm4_ctr_crypt(sm4ctx, ctr, dpdone, dpdone, k); /* 628: 再【原位】解密 */ done k; } vc_hmac_final(hc, tag); /* 633 */ if (memcmp(tag, stored, 32) ! 0) /* 635–638: 失败即拒绝 */ { fprintf(stderr, [VQF] HMAC-SM3 mismatch (tampered or wrong key)\n); ... return -1; } fprintf(stderr, [VQF] decrypted %s (SM4-CTR, HMAC-SM3 ok)\n, path); /* 639 */ }三层各管一件事挂载层加密/明文共用同一个st_mmap_open入口O_RDONLY打开139 行、PROT_READ | MAP_PRIVATE映射144 行。加密文件并不需要特殊挂载——密文和明文的磁盘布局逐字节同长CTR 保长解密只是把读到的字节在内存里换一遍。放写层613 行mprotect把整段映射升级为可写。注释612 行写明先 mprotect 使整映射可写。没有这一步循环里第一个写就会 SIGSEGV。单遍层624–631 行1 MB 一块流式推进。627 必须在 628 之前vc_sm4_ctr_crypt是原位的in out若先解密喂进 HMAC 的就是明文tag 永远对不上。拒绝层635–638 行 tag 比对失败 →st_mmap_close并返回 -1上层打印model load failed rc1。HMAC 覆盖到文件尾所以拒绝发生读完 解完之后——攻击者既拿不到哪一块错了的提示也拿不到任何明文字节。3. 改动后果明文 vs 密文加载实测 篡改拒绝闭环实测口径RK3588 (OrangePi 5 Plus) / aarch64 / 2026-09-07。对照组为同一份 Qwen3-VL-2B 权重明文生产版Modl/Qwen3-VL-2B-Instruct/model.vqf与加密版day24_enc/model.vqfVLLM_VQF_KEYtestkey123经 SM3 派生写侧 flags0xe3两者字节尺寸完全一致2,331,316,232 BCTR 保长的直接证据。计时取 admin API 的last_load_ms与 serve 日志[M-T] model load took … ms一致。文件加载路径last_load_ms磁盘数据区读取明文无 ENC flagmmap 直挂 目录解析322不读页按需惰性载入密文VQF-EncHMAC 单遍 原位解密67,967 / 67,935两轮全量 1 遍 ≈ 2.17 GiBserve 日志对照同一引擎同一格式# 明文 serve:8804 [M-A] VQF loaded: /mnt/emmc/Modl/Qwen3-VL-2B-Instruct/model.vqf [M-T] model load took 322 ms (startup time) 加密 serve:8803VLLM_VQF_KEYtestkey123 [VQF] decrypted /mnt/emmc/day24_enc/model.vqf (SM4-CTR, HMAC-SM3 ok) [M-A] VQF loaded: /mnt/emmc/day24_enc/model.vqf [M-T] model load took 67935 ms (startup time)吞吐核算与诚实边界约 2.17 GiB去尾部 32 B tag 后 ≈ 2,331,316,200 B÷ 67.95 s ≈32.7 MiB/s。这远低于 RK3588 eMMC 顺序读带宽150–280 MiB/s 量级——瓶颈不在盘而在纯 C 国密原语SM3 压缩轮 SM4 分组均无 NEON 加速的单核流式计算。所以发布口径应写明68 s 是当前未加速实现的真实成本对应公理名at_rest_encryption_load_tradeoffvllm_crypto.h 4–7 行注释——以加载期一次性全量读 解换取磁盘上无明文、运行期零额外开销。将来若对压缩轮/轮函数做 NEON 化这一数字会显著下降那是优化篇的事本篇只记账、不改实现。另注明文对照文件此前多次加载、页缓存偏热但 mmap 挂载本身不读数据区322 ms 的量级结论不受影响。篡改拒绝闭环翻转 1 字节 → 拒绝 → 恢复 → 成功[start tamper] pid76195 [flip] 1MB 48 - b7 # 文件偏移 1MB数据区内目录区仅几千字节必然落在密文数据区翻转一字节 [load1] REJECTED elapsed68s errmodel load failed rc1 [restore] flip back [load2] OK elapsed68s decrypted ... HMAC-SM3 ok这条证据链说明任何一字节的改动都会被 HMAC-SM3 在解密完成后拒绝且拒绝发生在读完 解完之后攻击者拿不到任何中间明文或错误位置提示恢复原字节后同一份文件原样加载成功。至此VQF-Enc 的磁盘存密文、内存跑明文、篡改必被拒三条承诺全部落地。
返回列表