漏洞

那一页明文为何在隧道外变成了密文:追踪 IP-TFS 丢失的一位所有权标记(CVE-2026-53363

CVE-2026-53363 始于一次看似完整的零拷贝搬运:数据、长度和页引用都到了目的地,决定“能不能原地写”的标记却留在身后;本文从一页仍被文件或 pipe 持有的内存出发,沿 IP-TFS 聚合、skb 分片转移与 ESP 加密逐站复核,解释两行修复为何守住了内核数据所有权。

暖纸手绘调查台上,工程师把不同大小的报文整理成统一信封,再送入加密隧道。
文章导航

研究依据逐行复核 iptfs_output_queued() 、 iptfs_consume_frags() 、IPv4/IPv6 ESP 输出路径与修复提交 e9096a5a170e

来源Linux CNA、上游 XFRM/IP-TFS/ESP 源码、RFC 9347 与 SOSEC 本地源码复核

故事从一页并不属于网络栈的内存开始。它也许是文件页缓存的一部分,也许刚被 vmsplice() 放进 pipe,又也许仍有另一个消费者握着同一页的引用。应用希望尽量少复制,于是 Linux 让这页数据以 skb 非线性分片的形式一路向下。只要每一站都记得它仍然“与外部共享”,这条零拷贝道路既快又安全:读取可以共享,真正写入前必须另做私有副本。

IP-TFS 接过这页时,任务不是简单转发一枚 IP 包。它把多枚内层报文重新排布、聚合和填充,让外部观察者更难从长度与节奏猜出通信内容。为了不把已经存在的页数据重新复制一遍,2024 年加入的优化把源 skb 的页分片描述符直接搬进新的聚合 skb。描述符到了,引用计数也按新的释放责任安排好了,唯独 SKBFL_SHARED_FRAG 没有同行。

丢掉一位标记,不会立刻造成崩溃。聚合 skb 仍能排队,长度仍然正确,ESP 仍会产生格式正常的密文,远端也可能照常解密。真正的错发生在更隐蔽的一刻:ESP 判断这些页是私有可写的,于是省略写时复制,把明文原地改成密文。网络包看起来成功了,隧道外另一个持有者看到的内容却被悄悄改变。

CVE-2026-53363 表面像是“少复制一个 flag”的机械问题,实质是一份跨子系统的所有权契约被截断。IP-TFS 负责重组流量,skb 负责描述内存,ESP 负责变换数据;任何一层只看自己的局部状态,都可能认为操作合理。只有把同一页从来源一路跟到写入点,才能看见那位标记实际上承担着隔离边界。

本文不靠危险利用代码证明问题。我们以公开的 Linux CNA 记录、上游引入与修复提交、RFC 9347、内核 sk_buff 文档以及 IPv4/IPv6 ESP 源码为依据,在本地只做静态差分和受控所有权推演。读者会看到漏洞成立所需的精确条件、为什么常规互通测试容易漏掉它、怎样验证供应商回移,以及怎样把这次经验变成以后审计零拷贝路径的方法。

先记住一句贯穿全文的话:页引用保证“内存还活着”,共享标记保证“你不能把它当成只属于自己”。CVE-2026-53363 的全部因果,都发生在这两个概念被误当成同一件事之后。

1 一次“成功”的发送,为什么留下了被改写的原页

1.1 隧道没有报警,错误却发生在隧道之外

设想一台承担加密出口的 Linux 网关。上层服务把一段文件内容送入 socket,内核没有把每个字节抄进新的连续缓冲区,而是让 skb 直接引用现成的页。IP-TFS 把几枚这样的内层包拼成尺寸更均匀的外层载荷,ESP 随后完成加密。抓包显示序号连续,认证标签正确,接收端解密后也拿到了原始数据。按传统网络验收标准,这是一笔无可挑剔的发送。

但如果在发送前给原文件页或 pipe 中的那段内容做一次哈希,发送后再做一次,脆弱内核上可能出现另一幅图景:网络上的密文正确,原先共享给网络栈的内存却被局部改成了加密结果。问题没有沿链路传播到对端,而是从链路处理过程反向污染了数据来源。它因此很容易被“能不能通信”的测试遗漏,也很难仅靠外部流量监控发现。

这类现象看似违反直觉,是因为人们常把“skb 持有这一页”理解成“skb 独占这一页”。内核的页引用只阻止内存过早释放;文件缓存、pipe、另一个 skb 或其他子系统仍可能同时引用同一页。网络栈可以在不复制的情况下读取它,却不能据此获得原地改写权。真正表示这种限制的,是随分片一起保存的共享属性。

发送路径的前半段恰好全部是只读的。分片被排队、计算长度、搬动描述符、写进聚合布局,这些操作都不必改变页里的字节。于是共享属性即使已经丢失,系统仍能走很远而没有可见异常。直到 ESP 为加密准备可写缓冲区,错误状态第一次转化为错误动作:本该触发复制的判断返回了“可以原地写”。

这也是调查时必须把观察点放在两个世界的原因。一边是外层 IPsec 报文:它验证协议功能是否仍然正常;另一边是最初提供页的对象:它验证零拷贝是否遵守所有权。只观察第一边,会得出“发送成功”;同时观察两边,才会发现成功是以改写别人的页为代价。

公开 CVE 记录把结果描述为只读页缓存页可能被 ESP 原地加密。源码显示边界还应按语义理解:凡是通过带 SKBFL_SHARED_FRAG 的非线性分片进入这条路径、并在网络变换之外仍有可观察所有者的页,都依赖该位迫使下游复制。文章后文会严格区分已经由上游确认的行为与这种从公开代码推导出的通用所有权模型。

1.2 漏洞需要三扇门同时打开,并非所有 IPsec 主机都一样

第一扇门是活动的 IP-TFS 发送路径。普通 ESP 传输、未启用 IP-TFS 的 XFRM state,或只承担接收解封装的主机,不会经过本次出错的 iptfs_consume_frags() 搬运。内核版本号只能说明代码是否可能存在,配置和实际方向决定脆弱函数是否会执行。

第二扇门是源 skb 的非线性分片确实与外部共享。若上层早已把数据复制进私有线性缓冲区,或者页没有被任何网络栈之外的主体持有,那么丢失共享位仍是错误,却缺少可以被反向污染的对象。sendfile()splice()vmsplice() 与其他页支持的高吞吐路径值得优先盘点,不能据此简单断言每次调用都必然触发。

第三扇门是下游选择了能够直接变换分片的 ESP 快路径。IPv4 与 IPv6 实现都检查 skb 是否克隆、是否非线性、是否带 frag_list,以及是否含共享分片。只有这一组条件使代码相信现有页可以安全写入时,缺失的标记才改变分支。若其他条件已经迫使 skb_cow_data() 复制,漏洞状态可能被偶然遮住。

三扇门解释了为什么同一个内核在不同设备上表现不同。一台只跑传统隧道的服务器可能没有可达路径;另一台启用了 IP-TFS,但应用总是复制发送,也可能暂时看不到外部页变化;承担大文件零拷贝发送的网关,则更接近公开记录描述的风险条件。版本清单只提供第一项坐标,优先级还须结合配置、数据来源与写入分支。

它们也给出安全的验证办法:不需要构造越界写或执行载荷,只需在隔离环境让一页受控内容同时被来源对象与 skb 引用,确认它经 IP-TFS 搬运后仍被标记为共享,并验证 ESP 写入发生在新页而非原页。测试目标是证明所有权不变量,不是放大破坏结果。

调查至此已经有了完整悬念:是谁搬走了分片,谁忘了搬走标记,谁最后依据错误标记写了数据。接下来沿着 RFC 9347 定义的流量整形目标进入 Linux 实现,看看一个为了减少可观察特征和复制成本的好优化,怎样在两个相隔甚远的子系统之间搭起了脆弱通道。

2 IP-TFS 把不同节奏装进同一只信封

2.1 RFC 9347 关心的是流量形状,Linux 必须处理的是字节与页

传统 IPsec 能隐藏载荷内容,却仍会留下包长、包数与发送间隔。一个短小交互请求、一段持续文件传输和一批周期性心跳,即便都被 ESP 加密,也可能呈现不同外形。RFC 9347 定义的 IP Traffic Flow Security 在 ESP 之前加入聚合、分片和填充,让发送端可以按更稳定的节奏产生外层包,从而削弱这些旁路特征。

协议视角把内层报文看作可排入 IP-TFS 流的记录。实现视角却必须决定每个字节放在哪里、由谁释放,以及需要写入时是否能继续复用原存储。Linux XFRM 接收已经形成的 skb,把它们放入 IP-TFS 队列,再从队列中消耗足够字节构造一枚输出 skb。一个内层包可能完整进入当前输出,也可能横跨两个输出边界。

如果每次聚合都分配大块连续内存并复制所有内层载荷,所有权最容易推理,却会抵消网络栈长期建设的零拷贝收益。对大吞吐网关而言,页数据已经存在,把 skb_frag_t 描述符移动到新 skb,往往比重抄数据便宜得多。引入提交 b96ba312e21c 正是在这条路径上共享内层包的页分片。

所谓“移动描述符”并不是把物理页搬到另一个地址。描述符只保存页引用、偏移与长度;转移后,目标 skb 接管释放责任,源 skb 不再把这些分片计入自己的 nr_frags。数据字节从头到尾可能待在同一页里,变化的是谁宣称持有它以及下游如何解释这种持有。

这种优化要求所有与页有关的语义随描述符同行。长度决定读取范围,引用决定生命周期,校验和状态影响协议处理,而共享标记决定写权限。只复制前三类信息,相当于把图书连同书页完整交给下一位保管员,却漏掉“不得在原件上批注”的标签。内容没丢,规则丢了。

IP-TFS 的隐私目标反而加重了这份规则的重要性。聚合后的数据几乎必然要交给 ESP 变换,写入不是罕见的附带行为,而是发送链的预定终点。一个在普通只读转发中或许长期潜伏的所有权缺陷,在“聚合后立刻加密”的组合里拥有稳定的写入触发器。

手绘工作台把多种尺寸和节奏的内层报文整理、填充成统一的 IP-TFS 信封,再送入 ESP 隧道。
图 1:IP-TFS 把流量形状重新编排,Linux 实现则必须同时搬运数据描述符与所有权语义。图中每一枚彩色条代表只读参与聚合的页片段;进入加密隧道前,写权限仍需单独确认。

2.2 从排队到 ESP,五次交接把局部优化连成一条因果链

第一站是上层发送路径。应用交出数据,skb 可能拥有线性头部和若干页分片。若页还受外部对象影响,网络栈把 SKBFL_SHARED_FRAG 放在 skb_shared_info.flags 中。这一位不是说整个 skb 已被 clone,也不是普通页引用计数的别名;它专门提醒下游,非线性分片可能在 skb 之外被观察或修改。

第二站是 IP-TFS 队列。发送逻辑统计当前输出还容得下多少字节,从队首 skb 取出完整载荷或一部分,必要时安排后续分片。队列管理关心长度、偏移、填充与时序,它没有理由修改被引用页里的明文。直到这里,脆弱与修复内核的外部行为仍然相同。

第三站是 iptfs_consume_frags()。当源 skb 中的页分片能够整体纳入目标时,函数把源 frags[] 条目追加到目标数组,增加目标 nr_frags,再把源计数清零。清零是所有权交接的落款:此后源 skb 销毁不能再释放那些引用,目标成为页分片在网络链路上的责任主体。

第四站是 XFRM/ESP 输出准备。ESP 需要头尾空间、认证数据和一片可以被密码算法写入的载荷。代码根据 skb 结构选择是直接使用现有分片,还是调用写时复制帮助函数。这个选择必须依赖第三站保留下来的共享信息,因为 ESP 并不知道页最初来自文件、pipe 还是别的生产者。

第五站才是密码变换。算法对目标散列列表中的明文字节进行原地或副本上的变换。若前一站选择正确,外部共享页保持明文,私有页承载密文;若选择错误,算法完全按照给定目标工作,却把结果写进仍在别处可见的页。密码代码没有越界,也没有绕过认证,它只是被交付了一份错误的可写性结论。

因此,漏洞修复不能只在最后一站粗暴禁止原地加密。那会牺牲所有真正私有分片的快路径,也掩盖上游元数据转移不完整的根因。正确位置是第三站:分片描述符从谁转给谁,共享约束就从谁转给谁。恢复这一事实后,第四站已有的防护逻辑会自然作出正确选择。

这条链也解释了为什么源码审计不能停在修复的两行。只看 toi->flags |= ...,容易把问题写成平淡的 flag 漏传;继续看 ESP,才知道该位控制真实写入;再回到页来源,才知道写入影响的并非临时网络缓冲区。报告的叙事顺序与调查顺序一致:从异常结果倒推决策,再追到被遗漏的事实。

3 一页内存有两本账:活多久,以及谁能写

3.1 引用计数守住生命周期,却不会自动授予独占写权

struct sk_buff 并不要求所有报文内容连续排列。线性区域通常保存协议头和一部分数据,skb_shared_info 中的 frags[] 则描述页片段。每个条目把一页中的偏移和长度交给网络栈使用,多个条目可以指向不同页,也可以让多个对象引用同一物理页。

第一本账是生命周期账。只要目标 skb 仍持有页引用,释放源 skb 就不应让物理页消失;目标最终销毁时,又必须准确归还引用。iptfs_consume_frags() 在描述符转移后把源 nr_frags 清零,正是为了避免源和目标都按“自己负责释放”结账。若这本账错了,常见结果会是提前释放、重复释放或泄漏。

第二本账是可写性账。页仍然活着,不表示当前 skb 是唯一观察者。页缓存需要继续代表文件内容,pipe 中的消费者期待读到生产者放入的数据,另一个 skb 也可能引用同一片段。即使引用计数完全正确,任一参与者擅自改写,其他参与者都会看到变化。SKBFL_SHARED_FRAG 就是这本账上的醒目标记。

两本账之所以容易混淆,是因为“增加引用”听起来像完成了共享管理。实际上引用只回答物理页能否释放,无法回答内容能否修改。只读阶段,两种状态的行为相同;写入阶段,它们突然分叉。内核中的写时复制机制正是把“活着但共享”的页变成“各自活着且各自可写”的私有副本。

skb_clone() 又引入另一类共享:skb 的数据区域可能由多个 skb 结构共同持有。ESP 因此既检查 clone/可写状态,也检查非线性分片是否带外部共享标记。一个 skb 没有被 clone,并不能推出它引用的每一页都只属于自己;CVE-2026-53363 恰好让后半句被错误地当成了前半句的自然延伸。

审计时应把这两本账写成独立问题。分片转移之后,谁负责 put_page?这检查生命周期。分片转移之后,谁还能观察或改变页里的字节?这检查写权限。只有两个答案都明确,才能判断新的 skb 是否适合交给会原地变换数据的下游。

同一页数据同时由文件柜、pipe 和网络分片托盘持有,红色封印随网络分片提醒下游它仍被共享。
图 2:页引用像钥匙,保证对象仍能找到这页;共享标记像不得改写的封印,限制持有者的动作。两者都要转移,却回答不同问题。

3.2 SKBFL_SHARED_FRAG 是聚合属性:一片共享,整体就不能冒充私有

在内核头文件中,SKBFL_SHARED_FRAG 位于 skb_shared_info.flags。辅助函数 skb_has_shared_frag() 只有在 skb 含非线性数据且该位存在时才报告共享。该标记以整个分片集合为粒度描述外部共享风险,并未为每个 skb_frag_t 单独保存状态位。

集合级表示法决定了合并规则必须是按位 OR。目标 skb 原有分片若已经共享,追加私有分片不能让标记消失;目标原先私有,只要加入任一共享来源,也必须变为共享。顺序不影响结果。用布尔逻辑表达就是:目标的新状态等于目标旧状态或源状态,而绝不能等于最后一个来源的状态。

修复还把传播限定在源 nr_frags 非零时。这个条件看似细小,却避免一枚已经没有分片、但 flags 中可能残留旧值的 skb 污染目标。真正被移动的集合有成员时,才继承该集合的共享属性;没有成员,就没有所有权事实需要传播。

标记必须在源计数清零前读取。清零之后,源 skb 在语义上已不再拥有这些分片;即便 flags 字段的位还留在内存里,也不应该再拿“没有成员的集合”解释目标。补丁把 OR 操作放在清零之前,使代码顺序与所有权事件顺序完全一致:先转交约束,再完成交接。

网络核心中其他移动或合并分片的路径也传播这一位,这为修复提供了交叉证据。它不是 IP-TFS 临时发明的规避措施,而是 skb 所有权模型已有的一贯做法。CNA 还把本问题与 CVE-2026-46300 所代表的同类遗漏联系起来,提醒审计者:只复制 frags[] 而忽略集合属性,是可以在不同辅助函数里重复出现的缺陷模式。

这也是为什么报告不把建议写成“禁用零拷贝”。共享页本身是合法且重要的性能能力,内核已经有为它准备的写时复制信号。真正需要守住的是:每次拆分、合并、转移描述符时,集合的最严格属性不能被稀释。把优化撤掉能够绕开一个现场,却不能修复所有权传播这一类问题。

现在,两本账已经对齐:目标正确接管页引用,却在脆弱版本里没有接管共享限制。下一章把镜头推近到几行代码,看这一位究竟在哪个赋值之间消失,又怎样让相距数层调用的 ESP 作出相反决策。

4 标记在清零前失踪,ESP 在几层之后作出错误决定

4.1 iptfs_consume_frags() 搬完了实体,却漏掉集合的限制

脆弱函数接收目标 skb 和源 skb,分别取得两边的 skb_shared_info。它先确认目标分片数组还有空间,再把源 frags[] 中的条目复制到目标数组尾部。这里复制的是描述符结构,不是描述符指向的页内容,因此实现保住了优化想要的零拷贝性质。

随后,函数把源分片数加到目标 nr_frags,更新载荷核算,最后把源 nr_frags 设为零。从释放责任看,这组操作是完整的:源 skb 不再宣称自己拥有条目,目标 skb 会在生命周期结束时负责归还页引用。静态检查若只追内存泄漏与 use-after-free,很可能在此处给出通过。

漏洞藏在两个计数赋值之间。源 skb_shared_info.flags 可能带有 SKBFL_SHARED_FRAG,目标 flags 却没有任何赋值接住它。源计数清零以后,那一位仍可能物理存在于源结构中,但它已经与被移动的页分片分离;真正持有分片的目标继续报告“没有共享页”。

这是典型的半次移动。编程语言里的所有权移动往往由类型系统限制源对象继续使用,C 里的 skb 转移则靠程序员同步更新数组、计数、引用和 flags。每个字段单看都合法,组合事实却不再成立。目标所描述的数据是真实的,目标对数据可写性的描述是假的。

引入提交的目标也可以从历史中还原。它让 IP-TFS “share page fragments of inner packets”,减少组装外层包所需的复制。源码差分没有引入新的密码逻辑,也没有改变协议解析;它新建了一条页分片所有权转移路径,安全回归随这条优化路径产生,IP-TFS 规范本身没有发生变化。

修复提交 e9096a5a170e7ecd6467bc2e08668ec39897cda7 在源有分片时加入一条核心操作:toi->flags |= fromi->flags & SKBFL_SHARED_FRAG。掩码只传播需要的共享位,OR 保留目标既有状态,位置又早于源计数清零。两行上下文恢复了完整移动语义,没有修改线上的 IP-TFS 格式。

修复小到容易被低估,但它的精确性可以用三个反例检验。目标已共享、源私有时,状态不能被清除;目标私有、源共享时,状态必须被置位;源没有实际分片时,陈旧 flags 不能凭空污染目标。补丁对三种情况都给出正确结果,这比笼统地“复制 flags”更可靠。

上下两条手绘流程对比页分片描述符转移时遗漏共享标记与修复后标记同行的结果。
图 3:脆弱路径把分片条目送到目标,却让共享封印留在空的源 skb;修复路径让封印先同行,再完成源计数清零。

4.2 IPv4 与 IPv6 ESP 都把这位标记当作写时复制的证词

沿调用链进入 net/ipv4/esp4.c,输出准备代码尝试判断现有 skb 是否可以直接交给异步或同步密码处理。若 skb 未被 clone、具有非线性数据、没有复杂的 frag_list,并且 skb_has_shared_frag() 返回 false,就存在直接使用现有分片的快路径。否则,代码需要通过 skb_cow_data() 等机制获得安全可写的布局。

net/ipv6/esp6.c 保持同类判断,这意味着问题不是某一种地址族的偶然分支。只要 IP-TFS 输出最终进入相应 ESP 处理,丢失的共享位就可能影响是否复制。部署盘点因而不能用“我们只跑 IPv6”或“只有 IPv4 隧道”排除风险,仍应以供应商实际源码与配置为准。

在修复内核上,目标 skb 继承标记,skb_has_shared_frag() 报告 true,快路径条件被否决。写时复制阶段建立私有可写页,密码算法只修改副本。原文件页或 pipe 页继续保存明文,外层 skb 则携带变换后的密文。两个观察者从这一刻分居不同物理存储,彼此不再干扰。

在脆弱内核上,函数报告 false。若其他条件也允许,ESP 认为分片可原地写,密码算法沿散列列表覆盖原字节。由于目标持有的仍是最初那一页,文件缓存或 pipe 另一端随后读取时看到的是变化后的内容。外层网络结果可以正确,因为被覆盖的字节正是 ESP 想发送的密文。

这不是密码算法把数据写到了错误偏移,而是调用者把错误的页交给了算法。因而更换加密套件、切换硬件或软件实现,不能作为通用修复。不同实现可能改变是否命中原地路径,从而影响复现概率,却没有恢复被截断的所有权事实。

同理,观察到 skb_cow_data() 在某次测试中执行,也不等于版本安全。clone 状态、分片排列、头尾空间或设备能力都可能迫使复制,暂时掩盖缺失标记。验证必须直接检查标记传播,并设计同时满足快路径其他条件的受控用例,不能把一次“原页没变”当成充分证明。

手绘分叉展示共享封印存在时先复制后加密,封印丢失时原共享页被直接送进加密压机。
图 4:同一套 ESP 变换在两种所有权结论下产生不同隔离结果。安全路径复制后写,脆弱路径把共享原页误当作私有页。

到这里,因果链闭合了:引入优化创建转移路径,转移遗漏共享位,ESP 依赖该位选择是否复制,原地加密把错误状态变成外部可见改写。后面的风险评估将不再停留于“可能内存损坏”的泛称,而是据此区分受影响资产、可观察结果与修复优先级。

5 把“受影响版本”收窄成真正需要处置的网关

5.1 四项证据缺一不可:代码、配置、页来源与写入路径

资产盘点的第一层是运行代码。Linux CNA 把引入点映射到 b96ba312e21c9b7ac1526829b9640ddc06695c0b,该工作进入 6.14 时代的上游实现;公开版本映射给出 6.18.36、7.0.13 与 7.1 的修复起点。这里的数字是上游坐标,不应直接替代发行版内核的构建证据。

企业发行版经常把新功能和安全修复回移到不同基线。一枚版本字符串看似早于 6.14,也可能携带 IP-TFS;另一枚字符串看似处于公开受影响范围,却可能已经回移 e9096a5a170e 的等价代码。可靠结论应来自供应商公告、源码包或构建标识,再以函数中的标记传播逻辑交叉确认。

第二层是活动配置。通过 ip -s xfrm state 与策略清单记录命名空间、地址族、端点、方向、模式、接口和计数器。静态配置仓库还要纳入,因为当前没有流量不表示下一次编排不会创建 IP-TFS state。短命容器网关尤其可能在人工检查时恰好为空。

配置必须区分发送与接收角色。本漏洞的错误写入发生在组织 IP-TFS 输出并交给 ESP 的方向;只解封装输入流量的节点与同时承担双向转发的节点,处置优先级不同。高可用集群中,备用实例也不能因计数器为零而被遗漏,它可能在故障切换后立刻接管同一业务。

第三层是数据如何进入 socket。文件服务、对象缓存、代理、备份通道和高吞吐消息系统常用页支持路径减少复制。一次 sendfile() 只能提供线索,最终结论需要应用配置、运行库、系统调用追踪与框架文档共同支撑。某些 TLS 或过滤层会在到达 XFRM 前重建私有缓冲区,某些路径则把原页一路保留。

vmsplice() 相关工作负载应单列,因为用户页经 pipe 进入内核后仍可能由原进程观察。splice() 可以在 pipe 与 socket/文件之间移动页引用,sendfile() 则常把页缓存内容直接送向 socket。这些接口只是典型来源,不是完整列表;任何产生带共享标记非线性分片的机制都属于审计范围。

第四层是 ESP 是否会选择原地变换。检查供应商对 esp4.cesp6.c、硬件卸载和异步密码路径的改动。设备特性可能让某些流量线性化,也可能仍然把页分片直接交给算法。不能把一次设备型号测试外推到所有网卡、队列和 MTU 组合。

四项证据应保留为一条可复核记录:内核构建的来源与哈希、活动 XFRM/IP-TFS 配置快照、应用到 socket 的数据路径、ESP 在该构建上的可写性决策。只有第一项为真时,资产是“含脆弱代码”;四项组合成立时,才是优先验证的“可到达场景”。这种分层避免同时犯下过度恐慌和过早排除两种错误。

网络设备清单往往只列传统 VPN 网关,IP-TFS 还可能出现在实验流量整形器、隐私网络、边缘节点、内核自测宿主与云镜像模板中。搜索范围应从包名和设备角色扩展到配置生成器、基础镜像、内核模块、XFRM 自动化代码和基础设施仓库中的模式字段。

虚拟化环境需要确认问题发生在哪一层。来宾内核构建 IP-TFS 并执行 ESP 时,宿主版本不能代替来宾结论;宿主或网络命名空间承担隧道时,容器用户空间看到的内核版本又可能只是同一宿主。记录执行 xfrm_iptfs 与 ESP 代码的真实内核实例,比按业务容器数量重复统计更准确。

影响评级还要考虑原页承载什么。若是可重新生成的临时数据,结果可能表现为短暂完整性错误;若是页缓存中的持久文件、被多个消费者读取的消息或敏感明文,机密性与完整性后果都会放大。CVE 分数提供统一入口,业务优先级则必须加入数据角色与恢复成本。

不要把“需要本地发送者提供页”误写成纯本地低风险。网关常替远端租户、代理用户或内部服务转发数据,能够影响进入零拷贝路径的主体未必拥有网关管理权。具体威胁模型取决于谁能控制载荷、页来源与 IP-TFS 流量,不宜脱离部署给出统一攻击者身份。

反过来,也不能把公开结果扩大成任意远程代码执行。已核实的链条是共享页在发送变换中被错误原地写入,后果首先是错误范围的数据改写与潜在泄露。进一步影响需要结合页面内容、其他消费者行为和系统恢复逻辑单独论证,不能用推测替代证据。

调查员以四张证据卡核对内核版本、活动隧道、共享页来源和 ESP 写入分支,四条线汇合后才确认网关风险。
图 5:真正的暴露由四项事实汇合。任一证据缺失都应标记为“待核实”,让后续调查补齐事实,避免凭想象给出肯定或否定。

5.2 现场最有价值的不是异常密文包,而是来源对象的静默变化

网络侧抓包通常只能证明 ESP 输出看起来正常。包长、序号、认证标签与对端解密成功,并不会告诉分析员密文是否写在私有副本上。把“PCAP 未见异常”写成排除依据,会错过漏洞最核心的反向影响。

来源侧证据更直接。文件完整性系统若在 IP-TFS 发送活动附近记录页缓存内容异常、校验失败或刷新后差异,应把时间戳与 XFRM 计数器、源进程和发送文件关联。需要区分磁盘持久内容是否真的改变,以及只在内存页缓存阶段出现、重新读取后恢复的变化。

pipe 消费者可能报告不可解析块、随机高熵片段或协议校验失败。若同一生产者数据在普通 socket 发送时正常,只在经特定 IP-TFS policy 输出时异常,这种条件差异比单独一条应用错误更有价值。记录错误前后的偏移和长度,可与实际被聚合的分片边界比较。

内核未必崩溃,也不一定产生明确告警。原地加密写入的是有效映射页,访问本身可能完全合法;KASAN、页故障和普通越界检测不一定触发。没有 oops 只能说明没有显性的非法地址访问,不能证明所有权正确。

若系统使用文件完整性监控,先确认告警基于磁盘、页缓存还是应用读取结果。不同传感器观察的层次不同:磁盘哈希可能在脏页回写前保持不变,应用读取却已经看到改写;另一种情况下,页被标记为脏并最终影响持久内容。报告应忠实记录实际观察,不把一种机制的结果外推为全部。

性能指标也可能提供旁证。修复后,命中共享分片的流量会在写入前复制,CPU、内存带宽或分配计数可能相对脆弱快路径变化。这个变化是安全语义恢复的代价,不应在没有分析的情况下被性能回归系统判为“应撤销”。部署验证要同时看正确性与容量。

若要保全证据,优先记录运行内核构建 ID、模块哈希、启动参数、XFRM state/policy、接口与网卡特性、命名空间映射、相关进程及应用版本。只保存 uname -r 不足以在供应商回移场景重建事实。

保存可疑文件或 pipe 输出时,避免在原系统反复读取和重放流量,以免改变页缓存和队列状态。隔离副本、记录采集方式与时间,分别计算来源内容和外层报文的哈希。若涉及敏感数据,按现有取证权限与保留策略处理,取证目标本身不能成为新的暴露来源。

一次出现内容变化,也不能立刻全部归因于本 CVE。应用本身写入、文件系统回写、缓存失效、测试并发和硬件错误都可能改变内容。最有力的归因是时间上紧邻 ESP 变换,变化范围与被转移分片一致,并且只有缺少标记传播的构建出现。

处置团队可以把证据分成三档。确定:源码缺少传播且运行路径四项成立;很可能:版本和配置成立,但页来源或快路径尚未观测;待排除:仅版本可能受影响。分档帮助集中隔离与补丁窗口,同时避免把未知写成安全。

到这一步,资产与证据都已从模糊版本名单收窄为可执行队列。下一章回到补丁历史:为什么上游用一位 OR 完成修复,哪些稳定分支已经承接,以及供应商回移需要验证哪些实际语义,提交名只能充当检索坐标。

6 两行补丁修的不是性能分支,而是一次完整交接

6.1 读懂修复要看 OR、掩码、条件和顺序四件事

补丁第一层语义是 OR。目标 skb 可能已经聚合过别的来源,它的共享状态代表此前所有分片的最严格限制。用赋值覆盖会让一个后到的私有源抹掉早先共享源的事实;OR 只会增加约束,不会错误放宽。聚合对象的安全属性天然具有这种单调性。

第二层是掩码。源 flags 还可能包含与本次所有权问题无关的位,补丁只取 SKBFL_SHARED_FRAG。这避免把属于源对象其他状态的含义不加区分地移植到目标。安全修复既不能漏掉所需事实,也不能为了省事复制整个状态字。

第三层是 fromi->nr_frags 条件。只有确实移动了页分片,源的共享状态才应影响目标。空源对象可能经过复用或清理,flags 与实际集合不一定构成当前事实;把条件写在转移语义上,可以阻止“没有数据却传播限制”的假阳性。

第四层是顺序。补丁在 fromi->nr_frags = 0 之前读取并传播状态。代码由此形成一笔原子意义上的交接:源仍是集合所有者时取出限制,目标继承限制,随后源放弃成员。虽然没有新增锁或事务,这个顺序让单线程执行中的语义边界清楚可证。

四点共同说明,供应商回移不能只用“出现类似一行”判断。若代码在清零后依据源计数决定是否传播,条件可能永远不成立;若用普通赋值,混合多个来源时仍可能丢位;若无条件复制 flags,又可能改变空分片场景。等价修复必须在所有组合上保持同一不变量。

审查回移时可以构造一张四格矩阵。目标私有/源私有应保持私有;目标私有/源共享应变共享;目标共享/源私有应保持共享;目标共享/源共享仍共享。再为每格加入源零分片变体,验证目标不因空对象变化。矩阵比仅比较补丁文本更能发现重构分支中的语义偏差。

还要检查供应商是否把 iptfs_consume_frags() 拆成内联函数、批量转移或设备专用实现。补丁可能正确出现在上游同名函数,却绕不过另一个实际路径。搜索 nr_frags 清零、frags[] 批量复制和自定义 skb 拼接,比只搜提交哈希更稳妥。

修复没有改变 RFC 9347 线格式,也没有要求 IPsec 对端升级。IP-TFS 聚合和填充结果保持兼容,变化仅发生在本地写入前是否分配私有页。因此,隧道两端可以按各自维护窗口升级;但每个承担脆弱发送路径的端点都必须独立修复,不能靠对端已更新获得保护。

补丁也没有把所有分片强制复制。真正私有且满足其他快路径条件的 skb 仍然能够原地处理;只有被标记为共享的集合恢复写时复制。这使性能成本与实际所有权风险对应,避免用全局慢路径掩盖元数据缺陷。

修复后的额外复制不应被描述为回归本身。共享页若要被 ESP 修改,复制是保持原对象不变的必要成本。容量测试要确认平台在安全语义下仍满足服务目标,必要时扩容或调节流量;删除标记追求旧指标会重新破坏所有权。

内核安全补丁常因为行数少而被误认为容易验证。本例恰好相反:代码改变只有一位,正确性证明横跨页生命周期、聚合集合与密码写入。评审记录应说明为什么 OR、条件与位置都必要,让未来重构者知道这不是可以随意“整理”的冗余。

建议把注释和测试聚焦在不变量,而非 CVE 名称。比如:“移动任一共享分片后,目标必须继续报告 shared,直到建立私有副本。”这种表述在编号淡出维护视野后仍然有用,也能覆盖未来新的聚合帮助函数。

修复的最后一层意义,是把责任放回最了解事实的地方。IP-TFS 转移函数知道哪些分片从源进入目标,因此由它传播属性;ESP 只消费可靠属性决定可写性。让每层维护自己的事实,优于在下游猜测页最初来自哪里。

手绘三幕展示共享优化遗漏封印、调查员定位孤立封印、修复者在清空源托盘前把封印系到目标并分发到稳定版本。
图 6:历史不是“加入功能—发现漏洞—打补丁”三句话,而是一项性能交接从不完整变完整,并由主线向各维护分支复制同一语义。

6.2 上游版本是坐标,运行构建才是最后答案

Linux CNA 记录把受影响实现起点指向引入提交,并列出多个 git 版本范围。对普通运维最直观的上游结论是:功能随 6.14 时期进入,6.18 系列从 6.18.36 起修复,7.0 系列从 7.0.13 起修复,7.1 已包含主线修复。任何更晚稳定小版本也应继承对应分支的修复。

这些坐标应在发布当日重新核对官方记录,因为稳定分支会继续演进。报告保留提交哈希,是为了让读者在版本标签之外核实代码。只要目标构建在 iptfs_consume_frags() 中实现等价传播,就具有核心修复;反之,仅修改包版本字符串不能替代代码证据。

上游主线提交与稳定提交可能拥有不同哈希,这是正常的 cherry-pick/backport 结果。审计工具不应要求所有构建都包含 e9096a5a170e 这个对象,而要检查相应分支提交或源码性质。供应商公告若提供自己的漏洞编号,也应映射到同一函数和不变量。

若使用自编译内核,先确认源码树、配置、补丁集和实际启动镜像一致。常见失误是构建目录已有修复,机器却仍从旧的 EFI 条目或容器宿主内核启动。记录 uname、构建 ID、启动路径和映像哈希,重启后再次采集。

若模块形式部署 IP-TFS,更新模块但不更新与之耦合的内核接口可能带来兼容风险;若代码静态内建,单独替换模块当然无效。以发行版支持的完整内核包和重启流程为首选,除非供应商明确支持相应热补丁或模块替换方案。

热补丁是否适合这处修复,要看工具能否安全重定向目标函数并覆盖所有调用,同时不破坏正在处理的 skb。由于修复极小,技术上或许可行,但运维仍需供应商支持证明。没有这种证明时,热补丁只能作为经过验证的临时桥接,完整更新仍是最终状态。

集群升级应先挑选能够代表真实负载的金丝雀:包含 IP-TFS、相同网卡卸载、主要 MTU、IPv4/IPv6 和零拷贝发送。只选空闲备用机无法观察复制成本与数据不变量;只选最高负载机又会放大未知回归。代表性比单纯“小流量”更重要。

金丝雀前保存基线:隧道吞吐、CPU、软中断、分配、丢包、延迟、XFRM 错误、应用校验与原页完整性。更新后使用同一受控流量对比。共享页复制增加可能体现在 CPU 或内存带宽上,但任何来源内容变化都应视为失败。

回滚条件要区分安全与可用性。若新内核导致无法建立隧道、对端互通失败或稳定性严重下降,可以回滚到已评审的缓解状态;若只是共享流量的复制成本上升,不应直接回到脆弱代码,而应先调节容量或临时减少 IP-TFS 流量。

无法立即更新时,最直接的临时措施是停止在受影响端创建 IP-TFS 发送 state,改用经过风险评估的其他传输模式。这样会失去 IP-TFS 的流量分析缓解,且可能改变性能与隐私目标,所以需要业务和安全共同确认,并设置恢复修复模式的期限。

另一种临时措施是确保进入相关 socket 的数据已复制到私有缓冲区。它更难持续证明,也容易被框架优化或路径差异绕过,不应作为长期替代。若采用,必须在真实大文件、小消息和故障切换路径上验证,并监控系统调用或应用配置漂移。

更改硬件卸载或强制线性化也许会让 ESP 不再命中脆弱快路径,但副作用和覆盖面依实现而异。没有源码和运行证明时,这只能叫降低可能性,不能叫修复。安全通告应避免给出未经供应商确认的万能开关。

升级顺序可以按四项证据排序:已观察来源变化的资产最高;四项路径均成立但尚无异常的其次;含代码且配置可随时启用的模板与备用节点再次;仅版本疑似且无功能的构建仍要纳入最终清理。这样既处理眼前风险,也消除未来配置变化留下的隐患。

版本处置的终点不是工单写上“已安装”,而是运行构建被证明包含修复、活动隧道完成正反向互通、共享页在加密后保持不变、容量仍满足目标。下一章把这四个结论拆成一套可重复的实验,让验证者不必依赖破坏性利用。

7 不运行破坏性利用,也能把所有权不变量验证到底

7.1 先做源码证明:确认修复存在、位置正确、没有旁路实现

验证从目标机器实际使用的源码包或供应商可追溯构建开始。定位 iptfs_consume_frags(),不要先假设函数与上游完全同形。记录它取得源/目标 skb_shared_info 的方式、复制分片的位置、目标计数更新和源计数清零点。

第一条断言是:当源有分片时,目标 flags 通过 OR 继承源 flags 中的 SKBFL_SHARED_FRAG。若修复被封装成辅助函数,继续展开到最终写入;若供应商使用等价名称,按行为而非字符串判断。截图或差分应包含足够上下文,证明操作发生在同一次转移中。

第二条断言是:传播发生在源放弃成员之前。检查条件引用的是清零前的 nr_frags,没有因为重排而永远为零。若代码先缓存分片数再清零,使用缓存作为条件也可能等价,需要在评审记录中解释。

第三条断言是:目标既有共享状态不会被覆盖。对赋值表达式做数据流检查,确认没有稍后的 flags 初始化把 OR 结果清掉。某些函数会在构造 skb 时先复制模板状态,传播补丁若放得太早,后续初始化仍可能消除它。

第四条断言是:没有另一条分片搬运路径绕过该函数。搜索对 frags[]memcpy/memmove、源 nr_frags = 0、目标计数增加及 IP-TFS 专用助手。设备卸载或发行版性能补丁尤其可能复制上游早期实现形成分叉。

第五条断言是:ESP 仍然根据共享标记选择复制。检查 IPv4 和 IPv6 路径的 skb_has_shared_frag() 或等价判断。若供应商把逻辑移动到公共 helper 或设备驱动,确认 false/true 分支的含义未反转,也没有在检查前清理 flags。

第六条断言是:skb_has_shared_frag() 的定义与预期相同。它应在存在非线性数据并设置共享位时返回真。若厂商内核扩展了 flags 或改变非线性判定,需重新推导补丁是否仍覆盖实际页集合。

把六条结果写成证据表,每条标注文件、函数、构建、行号和结论。表格能让第二位评审者从同一代码重做判断,也能在下一次供应商更新后快速比较。单独一句“已含 CVE 修复”无法支持这种复核。

源码证明完成后,验证者已经知道“应该发生什么”。运行测试的任务不再是盲目寻找崩溃,而是观察同一事实在四个点是否连续:源带标记、目标继承、ESP 复制、原页不变。

受控验证台把文件页与 pipe 页做指纹留样,追踪共享封印经过 IP-TFS,确认 ESP 只把私有副本变成蓝色密文。
图 7:安全验证不追求破坏效果,而是同时保存原页指纹、标记轨迹和密文副本,证明“共享读取—私有写入”的完整过程。

7.2 再做受控实验:两种页来源、两种地址族、三种聚合顺序

实验必须在隔离主机或虚拟机中进行,使用专门构建、无生产数据的内核与网络命名空间。固定 CPU、MTU、网卡特性和 XFRM 配置,记录每次重启后的构建 ID。测试内容使用可公开的重复模式,避免任何真实文件、凭据或租户流量。

准备第一种来源:一个本地夹具文件,写入已知块并计算哈希。通过能够保留页缓存分片的发送方式把它送入 socket,同时保持另一读取者可在发送前后检查原内容。测试必须直接确认 skb 带有非线性分片与共享位,API 名称本身不足以证明。

准备第二种来源:匿名页经 vmsplice() 进入 pipe,由一个消费者保留观察能力,另一路继续送往 socket。仍然在进入 IP-TFS 前采集页内容指纹。两种来源覆盖页缓存与 pipe 所有权,能帮助区分只对单一生产机制有效的偶然结果。

配置最小的受控 IP-TFS XFRM state 和对端,先用普通私有缓冲区验证隧道互通。确认对端收到的明文与输入一致、计数器增加、没有重放或策略错误。只有基础网络可靠,后续来源变化才不会被错误归因于配置故障。

加入观测点一:在 iptfs_consume_frags() 入口记录源分片数和共享位。用调试构建、临时 tracepoint 或经过审核的 BPF 方法均可,具体方式服从内核版本。不要在通用生产机器上部署不匹配结构布局的探针。

观测点二位于转移之后。记录目标 nr_frags、目标共享位,以及源计数已清零。修复构建的核心断言是:只要转入的源集合共享,目标就为 true;脆弱构建可用于隔离对照,但不应连接任何真实业务。

观测点三位于 ESP 写入准备。确认共享目标触发复制路径,并记录复制前后页标识不同。页标识的采集需避免改变引用与时序;若无法安全观察物理页,可用调试断言和原内容哈希间接证明。测试工具本身不应成为新的共享写入者。

观测点四回到来源。ESP 完成和对端成功接收后,重新读取夹具文件与 pipe 保留内容,逐字节比较并复算哈希。修复构建中原内容必须不变,密文只存在于发送 skb 的私有存储。任何差异都要保存偏移、长度和对应分片边界。

分别运行 IPv4 与 IPv6 ESP。两条路径源码相似但文件不同,供应商回移也可能只改一边。双地址族验证能发现不完整回移;如果产品明确不编译某一地址族,可以在报告中以构建证据说明未测试原因。

聚合顺序至少覆盖私有源后接共享源、共享源后接私有源、多个共享源连续进入同一目标。三种顺序验证 OR 的单调性。目标一旦因任一来源变为共享,就必须保持共享,直到真正复制出私有集合,不能被后到私有分片“洗白”。

加入空源用例,构造没有实际 frags 但 flags 状态可控的内部夹具,确认它不会改变目标共享结论。该测试通常需要内核自测环境,不应通过不安全的用户输入伪造内核结构。它验证补丁中的 nr_frags 条件不是多余装饰。

改变聚合边界,让源分片恰好装满输出、部分留给下一包,以及多个内层包进入一枚外层包。边界用例能够触发不同消费分支,防止测试只覆盖完整转移的理想路径。每个输出都应追踪来源集合与最终共享状态。

改变 clone、frag_list 和线性化条件,验证即使其他原因已经迫使复制,标记仍正确传播。正确性不能依赖偶然走慢路径;同时,这些对照能解释为什么某些生产流量未出现来源变化,却仍运行脆弱代码。

容量测试用真实比例但合成内容运行一段稳定窗口,比较修复前后吞吐、延迟、CPU、内存与分配失败。测试目标是为上线准备足够容量,并准确量化安全复制成本。将共享流量与私有流量分开统计,才能看见复制只发生在需要的集合上。

故障测试覆盖隧道重建、策略切换、网卡重置与主备切换,确认新内核不会在状态变更中丢失属性或产生互通回归。每个场景仍以来源哈希不变为硬断言,不能因为网络恢复成功就省略所有权检查。

自动化结果应包含输入夹具哈希、内核/模块哈希、XFRM 配置、四个观测点、对端明文哈希和原页后验哈希。完整证据让审计者重放一次通过的结果,屏幕上的绿色标记仅作为显示。失败时也能定位是配置、路径还是不变量。

测试结束后销毁临时 key、namespace、夹具数据和调试探针,保留经过脱敏的日志与构建证据。不要把实验用低强度密钥或宽松 policy 留在共享环境。受控验证的安全性包括善后,而不只是测试过程没有攻击载荷。

当源码六条断言与运行四点轨迹都通过,修复结论才完整:状态传播正确,消费者采取正确行动,外部对象保持不变,协议功能仍然成立。最后一章把这些技术结果编排成事件响应与长期工程动作,让一位标记不再依赖某位维护者的记忆。

8 从发现一位丢失的标记,到关闭整条所有权链

8.1 事件响应按证据推进:先止住新写入,再更新、验证与复盘

响应的第一小时先确认谁在运行 IP-TFS 发送路径,而不是重启所有 Linux 主机、抹掉关键现场。团队应冻结相关配置变更,导出活动与待创建的 XFRM state/policy,记录网关、命名空间、地址族、对端、计数器和业务所有者;同时采集内核版本、构建 ID、启动镜像、模块哈希、发行版公告映射和 iptfs_consume_frags() 的源码性质。拿不到精确源码的资产保留为“待核实”,不能仅凭版本字符串擅自排除。

随后识别共享页来源,并检索来源侧异常。应用团队需要说明文件发送、pipe、代理缓存和高吞吐框架的实际数据路径,把“使用相关 API”与“本次流量确实产生共享非线性分片”分开记录;文件完整性、应用校验、pipe 消费错误、缓存异常和只在 IP-TFS policy 生效时出现的数据差异,则应与 XFRM 计数器及业务发送日志对齐。外层 PCAP 只能提供背景,不能代替来源对象的证据。

若已经观察到与链条吻合的内容变化,先隔离发送端与受影响数据对象,保存构建、配置、来源内容和外层报文,避免反复发送同一页扩大结果。若尚无异常但四项条件成立,首选把流量迁到已修复网关,或停止在该节点创建 IP-TFS 发送 state,并记录失去流量形状保护、容量变化、负责人、到期日和配置漂移监控。更新内核不会自动修复已经改变的内容,数据恢复与漏洞修复必须并行。

业务无法停用 IP-TFS 时,经过验证的应用私有复制或供应商支持的临时内核修复只能作为桥接,并且要写清覆盖的来源与分支。补丁准备阶段按“已出现异常、四项条件齐全、配置可能启用、仅代码疑似”建立批次,把备用节点和镜像模板一并纳入;部署前确认 masked OR、非零条件、清零前顺序和目标旧状态保留,并检查产品启用的 IPv4/IPv6 ESP 路径仍消费共享位。最终状态仍是受支持、含等价属性传播的内核。

金丝雀更新后先验证启动与隧道健康,再运行共享页夹具。只有对端明文正确、原页不变、目标共享位保持、ESP 在私有页上写入四项同时成立,才扩大批次。若安全复制把 CPU 或内存带宽推近上限,应降低单节点负载、增加实例或调整调度;上线期间持续观察 XFRM 错误、重放、丢包、延迟、软中断、分配失败和应用内容校验,等待窗口由真实业务周期与主备切换决定。

互通故障先从策略、密钥、模块和网卡兼容性查起,因为修复不改变线格式;必须回滚时,只能回到已明确禁用风险路径的状态。全量安装后仍要逐台核对实际运行哈希,找出未重启宿主、旧启动项、脱离编排的边缘节点和旧基础镜像。恢复临时功能也应一次只撤一项,并在修复节点上重新确认隐私、性能与数据不变量。

可能被改写的数据要按来源恢复:持久文件与可信副本比较,pipe 或消息流按业务语义决定是否重放,缓存从权威源重建。通知只陈述已经建立的条件——特定 XFRM IP-TFS 分片转移遗漏共享标记,可能让 ESP 原地变换外部仍可见的页——既不扩写成所有 IPsec 流量的远程执行,也不淡化成性能问题。最后把优化引入、功能部署、资产核实、临时控制、补丁验证、数据恢复和旧镜像清理串成一条可复核时间线,关闭条件必须能由另一位工程师重做。

手绘闭环把资产盘点、临时绕行、内核修复、受控验证、分批网关上线和证据归档用一条红线串联。
图 8:响应不是“装包即结束”。只有资产、缓解、修复、验证、扩展和证据归档全部闭合,IP-TFS 才重新成为可信的加密发送链。

8.2 长期工程动作:让元数据与数据一样拥有不可丢失的迁移规则

长期治理先从 skb 分片移动的属性矩阵开始。每个会转移、合并或拆分 frags[] 的 helper,都应同时验证共享位、校验和状态、truesize、释放责任和目标计数,而不是只比较载荷字节。集合属性还要有明确的合并规则:只要一个成员仍被外部共享,目标集合就必须保留这一限制;不同 flags 即使存放在同一整数中,也不能默认采用同一种代数。

这种契约还要进入 IP-TFS 自测。除了线格式、分片重组和吞吐,夹具应覆盖页缓存、pipe、混合私有与共享来源、IPv4/IPv6 以及不同聚合边界,并把“对端收到正确明文”和“来源页保持原样”设为两个独立断言。前者证明功能成功,后者才证明零拷贝没有越过所有权界线。

代码评审则要把所有权问题放进每一次性能优化:谁仍能观察数据,第一次写发生在哪里,什么状态迫使复制,这个状态如何穿过每次交接。消费者若确实拥有相同语义,应尽量共用判定 helper;但接口仍须区分 skb clone、非线性页共享和设备专用可写布局。关键 flags 的注释也应描述这种所有权约束,而不是只列举某个历史校验和用例。

同样的思路可以用于缺陷类扫描:寻找移动分片并清零源计数、却没有传播集合状态的函数,以及原地写入前只检查 clone、忽略外部页共享的路径。扫描只能提供人工复核的入口,是否可达、输入是否共享、后续是否真的写入,仍需要结合上下文证明,不能由规则命中直接替代结论。

处置速度取决于平时留下的证据。资产系统除内核版本外,还应保存供应商补丁集、构建 ID、IP-TFS 编译状态、活动配置来源和最近一次不变性测试;一套常驻且不含敏感数据的实验室,则用页缓存与 pipe 两种来源、IP-TFS 对端、双地址族、可控 MTU 和安全观测点,为每次内核更新提供可重复的答案。

修复后的性能变化也要提前纳入容量预算。零拷贝不等于永不复制:当仍被外部观察的数据即将修改时,复制本身就是正确性成本。验收不能只看隧道形状、对端内容或吞吐中的某一项,而应同时确认 IP-TFS 的隐私目标、对端明文和本地来源完整性;否则一个看似漂亮的性能数字仍可能掩盖错误的所有权契约。

这类改动还需要跨子系统评审。IP-TFS 维护者理解聚合,网络核心维护者理解 skb 所有权,密码与 XFRM 维护者理解实际写入;这些视角应在设计阶段相遇。回归测试也要把负面断言当成一等公民,用稳定指纹证明原页未变,同时确认写入确实发生并覆盖足够路径,让 CI 在没有崩溃或攻击载荷的情况下也能发现语义回退。

组织层面可以定期演练从 CVE 记录映射到运行构建,要求团队在限定时间内找出受影响配置、源码性质和验证夹具。回移说明应保留修复语义,而不只保存提交哈希:无论分支代码如何演进,“在清零源分片计数前,把 shared 位 OR 到目标”仍能指导老基线实施等价修复,也给审计者留下稳定的证明目标。

最后,指标和维护文档都要记录这条完整因果链。隧道成功率、对端校验和与吞吐在脆弱版本上仍可能正常,因此回归和金丝雀阶段需要来源页哈希、写时复制观测和缓存内容异常等反向信号;缺陷记录也不应止于“漏传一个 flag”,而要写清明文页如何从文件或 pipe 进入 IP-TFS、怎样丢失限制、为何被 ESP 误判,以及补丁如何恢复交接。

CVE-2026-53363 最终守住的并非某一枚报文,而是一条朴素原则:零拷贝可以共享存储,却不能共享掉责任。数据描述符、生命周期和可写性必须一起穿过每次交接;当最后一个消费者准备落笔时,它需要知道手里的究竟是副本,还是仍属于别人的原件。

回到开头那次“成功”的发送,修复后的结果终于在隧道内外一致:对端得到正确明文,外层网络只见合规密文,原文件页与 pipe 内容保持原样。没有额外警报,也没有戏剧性崩溃,只有那枚不起眼的共享标记按时抵达——而这正是内核安全最可靠的样子。

研究记录

9证据、对象与来源

下面保留本文实际使用的标识、时间和原始材料,便于继续调查。

9.1研究对象

报告涉及的产品、行为者、技术、受影响对象和控制点。

漏洞编号CVE-2026-53363

Linux XFRM IP-TFS 共享页分片标记传播缺失

关键函数iptfs_consume_frags

移动页分片描述符的函数

关键标记SKBFL_SHARED_FRAG

决定写入前是否需要复制的所有权状态

引入提交b96ba312e21c9b7ac1526829b9640ddc06695c0b

IP-TFS 页分片共享优化

主线修复e9096a5a170e7ecd6467bc2e08668ec39897cda7

传播共享分片标记

成立条件IP-TFS output + shared nonlinear fragments + ESP in-place transform

共享页进入 IP-TFS 后被 ESP 误判为私有可写

9.2事件时间

  1. 零拷贝优化合入

    引入提交让 IP-TFS 输出共享内层 skb 的页分片。

  2. 主线修复落地

    修复在源分片计数清零前把共享标记 OR 到目标 skb。

  3. CNA 记录公开

    Linux CNA 发布 CVE-2026-53363 与受影响版本映射。

  4. SOSEC 完整复核

    完成两种 ESP 路径、稳定分支与受控验证方案复核。

9.3来源与材料

  1. CVE.org:CVE-2026-53363https://www.cve.org/CVERecord?id=CVE-2026-53363
  2. Linux 修复提交 e9096a5a170ehttps://git.kernel.org/stable/c/e9096a5a170e7ecd6467bc2e08668ec39897cda7
  3. Linux 6.18 稳定分支修复 dd66f7f6https://git.kernel.org/stable/c/dd66f7f6e360ee82cd905517726f8e9091265de5
  4. Linux 7.0 稳定分支修复 c885d111https://git.kernel.org/stable/c/c885d111ed9f5a0a1f3cc4e87a50db6518abaa6c
  5. 引入页分片共享的提交 b96ba312e21chttps://git.kernel.org/stable/c/b96ba312e21c9b7ac1526829b9640ddc06695c0b
  6. RFC 9347:IP Traffic Flow Securityhttps://www.rfc-editor.org/rfc/rfc9347.html
  7. Linux 内核文档:struct sk_buffhttps://docs.kernel.org/networking/skbuff.html
  8. Linux 内核文档:XFRM devicehttps://docs.kernel.org/networking/xfrm_device.html
  9. Linux 上游:net/xfrm/xfrm_iptfs.chttps://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/xfrm/xfrm_iptfs.c
  10. Linux 上游:include/linux/skbuff.hhttps://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/skbuff.h
  11. Linux 上游:IPv4 ESP 输出https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/esp4.c
  12. Linux 上游:IPv6 ESP 输出https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv6/esp6.c
  13. Linux 上游:sk_buff 核心实现https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/skbuff.c
  14. Linux man-pages:vmsplice(2)https://man7.org/linux/man-pages/man2/vmsplice.2.html
  15. Linux man-pages:sendfile(2)https://man7.org/linux/man-pages/man2/sendfile.2.html
  16. MITRE CWE-668:资源暴露给错误范围https://cwe.mitre.org/data/definitions/668.html