ワンクリックで
mux-audit
修改多路复用协议的帧处理、流管理、窗口控制、外部库集成、流生命周期相关代码时触发。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
修改多路复用协议的帧处理、流管理、窗口控制、外部库集成、流生命周期相关代码时触发。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
写入新模块文档、重构模块、或用户要求分析模块时触发。
Bug 修复或对接问题排查确认有效后,将经验记录到知识库。
修改 PMR 分配器、内存池配置、热路径容器、对象生命周期管理代码后触发。
编写或修改性能基准测试、分析 benchmark 结果、优化热路径性能时触发。
新增或修改 enable_shared_from_this 类、co_spawn 调用、shared_ptr 捕获的 lambda、co_await 后的成员访问时触发。
新增或修改原子操作、异步定时器、无锁通道、跨线程状态同步、CAS 竞争相关代码时触发。
| name | mux-audit |
| description | 修改多路复用协议的帧处理、流管理、窗口控制、外部库集成、流生命周期相关代码时触发。 |
多路复用协议在单条传输连接上承载多个独立的逻辑流(TCP 流和 UDP 流),是代理系统中最复杂的子系统。帧解析、窗口管理、流生命周期、半关闭语义、外部库集成——每一层都有独立的攻击面。任何一环的缺陷都可能导致数据损坏、资源泄漏、或远程可利用的漏洞。
多路复用协议的帧解析器是直接处理网络输入的代码。恶意客户端可以构造任意帧——超长帧、无效流 ID、窗口溢出、递归嵌套的头部块。帧解析器必须在"不信任任何输入"的原则下设计。
流控窗口使用无符号整数(通常 32 位)。递减操作可能下溢(wrap around 到最大值),增量操作可能导致溢出(超过协议允许的最大值)。两种溢出都会破坏流控语义——下溢使发送者认为有无限窗口,溢出违反协议规范。
流 ID 是有限资源(32 位无符号整数)。服务端和客户端使用奇偶分离的 ID 空间。如果流 ID 分配不是原子的,并发创建可能产生 ID 冲突。如果流 ID 耗尽时没有优雅处理,新流创建会静默失败。
| 检查项 | 说明 |
|---|---|
| 长度字段验证 | 帧头中的长度字段是否验证不超过剩余缓冲区大小?如果解析器信任帧头中声明的长度而不检查缓冲区边界,恶意帧可以触发越界读取 |
| 缓冲区部分消费 | 帧解析器消费了部分缓冲区后,未消费的字节如何处理?如果下一轮读取覆盖了未消费部分,数据丢失。需要保留未消费字节并在下次读取时拼接 |
| 头部解码递归深度 | 如果使用头部压缩(如 HPACK),解码是否有递归深度限制?恶意客户端可构造深度嵌套的头部块,导致解码器栈溢出或无限循环 |
| 超长帧处理 | 帧长度超过配置的最大帧大小时是否正确拒绝?超大帧可能导致内存耗尽。拒绝方式应发送协议错误帧(如 RST_STREAM)而非静默丢弃 |
| 帧类型白名单 | 未知的帧类型是否安全忽略(跳过)而非触发错误?协议扩展可能引入新帧类型,旧实现应优雅忽略。但管理帧(如设置、GOAWAY)必须是已知类型 |
| 检查项 | 说明 |
|---|---|
| 递减下溢检测 | 发送窗口递减后是否检测下溢(wrap around 到最大值)?发送窗口使用无符号整数,window -= size 在 size > window 时下溢。下溢后窗口值变为巨大正数,发送者可以无限制发送数据 |
| 增量溢出验证 | 窗口更新帧的增量值是否验证不会导致窗口溢出?如果 window + increment > max_window,应发送流控错误。未验证的增量值可能使窗口溢出到负数(无符号下变为大正数) |
| 初始窗口协商 | 连接建立时是否通过设置帧协商初始窗口大小?如果未协商,使用默认值是否与配置一致?默认窗口过小导致频繁的窗口更新帧,过大可能导致缓冲区溢出 |
| 窗口更新的竞态 | 两个协程同时触发窗口更新(如两个读循环同时消费了足够的字节)是否安全?重复的窗口更新不导致错误,但可能导致窗口值计算不准确 |
| 检查项 | 说明 |
|---|---|
| 分配原子性 | 流 ID 的递增是否在并发创建流时原子化?如果两个协程同时读取并递增 next_id,可能分配相同的 ID,导致流注册表中的条目被覆盖 |
| 复用检测 | 新建的流 ID 是否检查是否已存在于活跃流表中?如果复用了已存在的 ID,旧流的读者永远不会收到 FIN 或错误——数据静默丢失 |
| 耗尽处理 | 流 ID 接近最大值时是否优雅处理?新流创建应返回错误而非静默失败。连接应发送 GOAWAY 帧通知对端,然后等待现有流完成 |
| 半关闭状态管理 | 流的半关闭状态(本地关闭/远端关闭)是否正确管理?两个标志(本地关闭 + 远端关闭)产生四种组合——全部关闭、仅本地关闭、仅远端关闭、全部打开。四种组合都必须有明确的处理语义 |
| FIN 后的数据帧 | 收到 FIN 后继续收到数据帧是否静默丢弃(半关闭语义)?半关闭后到达的数据应被丢弃而非触发错误,因为网络中可能还有在途数据 |
| 检查项 | 说明 |
|---|---|
| 设置约束通告 | 是否向对端发送设置帧通告资源限制(最大并发流数、最大帧大小、最大头部列表大小、初始窗口大小)?未通告的限制使用默认值(通常是"无限制"),允许对端无约束地消耗资源 |
| 协议合规帧 | 连接关闭时是否发送协议要求的终止帧(如 GOAWAY)?未发送终止帧导致对端无法区分正常关闭和连接断开,可能触发不必要的重试 |
| 库选项配置 | 外部库的选项对象是否配置了安全参数?默认选项通常不启用保护(如设置洪水防护、自动 PING 响应)。未配置的选项可能导致外部库行为与安全预期不符 |
| 入站流数强制执行 | 对端创建的流数是否在设置帧通告的限制内强制执行?如果只设置了最大流数但不在入站时检查,限制形同虚设 |
| 检查项 | 说明 |
|---|---|
| 读写循环的退出协调 | 流的读循环和写循环独立运行,一端异常是否通知另一端退出?如果读循环异常退出但写循环继续运行,写循环可能操作已失效的流状态 |
| 保活与退出时机 | 长期运行的发送/接收循环是否正确保活流对象?如果循环持有的 self 在流关闭后仍引用流,可能导致循环在已关闭的流上操作 |
| close 的幂等性 | 流的关闭操作是否幂等?连接管理器可能调用 close,同时流的某个循环也可能触发 close。第二次调用不应崩溃或产生副作用 |
// ❌ 信任帧头长度,直接读取
auto len = read_uint24(frame_header);
auto payload = co_await async_read_exactly(transport, len);
// len 可能超过缓冲区或内存上限
// ✅ 验证长度不超过配置上限
auto len = read_uint24(frame_header);
if (len > config_.max_frame_size)
{
send_protocol_error(frame_too_large);
co_return;
}
auto payload = co_await async_read_exactly(transport, len);
// ❌ 无符号整数递减无下溢检测
send_window_ -= data_size; // data_size > send_window_ 时 wrap around
// ✅ 递减前检查
if (data_size > send_window_.load())
{
send_flow_control_error();
co_return;
}
send_window_.fetch_sub(data_size, std::memory_order_relaxed);
// ❌ 新 SYN 不检查 ID 是否已存在
auto id = next_id_++;
streams_[id] = create_stream(id);
// 如果 id 已存在,旧流被覆盖,旧流的读者永不收到 FIN
// ✅ 检查 ID 冲突
auto id = next_id_++;
if (streams_.contains(id))
{
send_protocol_error(stream_id_conflict);
co_return;
}
streams_[id] = create_stream(id);
// ❌ 提交空设置帧 — 使用默认值(通常是"无限制")
submit_settings(session, nullptr, 0); // 0 个参数
// ✅ 从配置读取并通告限制
setting settings[] = {
{MAX_CONCURRENT_STREAMS, config_.max_streams},
{MAX_FRAME_SIZE, config_.max_frame_size},
{MAX_HEADER_LIST_SIZE, config_.max_header_size},
{INITIAL_WINDOW_SIZE, config_.initial_window}
};
submit_settings(session, settings, 4);
// ❌ 读循环异常退出后写循环继续
void on_read_error(error_code ec)
{
read_loop_active_ = false;
// 写循环不知道读端已退出,可能在无效的流上操作
}
// ✅ 读循环退出通知写循环
void on_read_error(error_code ec)
{
read_loop_active_ = false;
write_side_error_ = ec;
write_signal_->notify(); // 唤醒写循环让它检查状态
}
coroutine-audit 覆盖了 co_spawn 保活、协程取消维度co-lifecycle-audit 覆盖了 shared_ptr 循环引用、对象生命周期维度tunnel-audit 覆盖了双向转发的 EOF 传播和关闭顺序维度traffic-audit 覆盖了多路复用流量形状、填充效果维度crypto-audit 覆盖了多路复用中 AEAD 序列号独立性和密钥隔离维度leak-audit 覆盖了多路复用帧格式的实现独特行为指纹、部署规模追踪维度probe-audit 覆盖了多路复用连接的关闭行为与标准服务器一致性维度