From ff590d89962bb517948396bcce47263ca6d8f27b Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Tue, 20 Oct 2026 21:13:50 +0800 Subject: [PATCH] =?UTF-8?q?docs(review):=20=E4=B8=93=E5=AE=B6=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=202026-10-19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/project_introduction.html | 7 + .../ESP32_COMM_专家评审_2026-10-19.html | 160 +++ docs/review/README.md | 11 + .../CODE_REVIEW_REPORT_2026-06-13.html | 970 ++++++++++++++++++ .../archive/COMMUNICATION_AUDIT_2026-05-05.md | 74 ++ .../esp32_architecture_review_2026-06-27.md | 301 ++++++ 6 files changed, 1523 insertions(+) create mode 100644 docs/review/ESP32_COMM_专家评审_2026-10-19.html create mode 100644 docs/review/README.md create mode 100644 docs/review/archive/CODE_REVIEW_REPORT_2026-06-13.html create mode 100644 docs/review/archive/COMMUNICATION_AUDIT_2026-05-05.md create mode 100644 docs/review/archive/esp32_architecture_review_2026-06-27.md diff --git a/docs/project_introduction.html b/docs/project_introduction.html index 07c3e60..00571cd 100644 --- a/docs/project_introduction.html +++ b/docs/project_introduction.html @@ -1545,6 +1545,13 @@ python "F:\.espressif\v6.0\esp-idf\tools\idf.py" build +
/spi/send 可原样透传任意 SPI 帧类型、timeout_ms 无上限,升级为致命(评审 F-01 / S-12)。
+config/sdkconfig.defaults 未被构建引用(S-01 / S-02)。
+docs/review/archive/。| 层 | 目录 / 模块 | 职责 | 规模(行) |
|---|---|---|---|
| 接入层 | server/ wifi_ota · http_handlers · ble_ota(_data/_host) | STA/AP、HTTP 路由、NimBLE GATT、窗口 ACK / reorder | ≈ 6.6k(http_handlers 2832) |
| 协调层 | ota/ OtaCoordinator · Esp32OtaCore | 9 态单例、唯一权威入口、150 s 忙态看门狗、epoch | ≈ 2.0k |
| 执行层 | relay/ OtaRelay · storage/ Gd32FlashStore · monitor/ MotorOtaRelay | GD32 中继状态机、gd32_ota 分区与 NVS meta、电机 OTA 喂数 | ≈ 3.5k |
| 传输层 | transport/ SpiMaster · protocol · canfd_bridge(+ 仓外 spi_link_v2) | Link v2 主站(会话/序号/credit/CRC32/重试)、AES/nonce、CAN 0xC0~0xC8 | ≈ 4.3k |
| 横切 | monitor/ health · debug_log · can_feedback · cpu_stats · task_stack 等;core/ | 健康门控、三路日志、遥测轮询、启动自检位图 | ≈ 3.6k |
任务模型:ota_relay(23) · wifi_ota/ble_ota(22) · health_mon(21) · ble_ota_worker(12) · spi_worker(8,唯一持 SPI) · ota_feed(7) · httpd(5) · can_fb(4) · cpu_stats/uart_rx(1)。
main/ 全部 60 个 C/C++ 源文件(≈ 2.0 万行)、components/transport、config/、两份 sdkconfig.defaults、sdkconfig.ci、dependencies.lock、CMake 与 Kconfig、CI workflow、.devcontainer/.vscode/.gitignore、Python 工具脚本、README 与白皮书、git 历史。docs/project_introduction.html §18/§19(含 2026-08-16 安全审计)与 README。git 历史中另有三份已被删除的评审:2026-06-27 架构扫描(af1f682 删除)、2026-06-13 高风险评审与 2026-05-05 通信梳理(5b2c5f8 删除)。本次已原样恢复至 docs/review/archive/,并在第 5 节逐条跟踪。spi_link/spi_link_v2.* 不在本仓库,CRC/帧格式部分只能按调用侧判断;0627 评审中约 50 条低风险风格类 P2 条目未逐条复核;网页前端(tools/)已移出仓库,不在范围内。| 编号 | 严重度 | 类别 | 问题 | 主要位置 |
|---|---|---|---|---|
| F-01 | 致命 | 安全 | HTTP 全部端点零认证 + CORS * + /spi/send 原始透传 | main/server/http_handlers.cpp:77-83, 1685-1756 |
| S-01 | 严重 | 构建 | 干净克隆构建出的是另一份固件(关键配置文件未生效且自相矛盾) | CMakeLists.txt |
| S-02 | 严重 | OTA | 回滚保护取消过早:mark_app_valid 早于自检判定 | main/core/main.cpp:131-147 |
| S-03 | 严重 | 并发 | 看门狗/中止路径与中继任务并发:清会话密钥但中继任务不停 | main/ota/ota_coordinator.cpp:625-635 |
| S-04 | 严重 | OTA | 忙态看门狗 150 s 短于 GD32 中继合法最坏耗时 | main/include/app_config.h:126 |
| S-05 | 严重 | 健壮性 | WiFi 运行期断线重试 5 次后永不重连,也不回退 AP | main/server/wifi_ota.cpp:94-100, 180-192, 751-777 |
| S-06 | 严重 | 安全 | GD32 握手 nonce 可预测且时间戳明文入日志 | main/relay/ota_relay.cpp:1027-1035 |
| S-07 | 严重 | 健壮性 | GET /can/mode 查询失败时会把 CAN 接收模式写回 ALL_PASS | main/server/http_handlers.cpp:1020-1030 |
| S-08 | 严重 | 安全 | 凭据与主密钥明文入仓,AP 弱口令 | main/include/app_config.h:31-32, 39-40, 161-164 |
| S-09 | 严重 | 安全 | BLE GATT 无配对/加密,范围内任意设备可 OTA 或重启 | main/server/ble_ota_host.cpp:366-376 |
| S-10 | 严重 | 实时性 | RSSI 更新覆盖过温 SAFE_SHUTDOWN 健康状态 | main/monitor/health_monitor.cpp:578-600 |
| S-11 | 严重 | 实时性 | 栈溢出钩子中调用可阻塞的 uart_write_bytes | main/monitor/task_stack.cpp:27-40 |
| S-12 | 严重 | 安全 | /spi/send 的 timeout_ms 无上限,可长期占死 SPI worker | main/server/http_handlers.cpp:1715 |
| G-01 | 一般 | OTA | 完成回调不校验状态与 epoch,可能覆盖新会话 | main/ota/ota_coordinator.cpp:372-392, 568-576 |
| G-02 | 一般 | 并发 | 协调器锁超时 100 ms,lock 失败直接跳过状态切换 | main/include/app_config.h:124 |
| G-03 | 一般 | OTA | 确认 APP 存活前即清除 meta;失败路径连发 3 次 CANCEL | main/relay/ota_relay.cpp:1554, 1573-1579, 1637-1643 |
| G-04 | 一般 | OTA | 校验缺口:先置 STORED 后算 SHA;ESP32 finalize 可不带 SHA、不比对实收字节 | main/storage/gd32_flash_store.cpp(finishGd32StorageAfterReceive 275-279 附近) |
| G-05 | 一般 | 内存 | OTA 对齐缓冲正常完成后不释放(常驻约 4 KB) | main/ota/ota_coordinator.cpp:203, 625-635 |
| G-06 | 一般 | OTA | 电机 OTA 与 GD32 OTA 共用分区和 meta,互斥不完整 | main/server/http_handlers.cpp:2622-2642, 2697-2700, 2748-2776 |
| G-07 | 一般 | 内存 | JSON 截断输出非法 JSON;motor_state 栈占用约 2.5 KB | main/server/http_handlers.cpp:2383, 2493 |
| G-08 | 一般 | 健壮性 | 输入校验不足与未定义行为 | main/server/http_handlers.cpp:323, 1056-1061, 1256, 1340-1341, 1521 |
| G-09 | 一般 | 并发 | suspendStack 跨任务写非原子 BLE 连接状态 | main/server/ble_ota.cpp:276-279 |
| G-10 | 一般 | 内存 | SPI 事务超时返回后 DMA 可能仍在使用栈上缓冲 | main/transport/spi_master.cpp:1001-1019 |
| G-11 | 一般 | 健壮性 | tick 比较非回绕安全 | main/transport/spi_master.cpp:883-889, 1443-1445, 1560-1572, 2058-2061, 2156-2163, 2544-2556 |
| G-12 | 一般 | 协议 | SPI 主站边界缺陷(resp_len 越读、out_init_status 未回填、HELLO 失败后会话不一致) | main/transport/spi_master.cpp:175, 622-627, 1692-1697, 2005-2042 |
| G-13 | 一般 | 构建 | CI 必然失败且不构建固件 | .github/workflows/ |
| G-14 | 一般 | 可维护性 | 任务数 > 20 时 CPU 统计冻结并每秒刷 WARN | main/monitor/cpu_stats.cpp:106-117 |
| G-15 | 一般 | 安全 | UDP 日志不可关闭、目标硬编码,fd 关闭存在竞态 | main/monitor/debug_log.cpp:689-711 |
| G-16 | 一般 | 可维护性 | 日志环满时选择性覆盖导致 /logs 时间序错乱 | main/monitor/debug_log.cpp:341-352 |
| G-17 | 一般 | OTA | OTA_MAX_FIRMWARE_SIZE 注释数值错误 | main/include/app_config.h:77 |
| A-01 | 建议 | 构建 | 仓库卫生:gitignore 行内注释失效、被忽略目录仍被跟踪、本机路径入库 | .gitignore:25,31,89-91 |
| A-02 | 建议 | 文档 | 版本信息与文档漂移 | main/include/version.h:18-21 |
| A-03 | 建议 | 构建 | 提交信息与内容不符,历史评审被静默删除 | git 历史 af1f682 |
| A-04 | 建议 | 实时性 | CS 前 esp_rom_delay_us 忙等最长 1 ms | main/transport/spi_master.cpp:946-952 |
| A-05 | 建议 | 实时性 | 间接优先级倒置 | main/core/main.cpp |
| A-06 | 建议 | 可维护性 | 死代码与量程不符 | main/monitor/health_monitor.cpp:389-396, 365 |
| A-07 | 建议 | 可维护性 | 日志格式串与参数不匹配、注释过期 | main/transport/spi_master.cpp:924-925 |
main/server/http_handlers.cpp:77-83, 1685-1756CMakeLists.txt;sdkconfig.defaults;config/sdkconfig.defaults:7-13,23,34,89-90;.gitignore:33main/core/main.cpp:131-147main/ota/ota_coordinator.cpp:625-635;main/relay/ota_relay.cpp:539-554main/include/app_config.h:126;main/relay/ota_relay.cpp:361-368main/server/wifi_ota.cpp:94-100, 180-192, 751-777main/relay/ota_relay.cpp:1027-1035;main/transport/protocol.cpp:90-131main/server/http_handlers.cpp:1020-1030main/include/app_config.h:31-32, 39-40, 161-164;main/server/wifi_ota.cpp:264main/server/ble_ota_host.cpp:366-376main/monitor/health_monitor.cpp:578-600;main/server/wifi_ota.cpp:853main/monitor/task_stack.cpp:27-40main/server/http_handlers.cpp:1715;main/transport/spi_master.cpp:646main/ota/ota_coordinator.cpp:372-392, 568-576main/include/app_config.h:124;main/ota/ota_coordinator.cppmain/relay/ota_relay.cpp:1554, 1573-1579, 1637-1643main/storage/gd32_flash_store.cpp(finishGd32StorageAfterReceive 275-279 附近);main/ota/esp32_ota_core.cpp finalizemain/ota/ota_coordinator.cpp:203, 625-635;main/storage/gd32_flash_store.cpp:162-179main/server/http_handlers.cpp:2622-2642, 2697-2700, 2748-2776main/server/http_handlers.cpp:2383, 2493;main/server/http_util.hmain/server/http_handlers.cpp:323, 1056-1061, 1256, 1340-1341, 1521main/server/ble_ota.cpp:276-279main/transport/spi_master.cpp:1001-1019main/transport/spi_master.cpp:883-889, 1443-1445, 1560-1572, 2058-2061, 2156-2163, 2544-2556main/transport/spi_master.cpp:175, 622-627, 1692-1697, 2005-2042.github/workflows/spi-link-selftest.ymlmain/monitor/cpu_stats.cpp:106-117main/monitor/debug_log.cpp:689-711;main/include/app_config.h:96main/monitor/debug_log.cpp:341-352main/include/app_config.h:77.gitignore:25,31,89-91;.trae/;.zcode/;.vscode/settings.jsonmain/include/version.h:18-21;README.mdgit 历史 af1f682main/transport/spi_master.cpp:946-952main/core/main.cpp;README 任务表main/monitor/health_monitor.cpp:389-396, 365;main/monitor/task_stack.cpp:30-39main/transport/spi_master.cpp:924-925;main/server/wifi_ota.cpp:403来源:0816=project_introduction.html §19 安全审计(2026-08-16);0613=CODE_REVIEW_REPORT_2026-06-13.html(v0.3.12);0627=esp32_architecture_review_2026-06-27.md;0505=COMMUNICATION_AUDIT_2026-05-05.md。
+统计:已修复 23 · 部分修复 5 · 仍存在/结论不成立 12 · 未逐条核对 2。总体看,历史上的并发、函数拆分、资源清理类问题大多已整改;安全类结论(认证、凭据、签名、ECB、nonce)一条都没有关闭。
| 来源 | 编号 | 原结论 | 当前状态 | 当前代码证据 |
|---|---|---|---|---|
| 0816 | ① | HTTP 22 端点零认证 | 仍存在 | http_handlers.cpp:77-83 CORS *,无鉴权;见 F-01 |
| 0816 | ② | WiFi 凭据与 MASTER_KEY 明文入仓 | 仍存在 | app_config.h:31-40,161-164;见 S-08 |
| 0816 | ③ | 无固件签名 | 仍存在 | 仅 SHA-256 完整性;未启用 Secure Boot / 签名校验 |
| 0816 | ④ | AES-ECB 确定性模式 | 仍存在 | ota_relay.cpp 609-620, 836-855 esp_aes_crypt_ecb |
| 0816 | 缓解 | nonce 每会话随机绑定防重放 | 结论不成立 | nonce 仅 tick 4 字节变化且明文入日志;见 S-06 |
| 0613 | #1 | SPI 总线无并发保护 | 已修复 | spi_master.cpp:588-821 单 worker + Job 池 + m_mutex |
| 0613 | #2 | 硬编码占位密钥 | 仍存在 | 同 0816 ② |
| 0613 | #3 | Flash 写入无对齐检查 | 已修复 | gd32_flash_store.cpp:199-276 flashWriteChunk 头尾 pad read-modify-write |
| 0613 | #4 | BLE/WiFi 双通道 ESP32 OTA 冲突 | 已修复 | Esp32OtaCore s_occupied + OtaCoordinator 单例互斥 |
| 0613 | #5 | SPI 帧 XOR 伪校验 | 已修复(迁移) | otaCrc8 已移除,Link v2 用 CRC32-MPEG2(实现在仓外 spi_link,未能直接核对) |
| 0613 | #6 | 0xA5 常量碰撞 | 已修复 | spi_master.h:33 SPI_APP_ENTER_BOOT_ACK_OK=0xB5 |
| 0613 | #7 | OTA 会话无全局超时 | 部分修复 | 传输阶段 120 s 预算 + 150 s 看门狗,但看门狗短于最坏耗时且无法停止中继任务;见 S-03/S-04 |
| 0613 | #8 | CORS 允许所有来源 | 仍存在 | http_handlers.cpp:79 |
| 0613 | #11 | OtaRelay 静态状态无锁 | 部分修复 | 成员已 private + StateLockGuard,reset() 仍可被外部任务调用;见 S-03 |
| 0613 | #16 | nonce 仅用 esp_random 一次/可预测 | 仍存在(形式变化) | 现为 tick+固定地址+常量种子;见 S-06 |
| 0613 | #17 | ota_meta checksum 从未填充 | 已修复 | metaFinalizeChecksum + metaVerifyChecksum(gd32_flash_store.cpp:555-620) |
| 0613 | #18 | ESP32 OTA 无回滚验证 | 部分修复 | 代码调用 mark_app_valid,但调用过早且回滚开关所在配置未生效;见 S-01/S-02 |
| 0613 | #19 | SPI 忽略 timeout | 已修复 | spi_master.cpp:981-1019 有界等待(残留 G-10) |
| 0613 | #20 | esp_ota_write 失败未 abort | 已修复 | esp32_ota_core finalizeCleanupAndReturn/beginCleanupAndReturn |
| 0613 | #22 | SPI 无就绪信号 | 已修复 | DATA_READY GPIO10 ISR + credit 流控 |
| 0627 | P0-1 | metaVerifyChecksum 全零跳过校验 | 已修复 | gd32_flash_store.cpp:563-569 返回 ESP_ERR_INVALID_STATE |
| 0627 | P0-2 | RSSI CRITICAL→NORMAL 无法降级 | 已修复(引入新问题) | health_monitor.cpp:596-598 可直接恢复 NORMAL,但会覆盖过温状态;见 S-10 |
| 0627 | P0-3 | OTA_MAX_FIRMWARE_SIZE 注释不一致 | 仍存在(原结论换算亦有误) | app_config.h:77;216 KiB 恰为 0x36000,注释 0x35F90 错误;见 G-17 |
| 0627 | P0-4 | 凭证硬编码入仓库 | 仍存在 | 同 0816 ② |
| 0627 | P0-5 | BleEsp32FinishCtx 三处 free 易漏 | 已修复 | ble_ota_host.cpp:183-193 由任务负责 free,失败路径统一释放 |
| 0627 | P0-7 | doRelayPackets 180 行 + goto | 已修复 | 拆出 sendRelayPacketWithRetry,阈值命名常量 |
| 0627 | P0-8 | ble_ota_process_data_batch 245 行 | 已修复 | 已拆为 process_esp32_packet / process_gd32_packet(ble_ota_data.cpp:878, 1001, 1167) |
| 0627 | P1-A | OtaRelay 状态成员封装破坏 | 部分修复 | 已 private;锁内外混合访问仍在 |
| 0627 | P1-9/P1-10 | getSnapshot 双重加锁 / 多次取状态 | 已修复 | stateToString + isIdleState,快照持锁一次取值 |
| 0627 | P1-11/P1-12 | esp32_ota_core 清理序列重复 | 已修复 | finalizeCleanupAndReturn / beginCleanupAndReturn |
| 0627 | P1-13/P1-14 | doFinishAndVerify 双职责 / APP 探测重复 | 已修复 | doSendFinishOta + doWaitForAppAlive;probeAndCacheAppAlive |
| 0627 | P1-17 | metaUpdate 读改写非原子 | 已修复 | gd32_flash_store.cpp:432-448 全程持 m_nvsMutex |
| 0627 | P1-18 | metaLoad nvs_open 失败当 OK | 已修复 | gd32_flash_store.cpp:345-355 区分 NOT_FOUND |
| 0627 | P1-22 | s_ota_recv_buf 全局共享 | 已修复 | 0.5.0 改为 OTA 会话内 RAII malloc |
| 0627 | P1-23 | 一次性任务创建失败上报不一致 | 已修复 | ble_ota_host.cpp:130-135, 189-194, 212-215 均 sendFinishEvent |
| 0627 | P1-24 | reorder slot malloc 失败静默丢弃 | 已修复 | ble_ota_data.cpp:466-474 发窗口 ACK 并返回 ESP_ERR_NO_MEM |
| 0627 | P1-25 | protocol.h 死代码 | 未逐条核对 | — |
| 0627 | P2-88 | notifyRelayStarted/Complete 纯转发 | 仍存在 | ota_coordinator.cpp |
| 0627 | P2-89 | 两个通知函数几乎相同 | 部分修复 | 已共用 helper |
| 0627 | P2-92 | 0xF0F0F0F0 魔法数字 | 已修复 | kLastPacketAckSeq |
| 0627 | 其余 P2 | 样板抽取/配置化/风格类约 50 项 | 未逐条核对 | 低风险风格项,本次未逐条复核,建议下轮评审处理 |
| 0505 | UDP | UDP 日志可选、建议可关闭 | 仍存在 | 无开关,默认开启并硬编码目标;见 G-15 |
| 阶段 | 目标 | 条目 | 预估 |
|---|---|---|---|
| P0 · 本周 | 堵住远程控制面,恢复可复现构建 | F-01(token + CORS 白名单 + /spi/send 白名单/默认关闭)、S-12、S-01(合并 sdkconfig.defaults + HZ static_assert)、S-08(轮换 WiFi 口令,凭据迁 NVS) | 2~3 人日 |
| P1 · 2 周内 | 消除变砖与失联路径 | S-02(mark_app_valid 后移)、S-03 + S-04(中继取消标志与分阶段看门狗)、S-05(断线长周期重连)、S-07、S-10、S-11、G-01、G-02 | 4~6 人日,需 GD32 全链路 OTA 回归 |
| P2 · 1 个月 | 加固密码学与无线面 | S-06(esp_fill_random nonce)、S-09(BLE LESC + 加密特征)、0816 ③④(签名 / AES-CTR 或 GCM,需三工程同步)、G-06、G-15 | 5~8 人日(含 GD32 侧) |
| P3 · 持续 | 工程卫生 | G-13(spi_link submodule + 固件构建 CI)、G-03~G-05、G-07~G-12、G-14、G-16、G-17、A-01~A-07 | 随迭代 |
| 维度 | 评分 | 理由 |
|---|---|---|
| 固件架构与分层 | 7 / 10 | 接入/协调/执行/传输分层清晰,协调器单例 + 单 SPI worker 设计合理 |
| SPI 协议与传输 | 6 / 10 | 会话/序号/credit/CRC32 完备,诊断充分;边界与回绕细节待补 |
| OTA 可靠性 | 5 / 10 | 互斥与 epoch 进步明显;中止协作、看门狗预算、回滚时机存在变砖级缺口 |
| FreeRTOS 与实时性 | 6 / 10 | 任务优先级经事故调优;钩子与健康状态逻辑有真实缺陷 |
| 网络与无线安全 | 2 / 10 | 零认证 + CORS * + 原始 SPI 透传 + 硬编码密钥 + BLE 无加密 |
| 代码质量与可维护性 | 6 / 10 | 历史评审的大函数/重复样板多数已整改;超长文件(http_handlers 2.8k 行)仍需拆分 |
| 构建与仓库卫生 | 3 / 10 | 干净克隆不可复现、CI 必败、仓外依赖、过程文件入库 |
| 文档 | 6 / 10 | 白皮书详尽,但版本/任务表漂移,历史评审被静默删除 |
| 综合 | 5.1 / 10 | 链路与 OTA 工程扎实、诊断能力强,历史评审的结构性问题大多已整改;但“可信局域网”前提下的零认证控制面、不可复现的构建配置、回滚时机与中止协作缺陷,使其距离量产/现场部署仍有明显距离。 |
docs/project_introduction.html §19 的跟踪;后续评审请统一归档到 docs/review/,不要随文档整理删除。本报告由 Hark 专家评审生成 · 2026-10-19 · 基线 af1f682。行号以该提交为准,后续提交可能漂移。
+SpiMaster 是单例但没有任何互斥锁。多个 FreeRTOS 任务直接调用 SPI 方法:ota_relay 任务 (优先级 MAX-2)、wifi_ota 任务 (MAX-3)、health_monitor 任务 (MAX-4)。协议操作是多步非原子的(如 otaTransfer = 发送帧 + 轮询响应,CS在两次传输之间释放),高优先级任务可在协议序列中间抢占。
+SemaphoreHandle_t m_mutex,所有公共方法入口加锁。对于多步协议操作(otaTransfer等),使用统一的锁粒度保护整个请求-响应序列。
+ MASTER_KEY 使用 NIST AES 测试向量,SCRAMBLE_CONST 使用简单递增序列,GD32_CHIP_UID 使用 0x00-0x0B。注释标注"需填入实际密钥"但从未替换。
+两个调用路径产生非对齐值:(1) WiFi OTA: gd32_offset 从 httpd_req_recv 返回值累加,最后一个chunk是固件余数;(2) BLE OTA: data_len = recv_len - 11(7字节头+4字节CRC),不保证4字节对齐。esp_partition_write 要求 offset 和 size 均4字节对齐。
+BleOta 和 WifiOta 各自维护独立的 m_esp32OtaActive、m_esp32OtaHandle、m_esp32UpdatePartition,但底层 esp_ota_begin/write/end 操作的是同一个OTA分区。两个通道同时触发时:第二个 esp_ota_begin 会失败或覆盖句柄;esp_ota_write 可能交错写入同一分区;导致固件损坏+变砖。
+XOR校验无法检测:偶数个相同位置的位翻转、字节交换、全零段丢失。固件数据的静默损坏几乎不可检测。
+同一值 0xA5 表示两种完全不同的语义。GD32在OTA窗口回复ACK时,ESP32可能将ACK字节误解析为OTA帧起始,导致协议状态机混乱。
+waitUntilNotWait() 每次调用有 max_wait_rounds 上限,但整个 forwardFirmware() 流程没有全局超时。GD32 持续返回 WAIT 时,单个 SEND_SIZE阶段可挂起 ~78秒,完整流程可挂起数分钟。OTA_RESULT_ERR_USER_ABORT 已定义但从未使用。
+任何网页都可以向 ESP32 发起 OTA 请求。结合占位密钥问题,攻击链完整:恶意网页 → 上传伪造固件 → 通过占位密钥认证 → 烧录恶意GD32固件。
+| # | 严重度 | 文件 | 问题 | 影响 |
|---|---|---|---|---|
| 9 | HIGH | wifi_ota.cpp:121 | WiFi事件回调中调用 esp_wifi_connect() 重连 | 在系统事件任务中调用可能死锁 |
| 10 | HIGH | health_monitor.cpp | healthState 静态变量无锁跨任务读写 | 竞态导致健康判断错误 |
| 11 | HIGH | ota_relay.h:80-84 | 5个静态变量(relayState等)无锁跨任务访问 | 协议状态不一致 |
| 12 | HIGH | wifi_ota.h:33-47 | WifiOta 所有成员 public 且无锁 | HTTP回调与WiFi事件回调竞争 |
| 13 | HIGH | ble_ota.h | BleOta 成员无锁,BLE GATT回调与主任务竞争 | BLE OTA 数据损坏 |
| 14 | HIGH | debug_log.cpp:296 | log() 栈分配 msg[512] + log_buf[600] = 1112字节 | 低栈任务中栈溢出 |
| 15 | HIGH | wifi_ota.cpp:1008 | spi_send_handler 栈分配 1728+ 字节 | HTTP server栈仅8KB,接近溢出 |
| 16 | HIGH | ota_relay.cpp:456 | sendNonceAndAuthenticate() nonce 仅用 esp_random() 一次 | 认证会话密钥可预测 |
| 17 | HIGH | ota_relay.h:25 | ota_meta_t.checksum[32] 从未填充/验证 | 固件完整性无保障 |
| 18 | HIGH | wifi_ota.cpp:429-444 | ESP32 OTA 完成→重启,无回滚验证 | 新固件若无法启动将变砖 |
| 19 | HIGH | spi_master.cpp:258 | syncTransfer/recv 忽略 timeout_ms 参数 | SPI事务可能无限等待 |
| 20 | HIGH | wifi_ota.cpp:599-629 | esp_ota_write 失败后 break,未调用 esp_ota_abort | OTA分区处于不一致状态 |
| 21 | HIGH | ble_ota.cpp:439-444 | BLE OTA 帧预留4字节CRC但减去计算不匹配 | 最后几个字节被误截断 |
| 22 | HIGH | spi_master.h | SPI仅4线,无硬件流控/就绪信号 | 高速传输时GD32来不及处理 |
项目中有 5个静态全局变量 (relayState, relaySeq, relayTotalPackets, gd32AppAlive, gd32Version) 和 大量public成员 (WifiOta/BleOta所有状态),跨越3+个FreeRTOS任务和多个ISR/callback上下文访问,没有任何一个被mutex/atomic/critical section保护。这是系统性问题,不是个别bug。
+SpiMaster 单例是唯一的SPI通信通道,但没有任何仲裁机制。WiFi/BLE/健康监控/OTA中继四个功能模块各自直接调用,协议级操作(请求-响应对)不是原子的。需要从架构层面引入SPI总线仲裁器。
+认证握手存在但密钥硬编码→形同虚设;固件校验字段存在但未填充→形同虚设;CORS开放→任何人可触发OTA。安全功能"看起来有"但实际上不提供保护,这种"安全假象"比没有安全功能更危险,因为它给了虚假的信心。
+| 优先级 | 修复项 | 工作量 | 紧急度 | 进度 |
|---|---|---|---|---|
| P0 | +添加 SPI 总线互斥锁 | +小 | +极高 — 生产环境必崩 | ++ |
| P0 | +替换硬编码密钥为安全存储 | +中 | +极高 — 安全漏洞 | ++ |
| P0 | +Flash 写入对齐检查 | +小 | +极高 — 数据损坏 | ++ |
| P1 | +添加全局 OTA 超时+中止机制 | +中 | +高 — 设备挂死 | ++ |
| P1 | +ESP32 OTA 添加回滚验证 | +中 | +高 — 变砖风险 | ++ |
| P1 | +替换 XOR 为 CRC-8/16 校验 | +小 | +高 — 固件损坏 | ++ |
| P1 | +消除 0xA5 值碰撞 | +极小 | +高 — 协议混乱 | ++ |
| P2 | +添加 OTA 单源互斥 (BLE/WiFi) | +中 | +中高 | ++ |
| P2 | +为所有跨任务状态添加锁/atomic | +大 | +中高 | ++ |
| P2 | +修复 WiFi 事件回调中的重连调用 | +小 | +中 | ++ |
| P3 | +修复大栈分配问题 | +小 | +中 | ++ |
| P3 | +限制 CORS 来源 | +小 | +中 | ++ |
| P3 | +实现 ota_meta_t 校验填充与验证 | +中 | +中 | ++ |
| 原始严重度 | 问题描述 | 驳回原因 |
|---|---|---|
| CRITICAL | +SPI DMA缓冲区栈分配导致数据损坏/硬Fault | +ESP-IDF 6.0 的 spi_master 驱动内置 setup_dma_priv_buffer() 函数自动检测并对齐DMA缓冲区,栈分配仅导致性能损失,不引发数据损坏 |
+
| HIGH | +TOCTOU: relayState 检查后 reset() 无锁 | +虽然模式存在,但当前代码流程中检查和重置在同一线程同步执行,实际竞态窗口极小,风险被高估 | +