Skip to content

Commit a23aa33

Browse files
Yuki-Nagoriclaude
andauthored
[1010] 修复未绑定变量触发建议时解引用 NULL 导致 SIGSEGV (#885)
* [1010] 修复未绑定变量触发建议时解引用 NULL 导致 SIGSEGV Co-Authored-By: Claude <noreply@anthropic.com> * [1010] main_names 建议扫描跳过 NULL 槽,修复未绑定变量 SIGSEGV Co-Authored-By: Claude <noreply@anthropic.com> * test --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9ad60d8 commit a23aa33

3 files changed

Lines changed: 65 additions & 0 deletions

File tree

devel/1010.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# [1010] 修复未绑定变量触发建议时解引用 NULL 导致 SIGSEGV
2+
3+
## 任务相关的代码文件
4+
- `src/s7.c``unbound_variable_error_nr` 中 main_names 建议扫描循环)
5+
6+
## 如何测试
7+
8+
### 1. 回归测试(推荐,纳入版本控制)
9+
10+
修复的回归用例已加入 `tests/scheme/s7-test.scm`,覆盖符号长度 20–23(恰好命中 main_names 尾部 NULL 桶):
11+
12+
```bash
13+
xmake b goldfish
14+
bin/gf tests/scheme/s7-test.scm
15+
```
16+
17+
修复后期望:`*** checks *** : 28 correct, 0 failed.`,退出码 0。
18+
19+
注意:用例把未绑定符号写在**调用运算符位置** `(undefined-symbol-xyz)`,而非裸符号。原因是 s7 进入"perhaps you meant"建议扫描有前置门槛(`unbound_variable_error_nr``err_code` 为 pair 且 `sym == car(err_code)`),只有调用位置的未绑定符号才会走到那条会解引用 NULL 的建议扫描路径,裸符号测不到这个 bug。
20+
21+
### 2. 手工验证(命令行复现崩溃点)
22+
23+
```bash
24+
xmake b goldfish
25+
# 长度 20 的未绑定符号:修复前段错误(exit 139),修复后正常报 ;unbound variable(exit 255)
26+
bin/gf -e '(undefined-symbol-xyz)'
27+
```
28+
29+
### 3. 反向验证(确认回归用例有效)
30+
31+
临时移除修复(删掉 `if (main_names[i] == NULL) continue;` 那 4 行),重新构建后跑测试,应观察到进程 SIGSEGV、退出码 139;恢复修复后重新构建,测试重新通过。这证明用例确实守住了该回归点。
32+
33+
## What
34+
1.`unbound_variable_error_nr` 的 main_names 建议扫描循环里,对 `main_names[i]` 为 NULL 的槽位跳过,不再喂给 `levenshtein`
35+
36+
## Why
37+
`unbound_variable_error_nr` 在报"未绑定变量"前,会用 levenshtein 距离在 `main_names` 建议表里找最接近的符号给出"perhaps you meant"提示。建议表按符号长度分桶,靠 `main_names_index[]` 记录每个长度区间的 `[start, end)`
38+
39+
- `MAIN_NAMES_SIZE` 声明为 443,注释 `443==NULL`
40+
-`main_names[]` 实际只填了 **430** 个字符串,索引 430–442 是未显式初始化的 NULL 槽(C 静态数组剩余元素自动置 0);
41+
- `main_names_index` 仍按"填满 443"计算,导致长度 **20–23** 的桶区间(如长度 20 的 `[425, 432)`)跨进了那片 NULL 区。
42+
43+
于是对任何长度 20–23 的未绑定符号,循环取到 `main_names[i] == NULL``levenshtein(... NULL ...)``s2[i-1] == s1[j-1]` 处解引用 NULL,整个解释器 SIGSEGV——未绑定变量本应只是可捕获的 scheme 错误,却变成进程级段错误,连报错信息都打不出来。
44+
45+
gdb 实测:`gf -e '(undefined-symbol-xyz)'`(20 字符)崩在 `levenshtein` 解引用 `r11=NULL`,调用方为 `unbound_variable_error_nr`(s7.c:59956),此时 `s2==NULL``len2==20`。Mogan STEM 里切换 GUI 主题正是踩中 20 字符的 `set-pretty-preference*` 未绑定,导致整应用崩溃。
46+
47+
> 注:根因是 `main_names` 没填满 + `main_names_index` 未同步。彻底修法是补齐表项或重算索引,但那需要逐项核对 443 个符号的长度分桶,易引入新错。本次先在调用处跳过 NULL——最小、精确、覆盖所有符号长度,把段错误降级回正常报错。
48+
49+
## How
50+
`src/s7.c` 的建议扫描循环(`for (int32_t i = start; i < end; i++)`)开头加一行:
51+
52+
```c
53+
if (main_names[i] == NULL) continue;
54+
```
55+
56+
遇到未初始化的 NULL 槽直接跳过,不进入 levenshtein 计算。未绑定变量恢复为正常的 `;unbound variable` 报错,建议功能对其余有效条目照常工作。

src/s7.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59953,6 +59953,10 @@ static no_return void unbound_variable_error_nr(s7_scheme *sc, s7_pointer sym)
5995359953
#endif
5995459954
for (int32_t i = start; i < end; i++)
5995559955
{
59956+
/* main_names has only 430 entries but main_names_index is sized
59957+
for 443, so the length-20..23 buckets run into trailing NULL
59958+
slots; skip NULLs to avoid dereferencing them in levenshtein. */
59959+
if (main_names[i] == NULL) continue;
5995659960
int32_t diff = levenshtein(sc, sym_name, sym_len, main_names[i], sym_len) + 1; /* perhaps same but i+/-1 as well */
5995759961
if (sym_name[0] != main_names[i][0]) diff++;
5995859962
if (diff < min_diff)

tests/scheme/s7-test.scm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,9 @@
131131
(check (let? 42) => #f)
132132
(check (let? 'hello) => #f)
133133

134+
(check-catch 'unbound-variable (undefined-symbol-xyz))
135+
(check-catch 'unbound-variable (undefined-symbol-xyza))
136+
(check-catch 'unbound-variable (undefined-symbol-xyzab))
137+
(check-catch 'unbound-variable (undefined-symbol-xyzabc))
138+
134139
(check-report)

0 commit comments

Comments
 (0)