From d66eee287b9525238614172a6ffbdaab86443024 Mon Sep 17 00:00:00 2001 From: Da Shen Date: Tue, 8 Sep 2026 01:19:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?[0144]=20C++=20=E5=B1=82=E4=B8=BA=20g=5Fren?= =?UTF-8?q?ame=20=E5=A2=9E=E5=8A=A0=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=EF=BC=8C=E4=BF=AE=E5=A4=8D=E9=9D=9E=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=E5=8F=82=E6=95=B0=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E6=AE=B5=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo/crash/README.md | 8 ++++-- demo/crash/c09-g_rename-integer.scm | 1 - devel/0144.md | 44 +++++++++++++++++++++++++++++ src/liii_os.cpp | 12 ++++++-- tests/liii/os/rename-test.scm | 8 ++++++ 5 files changed, 67 insertions(+), 6 deletions(-) delete mode 100644 demo/crash/c09-g_rename-integer.scm create mode 100644 devel/0144.md diff --git a/demo/crash/README.md b/demo/crash/README.md index e316fe32..e9f0cbd1 100644 --- a/demo/crash/README.md +++ b/demo/crash/README.md @@ -11,15 +11,17 @@ bin/gf demo/crash/<文件名>; echo "exit=$?" 每个片段均已 3 次重复验证稳定复现。 -历史条目:`h01-os-call-public-integer.scm`(os-call 传入非字符串段错误) -已在 devel/0142.md 修复,修复验证完成后移除。 +历史条目: +- `h01-os-call-public-integer.scm`(os-call 传入非字符串段错误) + 已在 devel/0142.md 修复,修复验证完成后移除。 +- `c09-g_rename-integer.scm`(g_rename 传入非字符串段错误) + 已在 devel/0144.md 修复,修复验证完成后移除。 ## 已确认的崩溃点 | 文件 | 触发代码 | 信号 | 根因 | |---|---|---|---| | h02-which-public-integer.scm | `(import (liii sys)) (which 123)` | SIGABRT | **公开 API**。`sys.scm` 的 `which` 直接透传 `g_which`,`goldfish.hpp` 的 `f_which` 用垃圾指针构造 `std::string`,空指针抛 `std::logic_error` 未捕获 | -| c09-g_rename-integer.scm | `(g_rename 1 2)` | SIGSEGV | `liii_os.cpp` 的 `f_rename` 对两个参数都未做类型检查,垃圾指针传入 `std::filesystem::rename` | | c11-g_listdir-integer.scm | `(g_listdir 99)` | SIGABRT | `liii_os.cpp` 的 `f_listdir` 未检查参数类型,`s7_string()` 得到空指针后构造 `std::string` 抛 `std::logic_error` | | f03-base64-encode-oob-length.scm | `(g_bytevector-base64-encode (make-bytevector 4 65) 1073741824)` | SIGSEGV | `liii_base64.cpp` 的长度参数不校验是否超过 bytevector 实际大小,编码循环按声明长度越界读取 1GB | diff --git a/demo/crash/c09-g_rename-integer.scm b/demo/crash/c09-g_rename-integer.scm deleted file mode 100644 index fe00dd22..00000000 --- a/demo/crash/c09-g_rename-integer.scm +++ /dev/null @@ -1 +0,0 @@ -(g_rename 1 2) diff --git a/devel/0144.md b/devel/0144.md new file mode 100644 index 00000000..1994c8b3 --- /dev/null +++ b/devel/0144.md @@ -0,0 +1,44 @@ +# [0144] 修复 g_rename 传入非字符串参数导致 gf 段错误的问题 + +## 任务相关的代码文件 +- src/liii_os.cpp +- tests/liii/os/rename-test.scm +- demo/crash/README.md + +## 如何测试 +```bash +xmake b goldfish +bin/gf tests/liii/os/rename-test.scm +``` + +预期:测试输出 `12 correct, 0 failed`。 + +## 2026-09-08 C++ 层为 g_rename 增加参数类型检查 + +### What + +1. `src/liii_os.cpp` 的 `f_rename` 入口处为 `src`(第 1 参数)与 + `dst`(第 2 参数)增加类型守卫:不是 string 时返回 + `s7_wrong_type_arg_error(sc, "rename", ...)`。 +2. `tests/liii/os/rename-test.scm` 增加 3 个 C 层入口参数类型回归测试 + (`(g_rename 1 2)`、`(g_rename "src" 2)`、`(g_rename 1 "dst")`)。 +3. 崩溃片段 `demo/crash/c09-g_rename-integer.scm` + (`(g_rename 1 2)`)修复验证完成后移除。 + +### Why + +`bin/gf demo/crash/c09-g_rename-integer.scm`(即 `(g_rename 1 2)`)会让 +进程段错误(SIGSEGV, exit 139)。根因在 C++ 层:`f_rename` 直接 +`s7_string(s7_car(args))` / `s7_string(s7_cadr(args))`,对非字符串对象 +等同于把对象强转为字符串 cell 解引用,垃圾指针传入 +`std::filesystem::rename` 后触发段错误。 + +`(liii os)` 公开包装 `rename` 已有 `string?` 检查,但根环境直接调用 +`g_rename` 的代码不受保护。按 0142/0143 的模式,防御放在 C++ 层。 + +修复前运行新测试会直接段错误(TDD 红灯),修复后 12/12 通过。 + +### How + +与 devel/0142.md 相同的模式:C++ 入口先 `s7_is_string` 检查,非字符串走 +s7 标准的 `s7_wrong_type_arg_error`。 diff --git a/src/liii_os.cpp b/src/liii_os.cpp index 27f32e32..7dd0b5e3 100644 --- a/src/liii_os.cpp +++ b/src/liii_os.cpp @@ -263,8 +263,16 @@ glue_remove_file (s7_scheme* sc) { static s7_pointer f_rename (s7_scheme* sc, s7_pointer args) { - const char* src= s7_string (s7_car (args)); - const char* dst= s7_string (s7_cadr (args)); + s7_pointer src_arg= s7_car (args); + if (!s7_is_string (src_arg)) { + return s7_wrong_type_arg_error (sc, "rename", 1, src_arg, "a string"); + } + s7_pointer dst_arg= s7_cadr (args); + if (!s7_is_string (dst_arg)) { + return s7_wrong_type_arg_error (sc, "rename", 2, dst_arg, "a string"); + } + const char* src= s7_string (src_arg); + const char* dst= s7_string (dst_arg); try { fs::rename (src, dst); return s7_make_boolean (sc, true); diff --git a/tests/liii/os/rename-test.scm b/tests/liii/os/rename-test.scm index 8b500f20..5dcad050 100644 --- a/tests/liii/os/rename-test.scm +++ b/tests/liii/os/rename-test.scm @@ -109,3 +109,11 @@ (check-report) + + +;; ; C 层入口 g_rename 参数类型测试 +;; src/dst 必须是 string?,C++ 层需要类型守卫, +;; 避免把非字符串对象当作 C 字符串指针导致段错误 (devel/0144.md) +(check-catch 'wrong-type-arg (g_rename 1 2)) +(check-catch 'wrong-type-arg (g_rename "src" 2)) +(check-catch 'wrong-type-arg (g_rename 1 "dst")) From ebc65ee5d96538b3be5ff0b01eed5c370ec5fe4a Mon Sep 17 00:00:00 2001 From: Da Shen Date: Tue, 8 Sep 2026 08:31:33 +0800 Subject: [PATCH 2/2] =?UTF-8?q?[0144]=20=E9=94=99=E8=AF=AF=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E7=94=B1=20wrong-type-arg=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E4=B8=BA=20(liii=20error)=20=E7=BA=A6=E5=AE=9A=E7=9A=84=20type?= =?UTF-8?q?-error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devel/0144.md | 11 +++++++---- src/liii_os.cpp | 10 ++++++++-- tests/liii/os/rename-test.scm | 8 ++++---- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/devel/0144.md b/devel/0144.md index 1994c8b3..66aa8b9a 100644 --- a/devel/0144.md +++ b/devel/0144.md @@ -18,12 +18,14 @@ bin/gf tests/liii/os/rename-test.scm ### What 1. `src/liii_os.cpp` 的 `f_rename` 入口处为 `src`(第 1 参数)与 - `dst`(第 2 参数)增加类型守卫:不是 string 时返回 - `s7_wrong_type_arg_error(sc, "rename", ...)`。 + `dst`(第 2 参数)增加类型守卫:不是 string 时抛出 + `(liii error)` 约定的 `type-error`。 2. `tests/liii/os/rename-test.scm` 增加 3 个 C 层入口参数类型回归测试 (`(g_rename 1 2)`、`(g_rename "src" 2)`、`(g_rename 1 "dst")`)。 3. 崩溃片段 `demo/crash/c09-g_rename-integer.scm` (`(g_rename 1 2)`)修复验证完成后移除。 +4. 按 review 意见,错误类型由 s7 内建的 `wrong-type-arg` 调整为 + `(liii error)` 约定的 `type-error`,测试断言同步更新。 ### Why @@ -40,5 +42,6 @@ bin/gf tests/liii/os/rename-test.scm ### How -与 devel/0142.md 相同的模式:C++ 入口先 `s7_is_string` 检查,非字符串走 -s7 标准的 `s7_wrong_type_arg_error`。 +与 devel/0142.md 相同的模式:C++ 入口先 `s7_is_string` 检查,非字符串抛出 +`(liii error)` 约定的 `type-error`(`string_type_error` 辅助函数,经 +`s7_error` 实现),可用 `(catch 'type-error ...)` 捕获。 diff --git a/src/liii_os.cpp b/src/liii_os.cpp index 7dd0b5e3..14d52597 100644 --- a/src/liii_os.cpp +++ b/src/liii_os.cpp @@ -69,6 +69,12 @@ string_vector_to_s7_vector (s7_scheme* sc, vector v) { return ret; } +// 抛出 (liii error) 约定的 type-error,irritant 为出错的参数 +inline s7_pointer +string_type_error (s7_scheme* sc, const char* msg, s7_pointer arg) { + return s7_error (sc, s7_make_symbol (sc, "type-error"), s7_list (sc, 2, s7_make_string (sc, msg), arg)); +} + static s7_pointer f_os_arch (s7_scheme* sc, s7_pointer args) { return s7_make_string (sc, TB_ARCH_STRING); @@ -265,11 +271,11 @@ static s7_pointer f_rename (s7_scheme* sc, s7_pointer args) { s7_pointer src_arg= s7_car (args); if (!s7_is_string (src_arg)) { - return s7_wrong_type_arg_error (sc, "rename", 1, src_arg, "a string"); + return string_type_error (sc, "rename: src must be a string", src_arg); } s7_pointer dst_arg= s7_cadr (args); if (!s7_is_string (dst_arg)) { - return s7_wrong_type_arg_error (sc, "rename", 2, dst_arg, "a string"); + return string_type_error (sc, "rename: dst must be a string", dst_arg); } const char* src= s7_string (src_arg); const char* dst= s7_string (dst_arg); diff --git a/tests/liii/os/rename-test.scm b/tests/liii/os/rename-test.scm index 5dcad050..20099357 100644 --- a/tests/liii/os/rename-test.scm +++ b/tests/liii/os/rename-test.scm @@ -111,9 +111,9 @@ (check-report) -;; ; C 层入口 g_rename 参数类型测试 +;; ; C 层入口 g_rename 参数类型测试(type-error 为 (liii error) 约定) ;; src/dst 必须是 string?,C++ 层需要类型守卫, ;; 避免把非字符串对象当作 C 字符串指针导致段错误 (devel/0144.md) -(check-catch 'wrong-type-arg (g_rename 1 2)) -(check-catch 'wrong-type-arg (g_rename "src" 2)) -(check-catch 'wrong-type-arg (g_rename 1 "dst")) +(check-catch 'type-error (g_rename 1 2)) +(check-catch 'type-error (g_rename "src" 2)) +(check-catch 'type-error (g_rename 1 "dst"))