fix(net-audit): 修 6 处寄存器口径/注释错误 + Trap 记录全量复核与审计章节

智能体团队四路并行只读审计(驱动层逐命令 / NET+测试层逐调用点 /
Trap 01-12 复核 / Trap 13-25 复核),三源交叉:芯片手册 + WCH 官方
SDK(CH395CMD.c / CH395INC.H / 21 个例程)+ git 历史。本提交只落
"已确认且低风险"的修法,其余发现登记进文末审计章节待逐项决策。

已修(A 类,全部有手册/SDK 出处):
A1 测试 TC-203 反核实写成 st_chk[0]==CH395F_TCP_ESTABLISHED——§5.22
   byte0 只有 00H/05H,条件恒假,这道 Trap 14 防线自写下起从未生效
   且日志无痕(与 Trap 07 同类的 byte0/byte1 语义越界)。改为
   byte0==OPEN && byte1==ESTABLISHED。
A2 测试 Phase3/4 把 GET_RECV_LEN 返回值直接当读取长度(本地
   rx_buf[300]/[64],芯片 RX 缓冲 2KB)→ 栈溢出;补夹取 + 留痕
   (同文件 TC201/202/204 早已 clamp,这两处是漏)。
A3 CH395F_GINT_STAT_DHCPV6 由 0x8000(bit15) 改 0x1000:SDK 是
   (1<<12)、§5.16 明确 CH395F 的 [12:15] 保留。全仓零引用。
A4 ch395f.h 命令分组注释字节数错 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 六处注释与手册/HEAD 相反:FUN_PARA(SOCKET_CLOSE=0)
   实为 bit3=1(正是 closed-with-data accept 的契约依据,写反会诱导
   把 0x08 改回去)、"初始化成功后设置 MAC"(§9.2.1 ① 是之前)、
   "复用默认缓冲"(net_init 已为 8 Socket 显式分配)、"由 MSS 推导"
   (固定布局、Trap 21 已解耦)、"等 SEND_OK"(门控是 SENBUF_FREE)、
   UDP 头 [0-1] reserved(§9.2.4 是数据长度)。
A6 ch395f.c write_send_buf 超限静默 return(void 无日志),且上限取
   DMA 2048 而非发送缓冲 1024;读路径有 OVERSIZE 金丝雀、写路径没有
   → 补 [ERR] 留痕 + >1KB 告警,行为不变(分块责任仍在调用方)。

审计确认"不是 bug"的最危险一项:GET_GLOB_INT_STATUS_ALL 拼装方向。
我们"第1字节|第2字节<<8"与官方 CH395CMDGetGlobIntStatus() 逐字相同,
glob & (1<<(sock+4)) = §5.16 bit4..bit11 = SDK GINT_STAT_SOCKn,三源
一致。另 55 条命令码、9 处 16 位字节序、14 条读命令字节数、60 个
常量、12 个单位参数点、48 块缓冲布局逐条核对无异常;17 处
get_socket_status 调用点全是 st[2],Trap 25 的 1 字节 bug 无残留。

文档:Trap_Records 加文末「2026-10-11 寄存器/数值口径全量审计」章节
(干净清单计数 + 15 条新发现含已修/待修 + 文档待修清单)。就地修正:
Trap 07 定案更正里"全文 RST 仅见于错误码与引脚"的绝对表述(§5.29 /
§9.2.6 其实写了芯片发 TCP RESET,只是都与 close 无关)+ 删不可核验的
"11 个悬空提交";Trap 16 的"IP/端口寄存器大端"(端口 §5.25/5.26 明确
低字节在前)+ 注明 §5.8 总括与官方例程自相矛盾;Trap 18 删悬空
"Trap 19"(该条从未存在)、net_socket() 改 net_bind();速查清单 DMA
1500 改 2052;Trap 13 加复核注(最高优先级:其"部分读后 recv_len
归零"与 §5.34 直接相左、与本仓 TC201 注释相反,且同族结论已被 Trap 21
撤回,却仍被 6 处当现行约束引用);Test_Guide 的 TRAP-07 行仍教
"卡 FIN_WAIT_2 / close(RST)"、悬空的 Trap 19 与两处"见风险 #N"。

待修(B 类,登记未动,需逐项决策与实测):B1 产品路径从未启用 Socket
KeepAlive(唯一调用点在 client 分支,产品三服务全是 TCP Server,
§5.56 要求服务器在 CONNECT 后开,60s/5s/3 参数空转);B2 tcp_connect
返回值丢弃且 CONNECTING 不在对账范围;B3 net_listen_locked 全 8 Socket
读即清会吞别槽事件;B4 全局中断 bit0-3 从不消费、get_unreach_info 零
调用;B5 getsockname 的 s_addr 与本层约定相反致 FTP 日志设备 IP 反序
(PASV 双重反转恰好正确,勿误判);B6 三处判据只看 byte1 未按 byte0
门控;B7 Phase5 单次写 4093B;B8 Phase4 缓冲块与 sock4 交叠;B9 杂项。

验证:产品构建 0 错 0 警、Code=196912(+124B,写路径两条新日志);
测试路径(PHASE2/3/4 打开)单独编译 0/0;内存快照已刷新;doc-links
EXIT=0、无 U+FFFD、pre-commit 全绿;@test 闭环 PASS(戳匹配 + 回读无
差异 + 锚点 x1 + 548ms)。
This commit is contained in:
2026-10-12 00:20:17 +08:00
parent bca6cc694c
commit afb0bfd5ff
7 changed files with 120 additions and 33 deletions
+13 -2
View File
@@ -481,7 +481,7 @@ void ch395f_clear_recv_buf(uint8_t sock) {
/*
* 函数功能:设置TCP KeepAlive空闲时间
* 入口参数:time - 空闲时间(秒) uint32_t
* 入口参数:time - 空闲时间(毫秒,须为 500 的倍数) uint32_t
* 限定条件:必须在ch395f_init()之前调用
*/
void ch395f_set_keepalive_idle(uint32_t time) {
@@ -503,7 +503,7 @@ void ch395f_set_keepalive_idle(uint32_t time) {
/*
* 函数功能:设置TCP KeepAlive探测间隔
* 入口参数:time - 探测间隔(秒) uint32_t
* 入口参数:time - 探测间隔(毫秒,须为 500 的倍数) uint32_t
* 限定条件:必须在ch395f_init()之前调用
*/
void ch395f_set_keepalive_intvl(uint32_t time) {
@@ -1174,8 +1174,19 @@ void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
/* 计算总长度:1字节命令 + 1字节Socket + 2字节长度 + 数据 */
total = 4U + len;
if (total > CH395F_DMA_BUF_SIZE) {
/* 异常金丝雀:与读路径对称——静默丢弃会让数据与错误一起消失(无从诊断),
* 故必须留痕。正常路径不会到达此处(NET 层按 NET_SEND_CHUNK_MAX 分块)。 */
DBG_ERROR("write_send_buf OVERSIZE: sock %u req %u > max %u, write dropped",
(unsigned)sock, (unsigned)len, (unsigned)(CH395F_DMA_BUF_SIZE - 4U));
return;
}
if (len > (uint16_t)CH395F_SEND_BUF_SIZE_BYTES) {
/* §5.32:单次写入长度不得大于该 Socket 的发送缓冲区(本仓 = 2 块 = 1KB)。
* 超出不会被命令拒绝,而是写穿发送缓冲导致数据错乱(Trap 20)→ 只告警不拦截,
* 分块责任在调用方(NET 层 NET_SEND_CHUNK_MAX)。 */
DBG_ERROR("write_send_buf OVERSIZE-SEND BUF: sock %u len %u > send buf %u (Trap 20)",
(unsigned)sock, (unsigned)len, (unsigned)CH395F_SEND_BUF_SIZE_BYTES);
}
/* 打包命令头到DMA发送缓冲区 */
s_spi2_dma_tx_buf[0] = CH395F_CMD_WRITE_SEND_BUF_SN;
+12 -6
View File
@@ -34,11 +34,13 @@ extern "C" {
#define CH395F_CMD_GET_IC_VER 0x01U
#define CH395F_CMD_GET_CMD_STATUS 0x2CU
#define CH395F_CMD_GET_GLOB_INT_STATUS 0x29U
#define CH395F_CMD_GET_GLOB_INT_STATUS_ALL 0x19U
#define CH395F_CMD_GET_UNREACH_IPPT 0x28U
#define CH395F_CMD_GET_PHY_STATUS 0x26U
#define CH395F_CMD_GET_DHCP_STATUS 0x42U
/* 无输入数据,多字节输出(§5.16 / §5.14,勿按 1 字节读) */
#define CH395F_CMD_GET_GLOB_INT_STATUS_ALL 0x19U /* 2 字节 */
#define CH395F_CMD_GET_UNREACH_IPPT 0x28U /* 8 字节 */
/* 无输入数据,6 字节输出 */
#define CH395F_CMD_GET_MAC_ADDR 0x40U
@@ -53,15 +55,17 @@ extern "C" {
/* 1 字节输入,1 字节输出 */
#define CH395F_CMD_CHECK_EXIST 0x06U
/* 4 字节输入,无输出数据(§5.52,勿按 1 字节写) */
#define CH395F_CMD_SET_FUN_PARA 0x55U
/* 2 字节输入,无输出数据 */
/* 2 字节输入(低字节在前),无输出数据 */
#define CH395F_CMD_SET_TTL 0x51U
#define CH395F_CMD_SET_ARP 0x44U
#define CH395F_CMD_SET_RETRAN_PERIOD 0x2BU
/* 3 字节输入,无输出数据 */
#define CH395F_CMD_SET_BAUDRATE 0x02U
#define CH395F_CMD_SET_RETRAN_PERIOD 0x2BU
/* 4 字节输入,无输出数据 */
#define CH395F_CMD_SET_IP_ADDR 0x22U
@@ -250,7 +254,9 @@ extern "C" {
#define CH395F_GINT_STAT_SOCK5 0x0200U
#define CH395F_GINT_STAT_SOCK6 0x0400U
#define CH395F_GINT_STAT_SOCK7 0x0800U
#define CH395F_GINT_STAT_DHCPV6 0x8000U
/* bit12 DHCPv6:官方 SDK 定义为 (1<<12),但 §5.16 明确 CH395F 的 [12:15] 为
* 保留位(仅 CH395X 系列产生)——本芯片上恒为 0,勿用作判据 */
#define CH395F_GINT_STAT_DHCPV6 0x1000U
/*
* Socket 中断状态位(CMD_GET_INT_STATUS_SN 返回值,来自手册表5-x)
@@ -698,7 +704,7 @@ void ch395f_set_keepalive_enable(uint8_t sock, uint8_t enable);
/*
* 函数功能:设置 TCP 重传次数
* 入口参数:cnt - 重传次数 uint8_t(0=使用默认值5)
* 入口参数:cnt - 重传次数 uint8_t(§5.17:默认 12 次,最大 20,>20 按 20 处理;无 0=默认语义)
* 限定条件:必须在 ch395f_init() 之前调用
*/
void ch395f_set_retrans_count(uint8_t cnt);
+10 -10
View File
@@ -235,8 +235,8 @@ static bool s_mac_is_zero(const uint8_t *p_mac) {
* 2. 启用 TCP Server 多连接模式(bit1=1),设置 TCP MSS、重传参数、KeepAlive 参数
* 3. 配置 IP/网关/子网掩码,或启用 DHCP
* 4. 初始化 CH395F 协议栈并使能 PING 响应
* 5. mac 非空且非全零:初始化成功后设置 MAC(写芯片 EEPROM,
* 每上电重放同值幂等)
* 5. mac 非空且非全零:初始化**之前**设置 MAC(§9.2.1 ①,Trap 24;
* 写芯片 EEPROM,每上电重放同值幂等)
*/
int net_init(const char *ip, const char *mask, const char *gateway, const uint8_t *mac) {
ch395f_status_t ret;
@@ -705,8 +705,8 @@ void net_process_messages(void) {
net_set_errno(NET_ERR_NOTCONN);
msg.result = -1;
} else {
/* 真正写入芯片:1KB/块等 SEND_OK 中断,单块停滞 1s 超时,
* 中途断连立即 CONNRESET;短写留痕,调用方须检查返回值 */
/* 真正写入芯片:1KB/块等 SENBUF_FREE 中断(§5.32/§5.23①),
* 单块停滞 1s 超时,中途断连立即 CONNRESET;短写留痕,调用方须检查返回值 */
msg.result = net_send_locked(p_sock, msg.buf, msg.len);
}
break;
@@ -1002,7 +1002,7 @@ int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen) {
uint8_t status;
/* net_init 已为全部 8 个 Socket 分配缓冲;此处仅对 Socket 4~7 兜底重设,
* 使用与 net_init 相同的布局(由 MSS 推导,见 net_init) */
* 使用与 net_init 相同的布局(固定 6 块/Socket,与 MSS 解耦,见 net_init / Trap 21) */
if (sockfd >= 4) {
/* 每 Socket 块数 = 接收 + 发送 */
uint8_t blocks_per_socket = (uint8_t)(CH395F_RECV_BLOCKS + CH395F_SEND_BLOCKS);
@@ -1077,8 +1077,8 @@ int net_listen_locked(int sockfd) {
return NET_ERR_RETURN_1(NET_ERR_INVAL);
}
/* 单连接:监听 Socket 自身即数据通道,复用 CH395F 默认收发缓冲,
* 不额外分配(Socket 0~3 默认缓冲互不重叠) */
/* 单连接:监听 Socket 自身即数据通道;收发缓冲已在 net_init 为全部
* 8 个 Socket 显式重分配(6 块/Socket,net_socket.c:339-345),此处不再分配 */
/* 设置协议类型为 TCP */
ch395f_set_proto_type(sockfd, CH395F_PROTO_TYPE_TCP);
/* 本地端口 = 控制块 local_port */
@@ -1294,7 +1294,7 @@ static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen
/* N-30(2026-10-11 手册 §5.23⑤ + 生产时间线定案):
* "closed-but-readable" 接受——STOR 类传输客户端**发完数据立即关数据连接**
* (正常 FTP 行为,Python ftplib sendall 后即 close),芯片按
* FUN_PARA(SOCKET_CLOSE=0) 规格保留 Socket 与 RX 缓冲供 MCU 排空
* FUN_PARA **bit3 SOCKET_CLOSE=1**(`0x08`,§5.52 ②)规格保留 Socket 与 RX 缓冲供 MCU 排空
* (实测 DISCONNECT 后 st=0x05/0x00、recv_len=1027=完整载荷)。
* 若 accept 轮询晚于对端 FIN,盲目 relisten(close+open 清缓冲)会把
* 已到达的数据销毁 → 425(此前 N-30"425/被吞"的主根因,并非芯片杀连接)。
@@ -1663,7 +1663,7 @@ static int net_connect_locked(int sockfd, const struct net_sockaddr *addr, int a
* 限定条件:p_sock 非空;Socket 处于 ESTABLISHED 状态;netTask 上下文
* 函数说明:1. 按 NET_SEND_CHUNK_MAX(硬件发送缓冲上限,见 Trap 20)分块写入 CH395F 发送 FIFO
* 2. 发送前等待 send_ready 置位,超时返回已发送字节数
* 3. 每写完一块等待 SEND_OK 中断(send_ready 清零后重新置位)
* 3. 每写完一块等待 SENBUF_FREE 中断(send_ready 清零后重新置位,§5.32/§5.23①)
* 4. 发送中途断连(状态非 ESTABLISHED)→ 立即失败并返回已发字节
* (errno=CONNRESET,不再空等超时)
*/
@@ -3053,7 +3053,7 @@ int net_recvfrom(int sockfd, void *buf, int len, int flags,
struct net_sockaddr_in *p_addr_in = (struct net_sockaddr_in *)src_addr;
/* 地址族 = IPv4 */
p_addr_in->sin_family = NET_AF_INET;
/* CH395F UDP 头 [0-1] reserved [2-3] src_port(LE) [4-7] src_ip */
/* CH395F UDP 头 [0-1] 数据长度(LE) [2-3] src_port(LE) [4-7] src_ip(§9.2.4 表) */
/* 源端口(小端 2 字节 → 主机序 → 网络序) */
p_addr_in->sin_port = net_htons((uint16_t)(header[2] | (header[3] << 8)));
/* 源 IP(小端 4 字节组装为网络序) */