com um clique
mux-audit
修改多路复用协议的帧处理、流管理、窗口控制、外部库集成、流生命周期相关代码时触发。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
修改多路复用协议的帧处理、流管理、窗口控制、外部库集成、流生命周期相关代码时触发。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional 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 覆盖了多路复用连接的关闭行为与标准服务器一致性维度