uim: Fix uim-module-manager segfault from a GC'd require filename

uim-module-manager crashes in uim_init() while requiring key.scm:

    #0  __strlen_evex ()
    #1  make_loaded_str (filename=0x7e)
    #2  scm_p_require ()
    ...
    #29 uim_init ()

scm_p_require() dereferences its FILENAME argument again after the
nested scm_load(), but nothing keeps a tagged ScmObj reference to it.
SigScheme's conservative collector only accepts properly tagged words
(within_heapp() / SCM_TAG_CONSISTENTP()), while the compiler keeps just
SCM_DROP_TAG(filename) and the interior char *, so the string is swept
and its cell recycled mid-load:

    before load: cell=0x7ffff7d5a090 obj_x=0x555555569180 "key.scm"
    after  load: cell=0x7ffff7d5a090 obj_x=0x7e            <- recycled

Whether it triggers is pure allocation timing, which is why uim-native
fell over while the cross-built uim ran the same scm files fine.

Add the patch to the common SRC_URI rather than the class-target one,
since it is uim-native that scm/Makefile runs at build time.

Signed-off-by: Khem Raj <khem.raj@oss.qualcomm.com>
This commit is contained in:
Khem Raj
2026-08-02 09:10:44 -07:00
parent 787d1b73e2
commit 703672013b
2 changed files with 106 additions and 1 deletions
@@ -0,0 +1,103 @@
From: Khem Raj <raj.khem@gmail.com>
Date: Sun, 2 Aug 2026 00:00:00 +0000
Subject: [PATCH] sigscheme: GC-protect the objects used by require
uim-module-manager segfaults inside uim_init() while requiring key.scm:
#0 __strlen_evex ()
#1 make_loaded_str (filename=0x7e)
#2 scm_p_require ()
...
#26 uim_scm_require_file ()
#29 uim_init ()
scm_p_require() dereferences its FILENAME argument again after
scm_require_internal() has run, and scm_require_internal() keeps
LOADED_STR live across a nested scm_load(). Neither object is
GC-protected.
SigScheme's conservative collector only recognizes *tagged* ScmObj
values: within_heapp() rejects any candidate word whose tag is not
consistent with the cell it points to (SCM_TAG_CONSISTENTP()). An
optimizing compiler is free to keep only SCM_DROP_TAG(filename) and the
interior "char *" returned by SCM_STRING_STR(), and gcc does exactly
that here:
scm_p_require:
mov %rdi,%rbp
and $0xfffffffffffffff8,%rbp # SCM_DROP_TAG(filename)
mov 0x0(%rbp),%r12 # SCM_STRING_STR(filename)
... # no tagged copy is kept anywhere
While the nested scm_load() runs, the string is therefore unreachable
for the GC. A cons allocation inside that load triggers a mark & sweep,
the string cell is put on the free list (its body free()d by
SCM_CELL_STRING_FIN()) and the cell is immediately recycled. The
following SCM_STRING_STR(filename) then returns whatever the new
occupant stores in obj_x -- 0x7e above -- and strlen() faults:
before load: cell=0x7ffff7d5a090 obj_x=0x555555569180 "key.scm"
after load: cell=0x7ffff7d5a090 obj_x=0x7e <- recycled
Whether this is hit depends purely on allocation timing, which is why it
shows up for one build of uim and not for another built from the same
sources on the same host.
Protect the objects for as long as they are needed.
Upstream-Status: Submitted [https://github.com/uim/uim/issues]
Signed-off-by: Khem Raj <raj.khem@gmail.com>
---
sigscheme/src/module-sscm-ext.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
--- a/sigscheme/src/module-sscm-ext.c
+++ b/sigscheme/src/module-sscm-ext.c
@@ -224,11 +224,15 @@
{
ScmObj loaded_str;
- loaded_str = make_loaded_str(filename);
+ /* scm_load() below can trigger a GC and the conservative collector only
+ * recognizes tagged ScmObj values, so protect LOADED_STR explicitly. */
+ scm_gc_protect_with_init(&loaded_str, make_loaded_str(filename));
if (!scm_providedp(loaded_str)) {
scm_load(filename);
scm_provide(loaded_str);
}
+ scm_gc_unprotect(&loaded_str);
+
return NULL;
}
@@ -242,15 +246,28 @@
ENSURE_STRING(filename);
+ /* FILENAME is dereferenced again below and its body is handed to
+ * scm_load() as a plain char *. Optimizing compilers keep only
+ * SCM_DROP_TAG(filename) and SCM_STRING_STR(filename) live, and
+ * within_heapp() rejects both, so without this explicit protection the
+ * string is swept (and its body free()d) by a GC triggered from the
+ * nested load. */
+ scm_gc_protect(&filename);
+
scm_require_internal(SCM_STRING_STR(filename));
#if SCM_COMPAT_SIOD
- loaded_str = make_loaded_str(SCM_STRING_STR(filename));
+ scm_gc_protect_with_init(&loaded_str,
+ make_loaded_str(SCM_STRING_STR(filename)));
retsym = scm_intern(SCM_STRING_STR(loaded_str));
SCM_SYMBOL_SET_VCELL(retsym, SCM_TRUE);
+ scm_gc_unprotect(&loaded_str);
+ scm_gc_unprotect(&filename);
return retsym;
#else
+ scm_gc_unprotect(&filename);
+
return SCM_TRUE;
#endif
}
+3 -1
View File
@@ -4,7 +4,9 @@ LICENSE = "BSD-3-Clause AND LGPL-2.0-or-later"
LIC_FILES_CHKSUM = "file://COPYING;md5=ab2826b41ca0ff4030d38cc39791d1c8"
SECTION = "inputmethods"
SRC_URI = "https://github.com/uim/uim/releases/download/${PV}/uim-${PV}.tar.bz2"
SRC_URI = "https://github.com/uim/uim/releases/download/${PV}/uim-${PV}.tar.bz2 \
file://0001-sigscheme-GC-protect-the-objects-used-by-require.patch \
"
SRC_URI:append:class-target = "\
file://uim-module-manager.patch \
"