Bug: is_idr_nalu 循环可能在帧末尾漏检 NAL 单元 (webrtc.rs:511) #13

Closed
opened 2026-06-04 20:59:55 +08:00 by dailz · 1 comment
Owner

位置

src/webrtc.rs:511-531

严重性

🟢

问题描述

循环条件 while i + 4 < data.len()i + 4 == data.len() 时退出。如果 H.264 帧的最后 4 字节恰好是 [0, 0, 1, 0x05](3 字节起始码 + IDR NAL 头),该 NAL 单元会被漏检。

虽然实际场景中可能性很低(帧末尾通常是 slice 数据而非独立的 IDR NAL),但作为 NAL 扫描器的正确性仍有欠缺。

建议修复

将循环条件改为 while i < data.len() 并在各分支内独立做边界检查:

fn is_idr_nalu(data: &[u8]) -> bool {
    let mut i = 0;
    while i < data.len() {
        if i + 4 <= data.len() && data[i..i + 4] == [0, 0, 0, 1] {
            if i + 5 <= data.len() {
                let nal_type = data[i + 4] & 0x1F;
                if nal_type == 5 { return true; }
            }
            i += 5;
        } else if i + 3 <= data.len() && data[i..i + 3] == [0, 0, 1] {
            if i + 4 <= data.len() {
                let nal_type = data[i + 3] & 0x1F;
                if nal_type == 5 { return true; }
            }
            i += 4;
        } else {
            i += 1;
        }
    }
    false
}
## 位置 `src/webrtc.rs:511-531` ## 严重性 🟢 低 ## 问题描述 循环条件 `while i + 4 < data.len()` 在 `i + 4 == data.len()` 时退出。如果 H.264 帧的最后 4 字节恰好是 `[0, 0, 1, 0x05]`(3 字节起始码 + IDR NAL 头),该 NAL 单元会被漏检。 虽然实际场景中可能性很低(帧末尾通常是 slice 数据而非独立的 IDR NAL),但作为 NAL 扫描器的正确性仍有欠缺。 ## 建议修复 将循环条件改为 `while i < data.len()` 并在各分支内独立做边界检查: ```rust fn is_idr_nalu(data: &[u8]) -> bool { let mut i = 0; while i < data.len() { if i + 4 <= data.len() && data[i..i + 4] == [0, 0, 0, 1] { if i + 5 <= data.len() { let nal_type = data[i + 4] & 0x1F; if nal_type == 5 { return true; } } i += 5; } else if i + 3 <= data.len() && data[i..i + 3] == [0, 0, 1] { if i + 4 <= data.len() { let nal_type = data[i + 3] & 0x1F; if nal_type == 5 { return true; } } i += 4; } else { i += 1; } } false } ```
dailz closed this issue 2026-06-06 21:34:26 +08:00
Author
Owner

修复方案

已通过 commit e6e05fb 修复。

问题根因

while i + 4 < data.len() 要求剩余至少 5 字节才进入循环体,导致数据末尾的 3 字节起始码 NAL 单元被跳过。

修复内容

  • 循环条件改为 while i < data.len(),使用切片 tail + starts_with + get() 替代手工索引算术
  • 各分支通过 let Some(&header) = tail.get(N) else { break } 安全处理边界,起始码后无 NAL 头时退出循环
  • 消除了理论上的 usize 溢出风险

测试

新增 10 个单元测试覆盖:

  • 空数据、短数据
  • 3/4 字节起始码无 NAL 头
  • 3/4 字节起始码 + IDR 在末尾(原始 bug 触发场景)
  • 帧中间 IDR 检测
  • 无 IDR 帧
  • 混合起始码长度
  • 全零数据(不 panic)
## 修复方案 已通过 commit e6e05fb 修复。 ### 问题根因 `while i + 4 < data.len()` 要求剩余至少 5 字节才进入循环体,导致数据末尾的 3 字节起始码 NAL 单元被跳过。 ### 修复内容 - 循环条件改为 `while i < data.len()`,使用切片 `tail` + `starts_with` + `get()` 替代手工索引算术 - 各分支通过 `let Some(&header) = tail.get(N) else { break }` 安全处理边界,起始码后无 NAL 头时退出循环 - 消除了理论上的 usize 溢出风险 ### 测试 新增 10 个单元测试覆盖: - 空数据、短数据 - 3/4 字节起始码无 NAL 头 - 3/4 字节起始码 + IDR 在末尾(原始 bug 触发场景) - 帧中间 IDR 检测 - 无 IDR 帧 - 混合起始码长度 - 全零数据(不 panic)
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dailz/wl-webrtc#13