15 固定长度命令的安全解析

核心规则:先验证全部输入,再修改设备状态;任何检查失败,状态保持不变。

1. 两字节命令格式

┌───────────────────┬───────────────────┐
│ buf[0]:命令号    │ buf[1]:参数值    │
│ 0x01:设置音量    │ 0~10:合法音量   │
└───────────────────┴───────────────────┘
             固定长度:2 字节
检查顺序检查内容失败处理
①指针不为空、len == 2立即返回 false,不得读取字段
②命令号为 1返回 false
③音量范围 0~10返回 false
④全部合法修改设备音量、返回 true

2. 解析流程图

flowchart TD
    A[收到 buf 和 len] --> B{buf 有效且 len == 2?}
    B -- 否 --> X[返回 false / 状态不变]
    B -- 是 --> C[读取命令号与参数]
    C --> D{命令号 == 1?}
    D -- 否 --> X
    D -- 是 --> E{音量 <= 10?}
    E -- 否 --> X
    E -- 是 --> F[更新 volume]
    F --> G[返回 true]

3. 精简 C 示例

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

static uint8_t volume = 5;

bool parse_volume(const uint8_t *buf, size_t len)
{
    if (buf == NULL || len != 2) return false;
    if (buf[0] != 1 || buf[1] > 10) return false;

    volume = buf[1];  // 全部验证成功后才修改
    return true;
}

4. 好做法与坏做法

✅ 先检查后修改❌ 边解析边修改
不会读取超出报文长度的字段可能发生越界读取
命令或参数非法时状态不变可能出现“部分修改后失败”
方便明确成功和失败语义难保证失败时状态一致

多字段命令:先检查所有字段,再一起提交业务状态。若有多个任务同时访问状态,仍需额外同步;验证顺序本身不提供并发原子性。

蓝牙/TWS 注意:len 只是调用者传入的数字,不能证明缓冲区真实可读长度。调用者必须保证 buf 至少有 len 字节可读;实际协议还应验证长度、命令号、参数范围及业务状态等约束。

速记:先查长度 → 再查命令 → 再查参数 → 最后提交。