|
3 | 3 | > 日期: 2026-03-10 |
4 | 4 | > 范围: `fl.c` (固件) + `serial_protocol.py` (上位机) |
5 | 5 |
|
6 | | -## 1. 现状分析 |
| 6 | +## 1. 整改前现状 |
7 | 7 |
|
8 | 8 | ### 1.1 CRC 算法 |
9 | 9 |
|
10 | 10 | 两端使用相同的 **CRC-16-CCITT**(初始值 `0xFFFF`,查表法),表一致,算法正确。 |
11 | 11 |
|
12 | 12 | 固件端 `calc_crc16_base(crc, data, len)` 支持增量计算(链式调用),可以避免拼接 buffer 的二次拷贝。 |
13 | 13 |
|
14 | | -### 1.2 各命令 CRC 覆盖范围 |
| 14 | +上位机端新增 `crc16_update(crc, data)` 对应固件端的链式调用。 |
15 | 15 |
|
16 | | -| 命令 | `addr`/`offset` | `len` | `data` payload | CRC 方向 | 风险 | |
17 | | -|------|:---:|:---:|:---:|:---:|:---:| |
18 | | -| `write` | ❌ | 隐含 | ✅ | 上→下 | **高** — 地址错误静默写坏内存 | |
19 | | -| `upload` | ❌ | 隐含 | ✅ | 上→下 | **高** — 偏移错误写坏 alloc buffer | |
20 | | -| `read` (响应) | ❌ | ❌ | ✅ | 下→上 | **中** — 无法确认数据来自请求地址 | |
21 | | -| `fwrite` | N/A | 隐含 | ✅ | 上→下 | 低 — 顺序写入,无地址参数 | |
22 | | -| `fread` (响应) | N/A | ❌ | ✅ | 下→上 | 低 — 顺序读取 | |
| 16 | +### 1.2 整改前各命令 CRC 覆盖范围 |
23 | 17 |
|
24 | | -**核心漏洞**: `--addr` / `-a` (offset) / `--len` 等数值参数未参与 CRC 校验。串口传输中任何一个字符翻转(如 `0x20000000` 变成 `0x20000800`)都不会被检测到。 |
| 18 | +| 命令 | `addr`/`offset` | `len` | `comp` | `orig` | `target` | `data` | CRC 方向 | 风险 | |
| 19 | +|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |
| 20 | +| `write` | ❌ | 隐含 | — | — | — | ✅ | 上→下 | **高** — 地址错误静默写坏内存 | |
| 21 | +| `upload` | ❌ | 隐含 | — | — | — | ✅ | 上→下 | **高** — 偏移错误写坏 alloc buffer | |
| 22 | +| `read` (请求) | ❌ | ❌ | — | — | — | — | 上→下 | **中** — 地址错误触发 HardFault | |
| 23 | +| `read` (响应) | ❌ | ❌ | — | — | — | ✅ | 下→上 | **中** — 无法确认数据来自请求地址 | |
| 24 | +| `patch` | — | — | ❌ | ❌ | ❌ | — | 上→下 | **极高** — 地址错误改变代码跳转 | |
| 25 | +| `tpatch` | — | — | ❌ | ❌ | ❌ | — | 上→下 | **极高** — 同上 | |
| 26 | +| `dpatch` | — | — | ❌ | ❌ | ❌ | — | 上→下 | **极高** — 同上 | |
| 27 | +| `fwrite` | N/A | 隐含 | — | — | — | ✅ | 上→下 | 低 — 顺序写入,无地址参数 | |
| 28 | +| `fread` (响应) | N/A | ❌ | — | — | — | ✅ | 下→上 | 低 — 顺序读取 | |
25 | 29 |
|
26 | | -### 1.3 未使用的 argparse 参数 |
| 30 | +### 1.3 已清理的未使用 argparse 参数 |
27 | 31 |
|
28 | | -| 参数 | 声明 | 状态 | |
| 32 | +| 参数 | 原声明 | 状态 | |
29 | 33 | |------|------|------| |
30 | | -| `entry` (`-e`) | `OPT_INTEGER('e', "entry", &entry, "Entry offset")` | **完全未使用** — 无任何命令读取 | |
31 | | -| `args` | `OPT_STRING(0, "args", &args, "Arguments")` | **完全未使用** — 无任何命令读取 | |
32 | | - |
33 | | -这两个参数是历史遗留,应当清理以减少攻击面和代码噪音。 |
34 | | - |
35 | | -### 1.4 命令风格不一致 |
36 | | - |
37 | | -Python 端 `upload` 使用短选项 (`-a`, `-d`, `-r`),而 `read_memory`/`write_memory` 使用长选项 (`--addr`, `--data`, `--crc`)。功能等价但风格混用,不影响正确性。 |
| 34 | +| `entry` (`-e`) | `OPT_INTEGER('e', "entry", &entry, "Entry offset")` | 已删除 — 无任何命令使用 | |
| 35 | +| `args` | `OPT_STRING(0, "args", &args, "Arguments")` | 已删除 — 无任何命令使用 | |
38 | 36 |
|
39 | 37 | ## 2. 整改方案 |
40 | 38 |
|
41 | 39 | ### 2.1 CRC 增强策略 |
42 | 40 |
|
43 | | -利用 `calc_crc16_base` 的链式调用能力,将 `addr`/`offset`/`len` 的字节表示依次喂入 CRC 计算,**无需拼接 buffer,无二次循环**: |
| 41 | +利用 `calc_crc16_base` 的链式调用能力,将数值参数的字节表示依次喂入 CRC 计算,**无需拼接 buffer,无二次循环**: |
44 | 42 |
|
45 | 43 | ```c |
46 | 44 | // 固件端示例 (write 命令) |
47 | | -uint16_t crc = 0xFFFF; |
48 | 45 | uint32_t addr32 = (uint32_t)addr; |
49 | 46 | uint32_t len32 = (uint32_t)n; |
| 47 | +uint16_t crc = 0xFFFF; |
50 | 48 | crc = calc_crc16_base(crc, &addr32, sizeof(addr32)); // 4 bytes |
51 | 49 | crc = calc_crc16_base(crc, &len32, sizeof(len32)); // 4 bytes |
52 | 50 | crc = calc_crc16_base(crc, buf, n); // payload |
53 | 51 | ``` |
54 | 52 |
|
55 | 53 | ```python |
56 | 54 | # 上位机示例 (write 命令) |
57 | | -import struct |
58 | | -crc = 0xFFFF |
59 | | -crc = crc16_update(crc, struct.pack('<II', addr, len(chunk))) |
| 55 | +crc = crc16_update(0xFFFF, struct.pack('<II', addr, len(chunk))) |
60 | 56 | crc = crc16_update(crc, chunk) |
61 | 57 | ``` |
62 | 58 |
|
63 | | -### 2.2 各命令改动清单 |
| 59 | +### 2.2 字节序约定 |
64 | 60 |
|
65 | | -| 命令 | 改动 | CRC 输入 (按顺序) | |
66 | | -|------|------|------| |
67 | | -| `write` | 固件 + 上位机 | `addr(4B)` + `len(4B)` + `data` | |
68 | | -| `upload` | 固件 + 上位机 | `offset(4B)` + `len(4B)` + `data` | |
69 | | -| `read` (响应) | 固件 + 上位机 | `addr(4B)` + `len(4B)` + `data` | |
70 | | -| `fwrite` | 不改 | 无地址参数,当前方案足够 | |
71 | | -| `fread` (响应) | 不改 | 无地址参数,当前方案足够 | |
| 61 | +CRC 中的数值参数统一使用 **小端序 (little-endian)** 编码,与 ARM Cortex-M 原生字节序一致。固件端直接取内存地址即可,无需额外转换。 |
72 | 62 |
|
73 | | -### 2.3 字节序约定 |
74 | | - |
75 | | -CRC 中的 `addr` 和 `len` 统一使用 **小端序 (little-endian)** 编码,与 ARM Cortex-M 原生字节序一致。固件端直接取内存地址即可,无需额外转换。 |
76 | | - |
77 | | -### 2.4 向后兼容 |
| 63 | +### 2.3 向后兼容 |
78 | 64 |
|
79 | 65 | - `crc` 参数仍然可选(`-1` = 不校验),旧版上位机不传 CRC 时固件跳过校验 |
80 | | -- 新版上位机传入的 CRC 已包含 addr/len,新版固件用增强算法验证 |
81 | | -- **不兼容场景**: 新上位机 + 旧固件 → CRC 不匹配 → 写入失败(安全侧失败,可接受) |
| 66 | +- 新版上位机传入的 CRC 已包含所有数值参数,新版固件用增强算法验证 |
| 67 | +- **不兼容场景**: 新上位机 + 旧固件 → CRC 不匹配 → 操作失败(安全侧失败,可接受) |
82 | 68 |
|
83 | | -## 3. 清理项 |
| 69 | +## 3. 整改结果 |
84 | 70 |
|
85 | | -| 项目 | 操作 | |
86 | | -|------|------| |
87 | | -| `entry` 参数 | 从 `argparse_option` 和局部变量中删除 | |
88 | | -| `args` 参数 | 从 `argparse_option` 和局部变量中删除 | |
| 71 | +### 3.1 各命令 CRC 覆盖范围(整改后) |
89 | 72 |
|
90 | | -## 4. 测试计划 |
| 73 | +| 命令 | CRC 输入 (按顺序) | CRC 方向 | 状态 | |
| 74 | +|------|------|:---:|:---:| |
| 75 | +| `write` | `addr(4B)` + `len(4B)` + `data` | 上→下 | ✅ 已修复 | |
| 76 | +| `upload` | `offset(4B)` + `len(4B)` + `data` | 上→下 | ✅ 已修复 | |
| 77 | +| `read` (请求) | `addr(4B)` + `len(4B)` | 上→下 | ✅ 已修复 | |
| 78 | +| `read` (响应) | `addr(4B)` + `len(4B)` + `data` | 下→上 | ✅ 已修复 | |
| 79 | +| `patch` | `comp(4B)` + `orig(4B)` + `target(4B)` | 上→下 | ✅ 已修复 | |
| 80 | +| `tpatch` | `comp(4B)` + `orig(4B)` + `target(4B)` | 上→下 | ✅ 已修复 | |
| 81 | +| `dpatch` | `comp(4B)` + `orig(4B)` + `target(4B)` | 上→下 | ✅ 已修复 | |
| 82 | +| `fwrite` | `data` | 上→下 | 无需改动 | |
| 83 | +| `fread` (响应) | `data` | 下→上 | 无需改动 | |
91 | 84 |
|
92 | | -### 4.1 上位机测试 (`test_serial_protocol.py`) |
| 85 | +### 3.2 涉及文件 |
93 | 86 |
|
94 | | -- `TestWriteMemoryCRC`: 验证 write 命令的 CRC 包含 addr + len + data |
95 | | -- `TestUploadCRC`: 验证 upload 命令的 CRC 包含 offset + len + data |
96 | | -- `TestReadResponseCRC`: 验证 read 响应解析时 CRC 包含 addr + len + data |
| 87 | +| 文件 | 改动 | |
| 88 | +|------|------| |
| 89 | +| `App/func_loader/fl.c` | `cmd_read` 增加请求 CRC 校验;新增 `verify_patch_crc()` 供 patch/tpatch/dpatch 使用;清理 `entry`/`args` 参数 | |
| 90 | +| `Tools/WebServer/core/serial_protocol.py` | `read_memory` 发送 `--crc`;新增 `_patch_crc()` 供 patch/tpatch/dpatch 使用;`_parse_read_response` 验证 addr+len+data | |
| 91 | +| `Tools/WebServer/utils/crc.py` | 新增 `crc16_update(crc, data)` 支持链式计算 | |
| 92 | +| `Tools/WebServer/tests/test_serial_protocol.py` | 新增 `TestEnhancedCRC` 测试类,覆盖 write/upload/read/patch/tpatch/dpatch 的 CRC 验证 | |
97 | 93 |
|
98 | | -### 4.2 固件测试 |
| 94 | +### 3.3 测试覆盖 |
99 | 95 |
|
100 | | -- 由 CI `lower-machine` job 中的固件单元测试覆盖 |
| 96 | +| 测试 | 验证内容 | |
| 97 | +|------|------| |
| 98 | +| `test_write_crc_includes_addr_and_len` | write 命令 CRC 包含 addr + len + data | |
| 99 | +| `test_upload_crc_includes_offset_and_len` | upload 命令 CRC 包含 offset + len + data | |
| 100 | +| `test_read_response_crc_includes_addr_and_len` | read 响应 CRC 包含 addr + len + data;旧格式 CRC 被拒绝 | |
| 101 | +| `test_read_cmd_includes_crc` | read 请求发送 --crc 覆盖 addr + len | |
| 102 | +| `test_patch_cmd_includes_crc` | patch 命令 CRC 包含 comp + orig + target | |
| 103 | +| `test_tpatch_cmd_includes_crc` | tpatch 命令 CRC 包含 comp + orig + target | |
| 104 | +| `test_dpatch_cmd_includes_crc` | dpatch 命令 CRC 包含 comp + orig + target | |
| 105 | +| `test_crc16_update_chaining` | crc16_update 链式调用等价于 crc16 拼接调用 | |
101 | 106 |
|
102 | 107 | ## 5. 影响评估 |
103 | 108 |
|
|
0 commit comments