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 字节可读;实际协议还应验证长度、命令号、参数范围及业务状态等约束。
速记:先查长度 → 再查命令 → 再查参数 → 最后提交。