diff --git a/demo/crash/README.md b/demo/crash/README.md index 52e36b31..b9dc1b8e 100644 --- a/demo/crash/README.md +++ b/demo/crash/README.md @@ -16,12 +16,13 @@ bin/gf demo/crash/<文件名>; echo "exit=$?" 已在 devel/0142.md 修复,修复验证完成后移除。 - `h02-which-public-integer.scm`(which 传入非字符串 abort) 已在 devel/0143.md 修复,修复验证完成后移除。 +- `c09-g_rename-integer.scm`(g_rename 传入非字符串段错误) + 已在 devel/0144.md 修复,修复验证完成后移除。 ## 已确认的崩溃点 | 文件 | 触发代码 | 信号 | 根因 | |---|---|---|---| -| 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..66aa8b9a --- /dev/null +++ b/devel/0144.md @@ -0,0 +1,47 @@ +# [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 时抛出 + `(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 + +`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` 检查,非字符串抛出 +`(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 27f32e32..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); @@ -263,8 +269,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 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 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); 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..20099357 100644 --- a/tests/liii/os/rename-test.scm +++ b/tests/liii/os/rename-test.scm @@ -109,3 +109,11 @@ (check-report) + + +;; ; C 层入口 g_rename 参数类型测试(type-error 为 (liii error) 约定) +;; src/dst 必须是 string?,C++ 层需要类型守卫, +;; 避免把非字符串对象当作 C 字符串指针导致段错误 (devel/0144.md) +(check-catch 'type-error (g_rename 1 2)) +(check-catch 'type-error (g_rename "src" 2)) +(check-catch 'type-error (g_rename 1 "dst"))