评审日期 2026-10-19 · 代码基线 master af1f682(固件 v0.5.2,ESP-IDF v6.0,ESP32-C3FH4)
评审方式:多专家视角并行通读 + 主审逐条复核 · 本报告只新增 docs/,未改动任何源码
综合评分 5.1 / 10
ESP32_COMM 是机器人 DCU 板上 ESP32-C3 的通信与 OTA 中枢固件:上位机经 WiFi HTTP / BLE GATT 把固件交给 ESP32;ESP32 自身走 app0/app1 AB 分区升级,GD32E508 固件经 SPI Link v2 六阶段握手 + AES-128 逐包加密中继,电机节点固件再经 GD32 CAN 二次中继;同时提供三路 CANFD 控制面与遥测回收。
[PC / 手机网页]──WiFi HTTP(22 路由) / BLE GATT(0xFFFF)──▶[ESP32-C3FH4]──SPI Link v2 13.33MHz 5线──▶[GD32E508]──3×CANFD──▶[电机节点]
│ gd32_ota 252KB 缓存 │ Bootloader / APP
│ app0/app1 AB 自升级 │ 电机 OTA CAN 中继
| 层 | 目录 / 模块 | 职责 | 规模(行) |
|---|---|---|---|
| 接入层 | 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。行号以该提交为准,后续提交可能漂移。