2026 年 6 月补丁日,微软在 HTTP.sys 内核驱动中修复了一个 CVSS 9.8 的整数溢出漏洞。ZDI 随后于 7 月 9 日发布深度分析,奇安信独立完成了 PoC 复现,确认该漏洞可在特定配置下从远程无凭据触发内核级代码执行。漏洞的核心并非复杂的协议逻辑,而是一处被长期忽视的 16 位无符号整数回绕:当一个本应单调增长的容量计数从 0xFFFB 加 5 回绕到 0x0000 后,紧接着的内存分配按 0 容量计算,却把约 512KB 的历史数据 memmove 进 40 字节的池块里。

本文按数据流自顶向下拆解整条攻击链:HTTP.sys 的缓冲区引用数组机制、整数回绕点、池溢出的内存布局、TLS 分 Record 的触发路径,以及防御侧的修复与检测思路。


一、漏洞概述

项目 内容
CVE 编号 CVE-2026-47291
CVSS 3.x 9.8(Critical,AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)
漏洞类型 整数溢出(CWE-190)+ 内核池堆溢出(CWE-122)
受影响组件 Windows HTTP.sys 内核态 HTTP 协议栈驱动(http.sys
触发向量 远程、未认证、仅依赖网络可达的 HTTPS 服务
补丁 2026 年 6 月补丁日
公开分析 ZDI 于 2026-07-09 发布深度分析
PoC 奇安信已成功复现(蓝屏路径)

该漏洞的攻击面集中在 HTTP.sys 对"单 HTTP 请求内大量头部行"的缓冲区管理上。由于缓冲区引用数组的容量字段被声明为 16 位无符号整数,攻击者通过在单个 TLS 连接中持续投递大量小记录,迫使容量字段越过 0xFFFF 边界回绕为 0,进而让扩容路径分配出远小于实际数据量的池块,造成超过 500KB 的非分页池溢出。


二、HTTP.sys 架构基础

HTTP.sys 是 Windows 的内核态 HTTP 协议栈驱动,承担 IIS 与 HTTP Server API(Http* 系列函数)的底层请求接收与解析工作。它常驻内核,直接挂钩 TDI/WFP 层收包,并在内核态完成 HTTP 请求行的切分、头部解析与请求对象构造,再把就绪的请求对象派发给用户态的工作进程(w3wp.exe)。

把请求接收放到内核里有明确的性能动机:避免每个连接的用户态/内核态上下文切换,由内核统一管理连接复用与请求队列。但代价同样明显——任何在内核态解析路径上的内存破坏漏洞都直接危及整个内核,权限边界从"应用进程"退化为"SYSTEM 内核态"。

        用户态                              内核态
+-----------------------+        +----------------------------------+
|  w3wp.exe (IIS 工作进程)|  <----+  HTTP.sys  请求队列 / 请求对象     |
|  应用层业务处理        |        |   ^  HTTP 头部解析、缓冲区管理     |
+-----------------------+        |   |                                |
                                 |   |  SChannel 内核态 TLS 解密        |
                                 |   |   ^  application data record     |
+-----------------------+        |   |   |  (逐 record 解密并交付)       |
|  客户端浏览器/curl     | ======>+   |   |                               |
|  TLS 1.2/1.3 over TCP |        |   v   v                              |
+-----------------------+        | TCP/IP 栈 (WFP/TDI 收包)            |
                                 +----------------------------------+

请求缓冲区管理是 HTTP.sys 的核心职责之一。对于单个 HTTP 请求,驱动需要为每一个接收到的数据片段(通常对应一个 TLS application data record 解密后的一段明文)维护一条引用记录,记录其指针与长度,供后续头部解析与请求体重组使用。这些引用被组织成一个"缓冲区引用数组",即漏洞发生的数据结构。


三、漏洞根因深度分析

3.1 缓冲区引用数组机制

HTTP.sys 为每个请求对象维护一个缓冲区引用数组,数组里每个槽位存放一个指向已接收缓冲区的指针(及其元数据,单个槽位占 8 字节量级)。数组本身用两个字段描述其状态:

  • capacity(容量):数组当前可容纳的槽位数,16 位无符号整数(unsigned __int16)。
  • count(已用计数):当前已写入的槽位数,同样为 16 位无符号整数。

当一个新的缓冲区片段到达、需要登记进引用数组时,驱动检查 count >= capacity:若已满,则执行一次扩容,将 capacity 增加 5 个槽位,并分配一个新的、更大的数组,把旧数组内容整体 memmove 过去。这是一个经典的"几何/线性增长数组"模式,逻辑上没有问题——问题出在描述容量的字段宽度上。

请求对象 (request_object)
+----------------------------------------------+
  ...                                          ...
  +0x640  capacity  : unsigned __int16   <-- 16 位容量字段
  +0x642  count     : unsigned __int16   <-- 16 位已用计数
  ...
  buf_ref_array --> [槽0][槽1]...[槽 capacity-1]
                      |     |
                      v     v
                    缓冲区指针/元数据 (每槽 8 字节)

3.2 整数溢出触发

16 位无符号整数的表达范围是 0x0000 ~ 0xFFFF(0 ~ 65535)。capacity += 5 是无回绕检查的普通加法,一旦容量逼近上界,加 5 就会越过 0xFFFF 并按 16 位宽度截断:

capacity 变化轨迹(每次扩容 +5,仅列关键点):

 0x0005  ->  0x000A  ->  0x000F  -> ... -> 0xFFF6 -> 0xFFFB
   (5)       (10)       (15)              (65526)  (65531)

最后一次扩容:
 0xFFFB + 5 = 0x10000
                |
                v  16 位截断(保留低 16 位)
              = 0x0000   <-- capacity 被写回为 0

从初值增长到 0xFFFB 大约经历 13,106 ~ 13,107 次扩容,这意味着攻击者需要向单个请求注入约 6.5 万个独立的缓冲区片段。到达 0xFFFB 后的下一次扩容,capacity += 5 在 16 位语义下等于 0,而 count 此刻已达到约 65,536(因为 count >= capacity 才触发扩容,且每一轮扩容的同时 count 也在随记录数增长)。

关键在于:截断发生在写入回请求对象之前,但后续所有依赖 capacity 的计算都使用了这个被截断的 0。

3.3 堆溢出触发

扩容时的内存分配大小按"固定头部 + 容量 × 每槽字节数"计算:

alloc_size = 0x28 + capacity * 8
           = 0x28 + 0 * 8
           = 40 字节        <-- 实际分配

固定头部 0x28(40 字节)用于数组自身的管理元数据,每个槽位 8 字节。capacity 已被截断为 0,于是 ExAllocatePool2 只从非分页池里切出 40 字节。然而紧随其后的 memmove 复制量按 count 计算,而非被截断的 capacity

copy_size = count * 8
          ≈ 65536 * 8
          = 524288 字节 (约 512KB)

约 512KB 的数据被写进一个 40 字节的池块,溢出量超过 524KB,足以覆盖后续多个非分页池块及其相邻的内核对象。

// 漏洞路径(简化自 HTTP.sys v10.0.26100.7705 的 UlpParseNextRequest)
// 请求对象 +0x640 处保存 capacity(unsigned __int16)
unsigned __int16 capacity = ReqCapacity(req);   // *(uint16*)(req + 0x640)
unsigned __int16 count    = ReqCount(req);      // 已用槽位数

if (count >= capacity) {            // 触发扩容
    capacity += 5;                  // ★ 无回绕检查:0xFFFB + 5 == 0x10000 -> 截断 0x0000
    *(unsigned __int16*)(req + 0x640) = capacity;   // 把 0 写回

    size_t alloc_size = 0x28 + (size_t)capacity * 8; // = 0x28 + 0 = 40 字节
    void  *new_buf = ExAllocatePool2(POOL_FLAG_NON_PAGED,
                                    alloc_size, 'psPH');   // 仅分配 40 字节

    // ★ 复制约 512KB 到 40 字节分配中 -> 内核非分页池溢出
    memmove(new_buf, old_buf, (unsigned __int64)count * 8);
}

溢出在内核非分页池中的内存布局:

非分页池 (NonPaged Pool)
+-------------------+ <- new_buf (ExAllocatePool2 返回,40 字节)
| 头部 0x28 (40B)    |   <-- 仅这一段是合法分配
+-------------------+ <- 合法边界 (offset 0x28)
| 溢出数据 槽0..N     |   <-- memmove 写入,远超 40 字节
| (约 524KB ...)     |
+-------------------+
| 相邻池块 A         |   <-- 被覆盖:池元数据 / 对象 vtable 指针
+-------------------+
| 相邻池块 B         |   <-- 被覆盖
+-------------------+
| ...               |   <-- 连续破坏,触发 PAGE_FAULT_IN_NONPAGED_AREA

3.4 关键代码位置

据 ZDI 分析,漏洞出现在 HTTP.sys v10.0.26100.7705UlpParseNextRequest() 路径中,核心是两行无防护操作:

  • 容量自增写回:*(unsigned __int16*)(request_object + 0x640) = capacity + 5; —— 对 16 位加法结果不做回绕检查,直接写回请求对象。
  • 数据搬迁:memmove(new_buffer, old_buffer, (unsigned __int64)count * 8); —— 复制量由 count 决定,而分配量由被截断的 capacity 决定,两者脱节导致溢出。

两行代码本身都不复杂,但组合起来构成了"分配按 0、复制按 count"的严重失配。根因是 capacity 这个本应单调增长、且语义上远大于 16 位表达范围的字段,被错误地用 16 位类型承载。


四、TLS 分 Record 机制与触发路径

4.1 为什么需要 TLS

触发该漏洞的现实障碍在于:HTTP.sys 的缓冲区引用数组是"每接收一个独立缓冲区片段"增长一项,而普通的明文 HTTP 请求里,多个头部行通常被打包进同一个 TCP 段、同一次接收,只会产生少量缓冲区引用,远达不到 6.5 万的量级。TLS 的分层 record 结构恰好提供了"逐片段交付"的能力。

TLS 把应用层数据切分成一个个 application data record,每个 record 独立加密、独立带 MAC/AEAD 标签。SChannel 在内核态逐 record 解密后,会把每个 record 的明文作为独立的数据块交付给 HTTP 解析器。这天然形成了一条 1:1 的对应关系:一个 TLS record 的明文 -> 一次 HTTP.sys 缓冲区登记 -> 引用数组里一个新槽位。

TLS 流(单连接内大量小 record)
+---------+ +---------+ +---------+     +---------+
| record1 | | record2 | | record3 | ... | recordN |
| "X-1:..| | "X-2:..| | "X-3:..|     | "X-N:..|
|  CRLF  | |  CRLF  | |  CRLF  |     |  CRLF  |
+---------+ +---------+ +---------+     +---------+
     |           |           |               |
     v           v           v               v
  SChannel 逐 record 解密,逐块交付
     |           |           |               |
     v           v           v               v
  HTTP.sys 缓冲区引用数组: [槽1][槽2][槽3] ... [槽N]
                          count: 1 -> 2 -> 3 -> ... -> N
                          capacity: 5 -> 10 -> 15 -> ... -> 0xFFFB -> 0x0000 (溢出)

只要攻击者把每个 HTTP 头部行单独封装进一个 TLS record 发送,就能让 countcapacity 按预期增长,最终命中回绕点。

4.2 触发条件

要走到整数回绕,需要同时满足以下条件:

条件 要求 说明
服务暴露 目标启用 IIS / HTTPS,HTTP.sys 处理 TLS 卸载 内核态解析路径生效
请求大小上限 MaxRequestBytes 注册表值 ≥ 262144 字节 默认 16384 不足以容纳 6.5 万行头部
记录数量 累积 65536+ 个 TLS application data record 越过 0xFFFF 回绕点
协议版本 HTTP/1.x over TLS HTTP/2、HTTP/3 走二进制分帧/QUIC,解析路径不同
连接稳定性 单连接持续约 11 分钟 每记录 ~10ms 节流,避免被重传/拥塞打断

MaxRequestBytes 是关键门槛。它位于注册表 HKLM\System\CurrentControlSet\Services\HTTP\Parameters\MaxRequestBytes,默认值 16384 字节会先于溢出点触发"请求头过大"拒绝,从而阻断攻击;只有当管理员把它上调到约 256KB 以上时,6.5 万行头部才能被容纳,漏洞路径才可达。

4.3 攻击时序图

时间轴:
0s ──────────────────────────────────────────────────────── 660s (~11 分钟)
|                                                              |
|  record 1     record 2          record 65536+                 |
|  (HTTP头1)    (HTTP头2)         (HTTP头65536+)                |
|                                                              |
|  capacity: 5 -> 10 -> 15 -> ... -> 0xFFFB -> +5 -> 0x0000     |
|              (每接收若干记录触发一次扩容)                       |
|                          |
|                          v  回绕发生
|                   ExAllocatePool2(40 字节)
|                   memmove(~512KB)  ->  内核非分页池溢出
|                          |
|                          v
|                   PAGE_FAULT_IN_NONPAGED_AREA / 蓝屏 (默认)
|                   或特定布局下 -> 内核 RCE

按每 10ms 一个记录的节流,6.5 万个记录约需 655 秒(约 11 分钟)持续连接。节流是为了规避接收窗口、TCP 拥塞控制与 TLS record 速率限制,保证整条流稳定送达。


五、利用分析

5.1 默认影响

在不做任何额外内存布局操控的情况下,溢出直接破坏非分页池中相邻的池块与池元数据。内核在后续访问被破坏的池块(释放、合并或解引用其内部指针)时触发 PAGE_FAULT_IN_NONPAGED_AREA,系统蓝屏重启。换言之,漏洞的"开箱即用"效果是远程拒绝服务:一个未认证的网络连接即可让目标服务器整机崩溃。

5.2 RCE 条件

从池溢出升级到内核级代码执行,需要满足额外的内存布局前提:

  • 溢出能精确覆盖某个含函数指针(如虚表、回调、I/O 完成例程)的相邻内核对象。
  • 攻击者能通过堆风(heap feng shui)控制被覆盖对象的位置与时机,使受控数据落在函数指针字段。
  • 在该函数指针被解引用时跳转到可控地址,最终完成内核态执行并提升到 SYSTEM。

由于溢出量大(>500KB)、连续覆盖多个池块,理论上为布局操控提供了较宽的"涂改"窗口,降低了精确命中的难度。但整体利用仍属高难度,依赖具体版本的池分配器行为与对象排布。

5.3 利用限制

  • 必须是非默认的 MaxRequestBytes 配置(≥256KB 量级),现实中这类调优多见于高负载 IIS 或某些反代后端。
  • 需要长达约 11 分钟的稳定 TLS 连接,长连接与流量异常容易被边界设备观测到。
  • 仅 HTTP/1.x over TLS 可触发;HTTP/2 的 HPACK 头部压缩与多路复用、HTTP/3 的 QUIC 帧解析都走不同代码路径,不受此漏洞影响。
  • 节流速率若过快会被 TCP/TLS 层反压打断,过慢则进一步拉长攻击窗口。

六、PoC 复现方法论

6.1 环境准备

复现蓝屏路径需要一台可控的 Windows Server:

  1. 安装并启用 IIS,绑定 HTTPS 站点(自签名证书即可,复现不校验)。
  2. 调整注册表放宽请求大小上限:
    HKLM\System\CurrentControlSet\Services\HTTP\Parameters\MaxRequestBytes
    类型: DWORD
    取值: 0x00100000 (1048576,1MB,确保 > 262144)
    
    修改后重启 HTTP 服务(net stop http / ynet start http,或重启系统)使配置生效。
  3. 关闭防火墙对 443 的拦截,确认从攻击机能建立 TLS 握手。
  4. 建议在虚拟机快照状态下操作,蓝屏不可逆。

6.2 PoC 脚本

下面的 Python 脚本是触发路径的概念演示,仅发送逐 record 的头部行以推高 count,不包含任何内核布局操控或 RCE 载荷,复现结果是蓝屏。

import ssl
import socket
import time

# PoC 概念演示(非完整 exploit,仅复现蓝屏路径)
RECORD_COUNT = 65540          # 略超 0xFFFF 回绕点
INTERVAL_MS  = 10             # 每记录间隔,规避反压

def trigger_overflow(target, port):
    context = ssl.create_default_context()
    context.check_hostname = False
    context.verify_mode   = ssl.CERT_NONE   # 复现环境不校验证书

    sock  = socket.create_connection((target, port), timeout=30)
    ssock = context.wrap_socket(sock, server_hostname=target)

    # 请求行,开始一个 HTTP/1.1 请求
    ssock.send(b"GET / HTTP/1.1\r\n")

    # 逐记录投递头部行:每次 send 通常生成一个独立的 TLS application data record
    for i in range(RECORD_COUNT):
        header = f"X-Pad-{i}: {'A' * 10}\r\n".encode()
        ssock.send(header)            # 1 次 send -> 1 个 TLS record -> 1 个缓冲区引用
        time.sleep(INTERVAL_MS / 1000)

    ssock.send(b"\r\n")               # 结束 HTTP 请求头
    try:
        ssock.recv(1024)
    except Exception:
        pass
    ssock.close()

if __name__ == "__main__":
    trigger_overflow("192.168.1.10", 443)

ssl 模块基于 OpenSSL,对每次 send 调用默认会封装为一个独立的 TLS record(受 SSL_OP_* 与内部缓冲影响,实际 record 边界由底层协商决定;为确保 1:1,可进一步用 ssock.setsockopt 或直接操作 record 边界的 BIO,但概念验证层面单次 send 已足够稳定地命中)。

6.3 验证方法

  • 蓝屏监控:目标机出现 PAGE_FAULT_IN_NONPAGED_AREA(错误码 0x50),且故障模块为 HTTP.sys,即可初步确认命中。
  • 内核调试:通过 WinDbg 串口/网络内核调试附加,在 UlpParseNextRequestExAllocatePool2 下断,观察 capacity0xFFFB 跃变为 0x0000、分配大小为 0x28memmove 长度为 ~0x80000(512KB)的失配现场。
  • 池标记核对:!pool used!poolfind 检查 'psPH' 池标记对应的 40 字节块被越界写入的痕迹。

七、修复方案与防御

7.1 官方修复

安装 2026 年 6 月补丁日更新。修复方向是把 capacity 从 16 位提升到 32 位(ULONG),并在扩容前加入显式回绕检查,使分配量与复制量重新由同一个未被截断的容量字段决定。

// 修复方案(示意):capacity 提升为 32 位 + 扩容前溢出检查
ULONG capacity = ReqCapacity32(req);   // *(ULONG*)(req + 0x640)
ULONG count    = ReqCount(req);

if (count >= capacity) {
    if (capacity > MAXULONG - 5)            // 显式溢出检查
        return STATUS_INTEGER_OVERFLOW;
    capacity += 5;
    *(ULONG*)(req + 0x640) = capacity;

    size_t alloc_size = 0x28 + (size_t)capacity * 8;   // 容量与分配重新一致
    void  *new_buf = ExAllocatePool2(POOL_FLAG_NON_PAGED, alloc_size, 'psPH');
    if (!new_buf)
        return STATUS_INSUFFICIENT_RESOURCES;

    memmove(new_buf, old_buf, (SIZE_T)count * 8);      // 此时 alloc_size >= count*8
}

7.2 临时缓解

在无法立即打补丁的环境中,保持 MaxRequestBytes ≤ 65535 字节即可切断触发链(默认 16384 已天然安全)。该值位于:

HKLM\System\CurrentControlSet\Services\HTTP\Parameters\MaxRequestBytes

确认该 DWORD 值未被运维或某些应用调大到 256KB 以上,是成本最低的有效缓解。

7.3 流量检测

检测维度 方法 阈值参考
解密检测 统计单个 HTTP 请求的头部行数 > 1000 行视为可疑
加密启发式 统计单 TLS 连接内小 application data record 数 > 1000 个小记录
连接时长 监控单连接持续时间 > 11 分钟的稳定长连接
record 大小分布 检测大量近乎等长的小 record record 净荷 < 64 字节且高度密集

加密流量无法直接看到 HTTP 头部内容,但"单连接内大量小 record"这一外发特征在流量层清晰可见,是 TLS 检测设备可以落地的启发式规则。

7.4 深度防御

  • WAF 规则:在前端限制单请求 HTTP 头数量与请求头总大小,在到达 HTTP.sys 之前截断。
  • TLS 中间盒解密检查:对出站/入站 TLS 流量解密后检测异常头部模式,兼顾隐私与安全时仅统计元数据。
  • 内核池保护:启用 PoolGuard / PoolCorruptionGuard 等内核池完整性校验,在溢出发生瞬间即触发检测而非等到蓝屏。
  • 纵深监控:对 HTTP.sys 模块崩溃、0x50 蓝屏建立告警关联,缩短从被攻击到发现的时间窗口。

八、整数溢出漏洞的通用启示

8.1 16 位整数溢出模式

16 位字段之所以危险,在于其上限仅 65535,对于任何"随用户输入线性增长"的计数器都属于可枚举范围。与更宽字段相比,16 位回绕所需的输入量低到攻击者可在分钟级别内构造完成。

CVE 组件 溢出字段宽度 溢出根因 主要影响
CVE-2026-47291 HTTP.sys uint16 capacity capacity += 5 回绕为 0 非分页池溢出,内核 RCE
CVE-2015-1635 HTTP.sys 64 位 Range 端点 Range 端点累加溢出导致越界读 内核整页读取/DoS
CVE-2020-0796 SMBv3 srv2 32 位偏移/长度 解压长度计算溢出 内核池溢出,RCE
CVE-2022-21907 HTTP.sys 计数 RST/计数处理失配 内存破坏,RCE

横向看,HTTP.sys 这类内核态协议栈是整数溢出的重灾区:协议字段多、计数器多、且常常直接来自远端输入。CVE-2015-1635 与 CVE-2026-47291 相隔多年却同源——都是"描述长度的字段在算术运算后失去语义",说明这类缺陷具有反复复发的结构性特征。

8.2 C 语言类型安全

C/C++ 中无符号整数运算的溢出是静默的:0xFFFB + 5unsigned __int16 语境下合法地等于 0x0000,编译器不报警,运行时不抛异常。这正是该类漏洞难以在常规测试中暴露的原因。可叠加的工程化手段包括:

  • 编译期选项:/GS(栈保护)、/sdl(安全开发生命周期检查)、/Qfast_transcendentals 之外的整数溢出相关检查;MSVC 的 /fsanitize=address 与 Clang 的 -fsanitize=integer,unsigned-integer-overflow 可在测试期捕获回绕。
  • 静态分析:启用 /analyze,配合 SAL 标注(_In_range__Out_range_)约束字段取值域。
  • 运行期检查:对长度/计数类运算统一走 RtlULongAddRtlSizeTMult 等带 STATUS_INTEGER_OVERFLOW 返回的安全整数 API,禁止裸算术。

8.3 内核开发安全

针对内核态开发,几条经验值得固化为编码规范:

  • 永远不信任来自远端的长度与计数字段;任何"用户可控、单调增长"的计数器都应假定会触及上限。
  • SIZE_T / ULONG_PTR 等指针宽度类型承载"可能很大"的容量与长度,而非 16 位类型——16 位只有在确证范围受限(如协议字段宽度)时才使用。
  • 扩容分配与复制必须使用同一个未截断的源字段,且分配大小严格不小于复制大小;二者解耦是这类溢出的直接成因。
  • 对几何/线性增长数组,优先使用既经审计的容器实现,而非每次手写 capacity += N + memmove

九、攻击面分析

HTTP.sys 长期暴露在内核态、直接处理远端不可信输入,是 Windows 内核远程攻击面中权重最高的组件之一。它常驻内核、解析复杂的文本协议、维护大量动态数据结构,且其崩溃直接等同于系统崩溃,因此任何一处内存破坏都会从"应用 DoS"放大为"内核 DoS/RCE"。

CVE 年份 触发点 类型 默认影响
CVE-2015-1635 (MS15-034) 2015 Range 头整数溢出 整数溢出/越界读 DoS / RCE
CVE-2021-31166 2021 HTTP 头解析内存破坏 内存破坏 DoS / RCE
CVE-2022-21907 2022 RST 与计数处理 内存破坏 RCE
CVE-2026-47291 2026 缓冲区引用数组 capacity 回绕 16 位整数溢出/池溢出 DoS / RCE

从这张时间线可以看到,HTTP.sys 的攻击面在过去十年里持续被不同维度的缺陷刷新:Range 头、头部解析、连接状态机、再到本次的缓冲区引用数组。CVE-2026-47291 的特别之处在于,它不是协议语义层面的复杂逻辑错误,而是一处纯粹的类型宽度误用——一个本该用更宽类型承载的计数器,被声明成了 16 位。这类缺陷修复成本低(一处类型提升 + 一行回绕检查),但发现成本高,往往要等到攻击者把计数推到上界才会显形。

对防御方而言,结论是直接的:保持补丁最新、收紧 MaxRequestBytes 默认值、在边界处对"单连接大量小 TLS record"建立启发式告警,并在内核侧启用池完整性保护。对开发方而言,教训是结构性的——任何随不可信输入线性增长、且参与内存分配大小计算的计数器,都不应使用窄宽度整数承载。