docs(net-audit): B1 服务器侧 KeepAlive 前提被实测推翻,撤回改动并记台架注入陷阱

B1 原判"产品路径从未启用 Socket KeepAlive → 静默断开无人知晓"(§5.56
要求 TCP 服务器在产生 SINT_STAT_CONNECT 后开启,产品三服务全是
Server,60s/5s/3 参数空转)。本轮按 §5.56 在 LISTENING→ESTABLISHED
分支补了 ch395f_set_keepalive_enable() 并上板做定案实验,结果推翻前提。

实测:连接静置时禁用 PC 网卡(真·静默死亡),芯片 +2 s 即报
"[ERR][NET] sock0 TIMEOUT" → 本层 relisten quarantine 释放重开监听。
2 s 只能是重传(§5.17 本仓 8×200 ms = 1.6 s 量级),与 KeepAlive 无
关——IDLE 最短 60 s、芯片默认也要 20 s。链路层失效本就被快速覆盖,
故依 Trap 25 铁律「不留未证明的复杂度」撤回该改动,只在代码内留注释
记录(含未覆盖场景与注入难点),不保留无证据的动作。

另一半天真:设备 2 s 就重开监听,PC 侧那条 socket 却继续"自认存活"
直到 4 分钟后自行 close(Python 对死链路 close() 不报错)。不对称恢复
才是现场隐患——客户端下次写会吃到 §5.29 的 RESET;单靠设备侧开
KeepAlive 只解决设备这一半。

新增「台架注入陷阱(2026-10-12)」四条(本轮踩到的,全部实测):
1. Windows 出站防火墙规则不能模拟"对端静默死亡"——WFP 对已建立的流
   有状态缓存,中途加 Block 不影响既有连接;实测静置+阻断 250 s 芯片
   毫无反应,看着像"KeepAlive 不生效",实则 ACK 一直正常往返。
2. 该规则还会吃掉本机后发的 FIN → 设备留下幽灵 ESTABLISHED,之后新
   连接被 §5.29"向试图连接的远端发送 TCP RESET"直接 RST,表现为"端口
   拒连",极易误判成设备故障(实为自伤,撤规则后自愈)。
3. 真正掐断链路用 Disable-NetAdapter;本机该 cmdlet 只有 -Name /
   -InterfaceDescription,没有 -InterfaceIndex/-ifIndex(第一次写错
   导致那轮网卡根本没关)。网卡名是中文,用
   (Get-NetAdapter -InterfaceIndex 19).Name 取值再传,避免命令行写中文;
   禁用/恢复必须写在 try/finally 里。
4. 仍未覆盖:"链路仍在、对端静默"(NAT 会话过期、中间节点黑洞)才是
   服务器侧 KeepAlive 唯一可能的价值场景,本机无安全注入手段(静态
   ARP 黑洞会被交换机泛洪兜底、删 IP 会连带拆掉本机连接)——未证实,
   故不保留改动。

判读教训(与 Trap 25 同源):看到 TIMEOUT 先算时序再归因,2 s 与 60 s
一眼可分;否则会把重传超时误记成"KeepAlive 生效",把相关性当因果。

验证:构建 0 错 0 警、Code=196912(与 afb0bfd 一致,说明撤回后二进制
回到已提交状态);doc-links EXIT=0、Trap_Records 无 U+FFFD 无 BOM;
@test 闭环 PASS(回读无差异 + forbidden 三项与 [ERR] 全缺席 + 594 ms)。
This commit is contained in:
2026-10-12 07:49:58 +08:00
parent afb0bfd5ff
commit 9a45be1700
2 changed files with 17 additions and 1 deletions
+9 -1
View File
@@ -938,7 +938,7 @@ Trap 22 的周期对账(200ms 节拍 + 400ms 去抖 + 1s 冷却)设计目标
| A4 | 低 | `ch395f.h:33-91`、`ch395f.c:484/506`、`ch395f.h:701` | 分组注释字节数错 4 处(`GET_GLOB_INT_STATUS_ALL`/`GET_UNREACH_IPPT` 归"1 字节输出"、`SET_FUN_PARA` 归"1 入 1 出"、`SET_RETRAN_PERIOD` 归"3 字节输入"、`GET_SOCKET_STATUS_SN` 归"1 字节输出");KeepAlive 参数注释写"秒"(§5.53/5.54 是 ms);`set_retrans_count` 写"0=默认值5"(§5.17 默认 12、无 0=默认语义)。**实现全部正确,注释误导**——Trap 25 的 1 字节缓冲栈越界正是这类注释发起的 | ✅ 已修 |
| A5 | 低 | `net_socket.c:238/1005/1080/1297/1666/708/3056` | 注释与手册/HEAD 相反:`FUN_PARA(SOCKET_CLOSE=0)`(实为 bit3=1,正是 closed-with-data accept 的契约依据)、"初始化成功后设置 MAC"(§9.2.1 ① 是之前)、"复用默认缓冲"(已 8 Socket 显式分配)、"布局由 MSS 推导"(固定布局、Trap 21 已解耦)、"等 SEND_OK"(门控是 SENBUF_FREE)、UDP 头 `[0-1] reserved`(§9.2.4 是数据长度) | ✅ 已修 |
| A6 | 低 | `ch395f.c:1176` | `write_send_buf` 超限**静默 return**(void、无日志),且上限取 DMA 2048 而非发送缓冲 1024;读路径有 OVERSIZE 金丝雀、写路径没有 | ✅ 已修(补 `[ERR]` 留痕 + >1 KB 告警,行为不变) |
| B1 | **中** | `net_socket.c:3553` | **产品路径从未启用 Socket KeepAlive**:`set_keepalive_enable` 唯一调用点在 `CONNECTING`(client)分支,产品三个服务(21/8888/7003)全是 TCP Server,`net_connect()` 只有测试在调 → 60 s/5 s/3 参数空转。§5.56 明文"Socket 为 TCP 服务器时,在产生 `SINT_STAT_CONNECT` 后打开 Keepalive",官方 `EXAM5_TCP_Server` 正是在 CONNECT 分支调 | ⏳ 待修(LISTENING→ESTABLISHED 分支补一行;需实测静默断开能否触发 `TIM_OUT`) |
| B1 | ~~中~~ **已推翻** | `net_socket.c:3553` | 原判"产品路径从未启用 Socket KeepAlive → 静默断开无人知晓"(§5.56 服务器侧应在 CONNECT 后开,产品三服务全是 Server,60s/5s/3 参数空转)。**2026-10-12 实测推翻前提**:连接静置时禁用 PC 网卡(真·静默死亡),芯片 **+2 s** 即报 `[ERR][NET] sock0 TIMEOUT` → 本层 `relisten quarantine` 释放重开;2 s 恰为 §5.17 重传 8×200 ms=1.6 s 量级,**与 KeepAlive 无关**(IDLE 最短 60 s)。链路层失效本就被快速覆盖。 | ✅ 已按 §5.56 补过使能并实测,**依 Trap 25「不留未证明的复杂度」撤回**,代码内留注释记录;残余场景见下「台架注入陷阱」 |
| B2 | 中低 | `net_socket.c:1638` | `ch395f_tcp_connect()` 返回值被丢弃(同文件 open/listen 都查),命令级被拒不产生中断 → 停在 `CONNECTING`,要等 30 s recv 超时才知失败;且 `net_reconcile_tick` 只裁 ESTABLISHED/LISTENING,`CONNECTING` 无人收口 | ⏳ 待修 |
| B3 | 低 | `net_socket.c:1120-1123` | `net_listen_locked` 对 8 个 Socket 全量 `GET_INT_STATUS_SN`(读即清)→ 吞掉其它在用 Socket 的挂起事件(RECV 是电平语义可下轮补回,CONNECT/DISCONNECT 边沿语义会丢) | ⏳ 待修(只清本 Socket) |
| B4 | 低 | `net_socket.c:446/469` | 全局中断 bit0-3(`UNREACH`/`IP_CONFLI`/`PHY_CHANGE`/`DHCP`)被读清但从不消费;`ch395f_get_unreach_info()` 全仓零调用 → IP 冲突、ARP 不可达、链路抖动三类事件完全静默 | ⏳ 待修(可观测性缺口) |
@@ -958,3 +958,11 @@ Trap 22 的周期对账(200ms 节拍 + 400ms 去抖 + 1s 冷却)设计目标
- 已失效条目未标注:Trap 01/02/11 的多连接模型随 2026-08-25 `319d00e3` 删除;Trap 09 的 `osDelay(7000)` 已被就绪屏障取代;Trap 10 的"`net_connect()` 不使用消息队列"已失效;Trap 12 的 #2/#4 与仓内 FatFs 源码矛盾(`ff.c:3101-3109` 剥前导分隔符、`:3127-3129` 空名返回根目录),且 `FF_FS_RPATH=0` 下 `"."` 不是合法路径段(HEAD 用 `"0:"`)。
- 日期:Trap 01/02/03/04/06/07 的"2025-07"均早于仓库首提交 `b1d05091`(2026-06-05);Trap 03/04/06 的 Phase 编号也错(DHCP=阶段 4、UDP=阶段 3、KeepAlive=阶段 11)。
- 证据通则:`*.log` 在 `.gitignore` 内 → 本文所有台架计数(40/40、34/34、15/15、4 轮 11/11、13/13…)**脚本在库、日志未入库 = 不可复核**;Trap 25 引用的实验分支 `n30-chip-bench`(tip `eddbede`)不含 bench 源文件与 uvprojx 注册,裸机芯片台架无法从任何已提交状态重建。
### 台架注入陷阱(2026-10-12,B1 复核过程中踩到)
- **Windows 出站防火墙规则不能用来模拟"对端静默死亡"**:WFP 对**已建立**的连接有状态缓存,中途 `New-NetFirewallRule -Direction Outbound -Action Block -RemoteAddress <设备IP>` 不影响既有流——实测静置 + 阻断 250 s 芯片毫无反应,看似"KeepAlive 不生效",实则 ACK 一直在正常往返。
- 更糟的副作用:该规则会**吃掉本机后来发出的 FIN** → 设备侧留下一条**幽灵 ESTABLISHED**,之后所有新连接被 §5.29"向试图连接的远端发送 TCP RESET"直接 RST,表现为"端口拒连",容易被误判成设备故障(实为自伤)。撤规则后设备自行恢复。
- **能真正掐断链路的手段**:`Disable-NetAdapter`(链路真的 down)。⚠ 本机 PowerShell 的该 cmdlet **只有 `-Name` / `-InterfaceDescription`**,没有 `-InterfaceIndex`/`-ifIndex`;网卡名是中文,用 `(Get-NetAdapter -InterfaceIndex 19).Name` 取值再传入,避免命令行里写中文字面量。禁用/恢复必须写在 `try/finally` 里,否则网卡留在关闭状态。
- **仍未覆盖的场景**:`Disable-NetAdapter` 只能造"链路层失效";"**链路仍在、对端静默**"(NAT 会话过期、中间节点黑洞)本轮没有安全注入手段(静态 ARP 黑洞会被交换机泛洪兜底、删 IP 会连带拆掉本机连接)。这才是服务器侧 KeepAlive 唯一可能的价值场景,**未证实**,故不保留该改动。
- **判读教训(与 Trap 25 同源)**:看到 `TIMEOUT` 先**算时序再归因**——2 s 与 60 s 一眼可分;否则会把"重传超时"误记成"KeepAlive 生效",把相关性当因果。