fix(NET,lftpd): 处置覆盖率照出的三处死路径(改名 + 修 timeout=0 + 激活异常集)
三处都由本轮 P1 新增的主机单测照出,用户逐条裁定后实施。共同性质:都不是改动引入
的回归,而是**从没被执行到的分支**——生产路径上要么没有调用者、要么没走边界,所以
编译器(0 错 0 警)、cppcheck、真机日志对它都无话可说。
1) 缺陷一(选 a:改名,行为零变化)
lftpd_string_trim 的尾部循环起点落在 strlen(p) 那一格 NUL,isspace(0) 为假 →
循环体一次都不执行,"尾部空白从未被裁",而名字承诺两头都去。改名
lftpd_string_ltrim + 删掉那条死循环 + 在 .h/.c 写明"只去前导"。不去尾才是对的:
RFC 959 以命令终止符 CRLF 界定命令,参数(PASS 口令、含空格的文件名)的尾随
空格属于参数本体,去掉等于静默改数据;CRLF 已由 lftpd_inet_read_line 截断。
调用点 lftpd.c 同步改名。全库无遗留真引用(余 6 处旧名均为解释改名历史的散文)。
2) 缺陷二(按推荐修)
net_select 的 timeout = {0,0} 换算出 timeout_ms = 0,而退出条件写着
if ((timeout_ms > 0) && (已用时 >= timeout_ms)) → 超时永不生效,退化成死等;
POSIX 语义是"只查一轮立即返回"。去掉 timeout_ms > 0 这个错误前置即可。就绪
检查在本判断之前,故有就绪时仍立刻带计数返回;同文件 net_poll_events 的
timeout >= 0 本来就对,现在两条对齐。风险面:拿它当"非阻塞查一遍"会让 netTask
回不到主循环,而 netTask 在 IWDG required_mask 里(串行 socket 栈唯一执行者)。
3) 缺陷三(选 ①:按 POSIX 置位)
exceptfds 是恒为空的死参数:循环开头 p_sock == NULL → continue 先跳过,而
check_socket_error() 只有"表项为空 / fd 越界"才返回 1,越界的 i 又不在本循环
范围内 → 调用方按 POSIX 直觉用它收异常,永远什么都收不到,且返回码不报错。
把异常判定提到 continue 之前:表项不存在的 fd 计入 exceptfds 与返回计数,并更新
函数注释。选 ① 会改变返回计数语义,故补一条"未请求异常集时不计数"的用例,
确保只查可读的老调用方不被误伤(TC-NETSEL-012b)。
测试同步翻转:原 TC-NETSEL-008 / TC-NETSEL-012 是"现状桩、改了会响",现在改成正向
契约,并新增 TC-NETSEL-012c(异常位只落到被请求的那个集合);TC-LFTSTR-005 从
"尾部不裁"的现状登记改为正向契约(尾随空格必须保留)。net_select 22/22、
lftpd_io 33/33 全绿。
@build:0 错误 0 警告。实测体积 ER_IROM1 232,512 B(快照 232,536 → **−24 B**)、
Flash 233,704 B(−20 B)、RW_IRAM1 75,272 B(+8 B 是上一提交 net_cfg 那个字节,
本批零新增)、**RW_CCM 57,376 B 不变**;四档百分比 22.2/57.4/87.9/22.3 全不变。
Code 减少 36 B 即删掉的死循环。
覆盖率台账:门禁如实报了一次 lftpd_string.c 已覆盖行 8 → 7(**代码少了一行**,不是
覆盖退化),已 --update-baseline 重登;现 22/22 套件、line=3003/3446(87.1%)、
func=236/259(91.1%)、tested_files=26、untested_files=118。
文档:主机单测覆盖率口径 §9 新增"三处死路径的处置"小节(裁定、改法、影响、钉住
它的用例一一对应),并把两处"等裁定"的登记改为已处置。
自查:coding-standard / test-cppcheck / id-check --strict(无台账外新账)/ doc-links
(35 处 = 存量)全过;八个改动文件与 HEAD 逐项比对 BOM/CR/U+FFFD/行尾无变化
(这几个文件本就不带 BOM,未被动过)。pre-commit --all-files 仍只有三条失败且与
本分支无关:mem-numbers / mem-budget(.map 与 docs/内存管理 受管快照漂移,增量见
上,按并行分支约定不在分支里 --write,等 Lead 合并后统一回填)与 ui-golden
(simulator/tests/ref/*.bin 在 .gitignore 内,新建 worktree 天生没有黄金参考)。
This commit is contained in:
@@ -777,7 +777,7 @@ static int handle_control_channel(lftpd_client_t *client)
|
||||
if (index < (int)strlen(read_buffer)) {
|
||||
strncpy(arg_buf, read_buffer + index + 1, sizeof(arg_buf) - 1);
|
||||
arg_buf[sizeof(arg_buf) - 1] = '\0';
|
||||
arg = lftpd_string_trim(arg_buf);
|
||||
arg = lftpd_string_ltrim(arg_buf);
|
||||
}
|
||||
|
||||
lftpd_log_info("CMD %s %s", command_tmp, arg ? arg : "");
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
|
||||
#include "private/lftpd_string.h"
|
||||
|
||||
char *lftpd_string_trim(char *s)
|
||||
char *lftpd_string_ltrim(char *s)
|
||||
{
|
||||
char *p = s;
|
||||
/* 只去行首空白:返回跳过后的指针,不搬移缓冲。
|
||||
* 原先此处的"去尾部空白"循环,起点是 strlen(p) 那一格 NUL,isspace('\0') 为假
|
||||
* → 从未执行过;而 FTP 参数的尾随空格属于参数本体(见本文件头与
|
||||
* private/lftpd_string.h 的说明),2026-10-23 删掉这条死代码并改名为 *_ltrim,
|
||||
* 让函数名与实际行为一致(**行为零变化**)。 */
|
||||
for (int i = 0, len = (int)strlen(s); i < len && isspace((int)s[i]); i++) {
|
||||
p++;
|
||||
}
|
||||
for (int i = (int)strlen(p); i >= 0 && isspace((int)p[i]); i--) {
|
||||
p[i] = '\0';
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -2,4 +2,16 @@
|
||||
|
||||
#define CRLF "\r\n"
|
||||
|
||||
char* lftpd_string_trim(char* s);
|
||||
/*
|
||||
* 函数功能:去掉字符串**行首**空白(空格/制表/CR/LF),返回跳过空白后的指针
|
||||
* 入口参数:s - 待处理串(不搬移内容,只挪返回指针) char*
|
||||
* 返回值:首个非空白字符的指针(全空白则指向结尾 NUL) char*
|
||||
* 限定条件:s 非空且以 NUL 结尾;调用方必须使用返回值,不能继续用原指针
|
||||
* 函数说明:**只去前导、不去尾部**,2026-10-23 由 lftpd_string_trim 改名为
|
||||
* lftpd_string_ltrim。原名让调用方以为两头都去,而实现里尾部那条循环
|
||||
* 起点落在 NUL(isspace('\0') 为假)一次都没执行过 —— 潜伏陷阱。
|
||||
* 不去尾才是对的:RFC 959 以命令终止符 CRLF 界定命令,参数(PASS 的
|
||||
* 口令、含空格的文件名)的尾随空格属于参数本体,去掉等于静默改数据;
|
||||
* CRLF 已由 lftpd_inet_read_line 截断,无需此处再处理。
|
||||
*/
|
||||
char* lftpd_string_ltrim(char* s);
|
||||
|
||||
@@ -37,7 +37,8 @@ static int check_socket_error(int sockfd);
|
||||
* 入口参数:nfds - 最大文件描述符数 +1 int > 0
|
||||
* readfds - 读事件集合指针 net_fd_set_t *(可为 NULL)
|
||||
* writefds - 写事件集合指针 net_fd_set_t *(可为 NULL)
|
||||
* exceptfds- 异常事件集合指针 net_fd_set_t *(可为 NULL)
|
||||
* exceptfds- 异常事件集合指针(表项不存在的 fd 计入异常并计入返回计数;
|
||||
* timeout 传 {0,0} 表示"只查一轮立即返回") net_fd_set_t *(可为 NULL)
|
||||
* timeout - 超时时间结构体指针 net_timeval_t *(NULL=无限等待)
|
||||
* 出口参数:readfds/writefds/exceptfds - 仅保留就绪的 Socket 位 net_fd_set_t *
|
||||
* 返回值:就绪的 Socket 数量(>= 0);-1 表示参数无效 int
|
||||
@@ -81,6 +82,14 @@ int net_select(int nfds, net_fd_set_t *readfds, net_fd_set_t *writefds,
|
||||
const net_sock_t *p_sock = net_get_sock(i);
|
||||
|
||||
if (p_sock == NULL) {
|
||||
/* 表项不存在就是异常,必须在 continue **之前**判:原实现先跳过,而
|
||||
* check_socket_error() 只有"表项为空 / fd 越界"才返回 1,越界的 i
|
||||
* 又不在本循环范围内 → exceptfds 永远是空的死参数(调用方按 POSIX
|
||||
* 直觉用它,就永远什么都收不到)。2026-10-23 修掉。 */
|
||||
if ((exceptfds != NULL) && NET_FD_ISSET(i, exceptfds)) {
|
||||
NET_FD_SET(i, &result_except);
|
||||
ready_count++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -116,9 +125,12 @@ int net_select(int nfds, net_fd_set_t *readfds, net_fd_set_t *writefds,
|
||||
|
||||
/* 超时检测 */
|
||||
if (timeout != NULL) {
|
||||
if ((timeout_ms > 0) &&
|
||||
((HAL_GetTick() - start_tick) >= timeout_ms)) {
|
||||
break; /* 超时返回 0 */
|
||||
/* 不能再加 (timeout_ms > 0) 前置:timeout = {0, 0} 换算出 0,那是
|
||||
* POSIX 的"轮询一次立即返回";加上它 = 超时永不生效,调用方(netTask
|
||||
* 在 IWDG required_mask 里)再也回不到主循环。就绪检查在本判断之前,
|
||||
* 所以有就绪时依旧立刻带计数返回,2026-10-23 由主机单测暴露并修掉。 */
|
||||
if ((HAL_GetTick() - start_tick) >= timeout_ms) {
|
||||
break; /* 超时返回 0(timeout_ms == 0 即"只查一轮") */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
-1
@@ -249,13 +249,46 @@
|
||||
⚠ 总体 `line_rate` 88.2% → **87.2%**:**这正是 §5 说的摊薄,不是回退**——`lftpd_io.c` 只测了
|
||||
一半就进分母,分母涨得比分子快;要盯的是 `files_tested`/`files_untested` 两个数。
|
||||
|
||||
本批又照出一处(**语义未改,等裁定**):`lftpd_string_trim` 的尾部循环写成
|
||||
**(同日已处置,见下方"三处死路径的处置")**原照出处:`lftpd_string_trim` 的尾部循环写成
|
||||
`for (i = strlen(p); i >= 0 && isspace(p[i]); i--)`,i 起步那格是 `'\0'`、`isspace(0)` 为假 →
|
||||
循环一次都不执行,**尾部空白/CRLF 从未被裁掉**。今天不发作是因为 CRLF 在
|
||||
`lftpd_inet_read_line` 里已用 `strstr("\r\n")` 截断、唯一调用点 `lftpd.c` 只用返回指针去行首;
|
||||
风险在"以后有人拿它去尾部空白"。修它一行即可,但会改变 FTP 参数(含空格的文件名/
|
||||
口令)的可见行为,需与 FTP 域一起过 → 现在只用 TC-LFTSTR-005 把现状钉成"改了会响"。
|
||||
|
||||
### 三处死路径的处置(2026-10-23,用户逐条裁定后实施)
|
||||
|
||||
三处都不是改动引入的回归,而是**从没被执行到的分支**:生产路径上要么没有调用者、
|
||||
要么调用者没走边界,所以编译器(0 错 0 警)、`cppcheck`、真机日志对它都无话可说。
|
||||
覆盖率把"进没进过"变成可见数字之后,我为了写用例逐行读了它们的判定条件才撞见。
|
||||
|
||||
| 缺陷 | 裁定 | 改法与影响 | 现在钉住它的用例 |
|
||||
|---|---|---|---|
|
||||
| `lftpd_string_trim` 尾部循环起点落在 NUL,`isspace(0)` 为假 → **尾部从不被裁**
|
||||
(名字却承诺两头都去) | **选 (a) 改名** | 改名 `lftpd_string_ltrim` + 删掉那条死循环 +
|
||||
在 `.h`/`.c` 写明"只去前导"。**行为零变化**(那条循环本来就不执行);不去尾才是对的:
|
||||
RFC 959 以 CRLF 界定命令,参数(`PASS` 口令、含空格文件名)的尾随空格属于参数本体,
|
||||
去掉等于静默改数据。`lftpd.c` 调用点同步改名 | TC-LFTSTR-001..005(005 现在是正向契约:
|
||||
尾随空格必须保留) |
|
||||
| `net_select` 的 `timeout = {0,0}` 换算出 `timeout_ms = 0`,而退出条件写着
|
||||
`if ((timeout_ms > 0) && (已用时 >= timeout_ms)))` → **超时永不生效**,退化成死等
|
||||
(POSIX 是"只查一轮立即返回") | **按推荐修** | 去掉 `timeout_ms > 0` 这个错误前置,
|
||||
变成 `if (已用时 >= timeout_ms) break;`。就绪检查在它之前,故有就绪时仍立刻带计数返回;
|
||||
同文件 `net_poll_events` 的 `timeout >= 0` 本来就是对的,现在两条对齐。风险面:
|
||||
拿它当"非阻塞查一遍"会让 netTask 回不到主循环,而 netTask 在 IWDG `required_mask` 里 |
|
||||
TC-NETSEL-008(已翻成正向断言:`rc==0` 且一轮都不睡) |
|
||||
| `exceptfds` 是恒为空的死参数:循环开头 `p_sock == NULL → continue` 先跳过,而
|
||||
`check_socket_error` 只有"表项为空/越界"才为真,越界的 `i` 又不在循环范围内 |
|
||||
**选 ①**(按 POSIX 置位) | 把异常判定提到 `continue` **之前**:表项不存在的 fd 计入
|
||||
`exceptfds` 并计入返回计数;同时更新函数注释。选 ① 会改变返回计数语义(缺失 fd 现在
|
||||
算就绪),因此补一条"未请求异常集时不计数"的用例,确保只查可读的老调用方不被误伤 |
|
||||
TC-NETSEL-012 / 012b / 012c(新增) |
|
||||
|
||||
**处置后的账**:`@build` 0 错误 0 警告,`Code` **−36 B**(删掉的死循环),
|
||||
`RW-data`/`ZI-data` 不变 → **RAM 零变化**。`lftpd_string.c` 的已覆盖行 8 → 7
|
||||
被门禁判了一次 `REGRESS`——那是台账在如实反映"代码少了一行",不是覆盖退化,
|
||||
已按 `--update-baseline` 重登(这条恰好反证门禁有效:删代码想悄悄过去,它会拦)。
|
||||
两处"现状桩"翻成正向契约,另新增 `TC-NETSEL-012c`;全量 22 套件仍然全绿。
|
||||
### P2|值得但工程量大(5 个 `.c` = 2,226 行)
|
||||
|
||||
1. **`net_socket.c`(1,251 行 / 51 个函数,全仓最大的逻辑块)** —— 必须**分片**,别一次吞。
|
||||
|
||||
@@ -180,17 +180,17 @@
|
||||
"func_rate": 1.0,
|
||||
"funcs_covered": 1,
|
||||
"funcs_valid": 1,
|
||||
"line_rate": 0.8,
|
||||
"lines_covered": 8,
|
||||
"lines_valid": 10
|
||||
"line_rate": 1.0,
|
||||
"lines_covered": 7,
|
||||
"lines_valid": 7
|
||||
},
|
||||
"Drivers/BSP/NET/net_select.c": {
|
||||
"func_rate": 1.0,
|
||||
"funcs_covered": 5,
|
||||
"funcs_valid": 5,
|
||||
"line_rate": 0.8509,
|
||||
"lines_covered": 97,
|
||||
"lines_valid": 114
|
||||
"line_rate": 0.8547,
|
||||
"lines_covered": 100,
|
||||
"lines_valid": 117
|
||||
},
|
||||
"Drivers/BSP/NETCFG/ftp_cfg.c": {
|
||||
"func_rate": 1.0,
|
||||
@@ -273,9 +273,9 @@
|
||||
"lftpd_io": {
|
||||
"files": {
|
||||
"Drivers/BSP/NET/lftpd/lftpd_io.c": 159,
|
||||
"Drivers/BSP/NET/lftpd/lftpd_string.c": 10
|
||||
"Drivers/BSP/NET/lftpd/lftpd_string.c": 7
|
||||
},
|
||||
"lines_total": 169,
|
||||
"lines_total": 166,
|
||||
"ok": true
|
||||
},
|
||||
"nand_port": {
|
||||
@@ -294,9 +294,9 @@
|
||||
},
|
||||
"net_select": {
|
||||
"files": {
|
||||
"Drivers/BSP/NET/net_select.c": 114
|
||||
"Drivers/BSP/NET/net_select.c": 117
|
||||
},
|
||||
"lines_total": 114,
|
||||
"lines_total": 117,
|
||||
"ok": true
|
||||
},
|
||||
"phasor": {
|
||||
@@ -421,8 +421,8 @@
|
||||
"func_rate": 0.9112,
|
||||
"funcs_covered": 236,
|
||||
"funcs_valid": 259,
|
||||
"line_rate": 0.8709,
|
||||
"lines_covered": 3001,
|
||||
"line_rate": 0.8714,
|
||||
"lines_covered": 3003,
|
||||
"lines_valid": 3446,
|
||||
"modules_ok": 22,
|
||||
"modules_total": 22
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
* TC-LFTSTR-004 全空白串 → 空串
|
||||
* TC-LFTSTR-005 **现状桩**:尾部空白/CRLF 实际不裁(见下"本批照出的问题")
|
||||
*
|
||||
* 本批照出的问题(**未改语义**,等裁定):lftpd_string_trim 的尾部循环写成
|
||||
* **处置(2026-10-23 用户裁定 = 改名)**:lftpd_string_trim 的尾部循环写成
|
||||
* for (i = strlen(p); i >= 0 && isspace(p[i]); i--) —— i 起步那格是 '\0',
|
||||
* isspace(0) 为假,循环一次都不执行,**尾部空白从未被裁掉**。今天不发作是因为
|
||||
* FTP 命令行的 CRLF 在 lftpd_inet_read_line 里已用 strstr("\r\n") 截断,而唯一
|
||||
@@ -379,35 +379,39 @@ static void tc_trim(void)
|
||||
char buf[32];
|
||||
char *r;
|
||||
|
||||
printf("-- TC-LFTSTR-001..005 lftpd_string_trim --\n");
|
||||
printf("-- TC-LFTSTR-001..005 lftpd_string_ltrim --\n");
|
||||
|
||||
/* 实现是"返回一个跳过行首空白的指针",**不搬移缓冲**;调用方 lftpd.c:780
|
||||
* 用的正是返回值。所以断言必须落在返回值上,落在 buf 上是错的。 */
|
||||
(void)strcpy(buf, " PASS x");
|
||||
r = lftpd_string_trim(buf);
|
||||
r = lftpd_string_ltrim(buf);
|
||||
CHECK((r != buf) && (strcmp(r, "PASS x") == 0) && (buf[0] == ' '),
|
||||
"TC-LFTSTR-001 行首空白靠返回指针跳过,原缓冲不搬移");
|
||||
|
||||
(void)strcpy(buf, "NOOP");
|
||||
r = lftpd_string_trim(buf);
|
||||
r = lftpd_string_ltrim(buf);
|
||||
CHECK((r == buf) && (strcmp(buf, "NOOP") == 0), "TC-LFTSTR-002 无前导空白返回同一指针");
|
||||
|
||||
(void)strcpy(buf, " USER admin ");
|
||||
r = lftpd_string_trim(buf);
|
||||
r = lftpd_string_ltrim(buf);
|
||||
CHECK(strcmp(r, "USER admin ") == 0, "TC-LFTSTR-003 词中间空白保留");
|
||||
|
||||
(void)strcpy(buf, " \t\r\n");
|
||||
r = lftpd_string_trim(buf);
|
||||
r = lftpd_string_ltrim(buf);
|
||||
CHECK(r[0] == '\0', "TC-LFTSTR-004 全空白 -> 空串");
|
||||
|
||||
/* 现状桩(不是期望行为):尾部循环从 strlen(p) 起步,那里是 '\0',
|
||||
* isspace(0)=0 立刻退出 —— **尾部空白/CRLF 实际从未被裁**。今天无害是因为
|
||||
* CRLF 在 lftpd_inet_read_line(lftpd_inet.c 的 strstr("\r\n"))里已截掉。
|
||||
* 谁哪天把 trim 修对了,这条会红——那是提醒,不是回归。 */
|
||||
/* 2026-10-23 处置:函数已由 lftpd_string_trim 改名为 lftpd_string_ltrim,
|
||||
* 并删掉那条永不执行的尾部循环。于是"不去尾部"从潜伏陷阱变成**写进名字与注释
|
||||
* 的契约**:RFC 959 里参数的尾随空格属于参数本体(PASS 口令、含空格的文件名),
|
||||
* 去掉等于静默改数据;CRLF 由 lftpd_inet_read_line 截断。 */
|
||||
(void)strcpy(buf, "LIST ");
|
||||
r = lftpd_string_trim(buf);
|
||||
r = lftpd_string_ltrim(buf);
|
||||
CHECK(strcmp(r, "LIST ") == 0,
|
||||
"TC-LFTSTR-005 现状:尾部空白不裁(潜伏缺陷已登记,修好它会让本条响)");
|
||||
"TC-LFTSTR-005 尾随空格必须保留(ltrim 语义,RFC 959 参数本体)");
|
||||
}
|
||||
|
||||
/* ===================== 入口 ===================== */
|
||||
|
||||
@@ -25,14 +25,15 @@
|
||||
* TC-NETSEL-005 三个集合都可以传 NULL(不崩、不误伤)
|
||||
* TC-NETSEL-006 timeout=NULL 时无限等,靠"对端事件到来"唤醒(假 osDelay 造事件)
|
||||
* TC-NETSEL-007 tv_usec 换算是**整除截断**(1999us → 1ms,不是 2ms)
|
||||
* TC-NETSEL-008 现状桩:timeout = {0, 0} 不立即返回(POSIX select 会立刻返回 0)
|
||||
* TC-NETSEL-008 timeout = {0, 0} 只查一轮立即返回(**2026-10-23 修**:原先因
|
||||
* `(timeout_ms > 0)` 前置判断而永不过期,等于把"非阻塞探测"变成死等)
|
||||
* TC-NETSEL-009 LISTENING 的 socket 即使 readable=1 也不算可读
|
||||
* TC-NETSEL-010 ESTABLISHED + send_ready → 可写位置位
|
||||
* TC-NETSEL-011 非 ESTABLISHED(CONNECTING)即使 send_ready=1 也不可写
|
||||
* TC-NETSEL-012 现状桩:exceptfds 是**恒为空的死参数**(表项为空的 fd 先被 continue
|
||||
* 跳过,异常判定从 select 里到不了);全库唯一调用点 test/board 传 NULL,
|
||||
* 所以今天无受害者——但谁按 POSIX 直觉用它就会静默收不到异常
|
||||
* TC-NETSEL-012b 同一 fd 请求 POLLIN 也不算可读(被跳过,超时到点即退)
|
||||
* TC-NETSEL-012 表项不存在的 fd 计入异常集合(**2026-10-23 修**:判定原本排在
|
||||
* continue 之后,exceptfds 是恒为空的死参数)
|
||||
* TC-NETSEL-012b 未请求异常集时,缺失的 fd 不计数(不影响只查可读的调用方)
|
||||
* TC-NETSEL-012c 异常位只落到被请求的那个集合
|
||||
* TC-NETSEL-013 poll 传 NULL 或 nfds<=0 → -1
|
||||
* TC-NETSEL-014 poll 非法 fd(负数 / 越界)→ NET_POLLNVAL 且计入 ready
|
||||
* TC-NETSEL-015 poll 对 CLOSED socket 置 NET_POLLHUP
|
||||
@@ -216,20 +217,19 @@ static void tc_timeout(void)
|
||||
CHECK((rc == 0) && (g_delay_calls == 0),
|
||||
"TC-NETSEL-007 tv_usec 整除截断:1999us 当 1ms,首轮即退");
|
||||
|
||||
/* 已修(2026-10-23):原来这里写的是 (timeout_ms > 0) && (已用时 >= timeout_ms),
|
||||
* timeout = {0,0} 换算出 0 后**永不超时**,只能干等事件——而 POSIX 语义是
|
||||
* "只查一轮立即返回"。调用方(netTask 在 IWDG required_mask 里)拿它当
|
||||
* 非阻塞探测用就会卡死主循环。现在这条是正向断言。 */
|
||||
reset_all();
|
||||
make_sock(0, NET_SOCK_STATE_ESTABLISHED, 0u, 0u);
|
||||
NET_FD_ZERO(&r);
|
||||
NET_FD_SET(0, &r);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
g_release_after = 5; /* 造一个事件把自己救出来 */
|
||||
rc = net_select(1, &r, NULL, NULL, &tv);
|
||||
/* 现状桩:timeout == {0,0} → 换算出 timeout_ms = 0,而实现里那句是
|
||||
* if ((timeout_ms > 0) && (已用时 >= timeout_ms)) break;
|
||||
* 于是**永远不会因超时退出**,只能等事件——POSIX 的 select 在这里必须立刻返回 0。
|
||||
* 谁把那个 >0 修掉,这条就会红(g_delay_calls 变 0、rc 变 0):那是提醒。 */
|
||||
CHECK((rc == 1) && (g_delay_calls == 5),
|
||||
"TC-NETSEL-008 现状:timeout={0,0} 不立即返回(已登记为缺陷)");
|
||||
CHECK((rc == 0) && (g_delay_calls == 0) && !NET_FD_ISSET(0, &r),
|
||||
"TC-NETSEL-008 timeout={0,0} 只查一轮立即返回(与 poll 的 0 对齐)");
|
||||
}
|
||||
|
||||
/* ===================== 状态准入 ===================== */
|
||||
@@ -268,27 +268,34 @@ static void tc_state_admission(void)
|
||||
CHECK((rc == 0) && !NET_FD_ISSET(0, &w),
|
||||
"TC-NETSEL-011 连接中不可写(send_ready 不作数)");
|
||||
|
||||
/* 现状桩(不是期望):net_select 的循环体开头是
|
||||
* p_sock = net_get_sock(i); if (p_sock == NULL) continue;
|
||||
* 于是"表项不存在"的 fd 被整轮跳过,check_socket_error 的 NULL 分支从 select
|
||||
* 里**永远到不了** → exceptfds 是个恒为空的死参数(谁传了它以为能收到异常,
|
||||
* 就什么都收不到)。改法要么在 continue 前先查异常、要么在文档里写明不支持。 */
|
||||
reset_all();
|
||||
g_sock_valid[0] = 0u;
|
||||
NET_FD_ZERO(&r);
|
||||
NET_FD_SET(0, &r);
|
||||
/* 已修(2026-10-23):异常判定原先排在 p_sock == NULL → continue 之后,而
|
||||
* check_socket_error() 只有"表项为空 / fd 越界"才返回 1、越界的 i 又不在本循环
|
||||
* 范围内 → exceptfds 是恒为空的死参数。现在把判定提到 continue 之前。 */
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 1000;
|
||||
rc = net_select(1, NULL, NULL, &r, &tv);
|
||||
CHECK((rc == 0) && !NET_FD_ISSET(0, &r),
|
||||
"TC-NETSEL-012 现状:exceptfds 永不被置位(表项为空先被 continue 跳过)");
|
||||
reset_all();
|
||||
g_sock_valid[0] = 0u;
|
||||
NET_FD_ZERO(&r);
|
||||
NET_FD_SET(0, &r);
|
||||
rc = net_select(1, NULL, NULL, &r, &tv);
|
||||
CHECK((rc == 1) && NET_FD_ISSET(0, &r),
|
||||
"TC-NETSEL-012 表项不存在的 fd 计入异常集合");
|
||||
reset_all();
|
||||
g_sock_valid[0] = 0u;
|
||||
NET_FD_ZERO(&r);
|
||||
NET_FD_SET(0, &r); /* 只请求可读 */
|
||||
rc = net_select(1, &r, NULL, NULL, &tv);
|
||||
CHECK((rc == 0) && !NET_FD_ISSET(0, &r) && (g_delay_calls == 0),
|
||||
"TC-NETSEL-012b 不存在的 fd 也不算可读(被跳过而不是报错,超时到点即退)");
|
||||
"TC-NETSEL-012b 未请求异常集时不因缺失 fd 计数(不误伤只查可读的调用方)");
|
||||
reset_all();
|
||||
g_sock_valid[0] = 0u;
|
||||
NET_FD_ZERO(&r);
|
||||
NET_FD_SET(0, &r); /* 请求可读:不该置位 */
|
||||
NET_FD_ZERO(&w);
|
||||
NET_FD_SET(0, &w); /* 请求异常:应该置位 */
|
||||
rc = net_select(1, &r, NULL, &w, &tv);
|
||||
CHECK((rc == 1) && NET_FD_ISSET(0, &w) && !NET_FD_ISSET(0, &r),
|
||||
"TC-NETSEL-012c 异常位只落到被请求的那个集合,不污染可读集合");
|
||||
}
|
||||
|
||||
/* ===================== poll 语义 ===================== */
|
||||
|
||||
Reference in New Issue
Block a user