Compare commits

...

2 Commits

Author SHA1 Message Date
Luca Boccassi b6473c8a71
Merge b6f31239b7 into 710653d3bc 2025-04-17 12:50:38 -07:00
Luca Boccassi b6f31239b7 core: harden a couple of UNIT_VTABLE handling to avoid segfaults
Most places already check that the UNIT_VTABLE pointer is valid
before calling it, but a couple don't, so add the same checks.

Also ensure the type is valid before using it as an index in the
array, to avoid out of bounds accesses.

Finally in the cleanup path check that the vtable is defined at
all, as it seems in some cases it isn't, as shown in the backtrace
below.

This should hopefully at least address a crash observed in the wild
on arm64:

https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/2098861

 (gdb) bt full
 #0  0x0000e14d99f296a8 in ?? ()
 No symbol table info available.
 #1  0x0000e14d999df8e4 in missing_rt_tgsigqueueinfo (info=0xffffddda1ca0, sig=11, tid=<optimized out>, tgid=1227)
    at ../src/basic/missing_syscall.h:384
 No locals.
 #2  propagate_signal (sig=sig@entry=11, siginfo=siginfo@entry=0xffffddda1ca0) at ../src/basic/signal-util.c:301
        p = 1227
        __func__ = "propagate_signal"
 #3  0x0000bb509f70e9bc [PAC] in crash (sig=11, siginfo=0xffffddda1ca0, context=<optimized out>)
    at ../src/core/crash-handler.c:94
        sa = {__sigaction_handler = {sa_handler = 0x0, sa_sigaction = 0x0}, sa_mask = {__val = {
              0 <repeats 16 times>}}, sa_flags = 0, sa_restorer = 0x0}
        pid = 0
        __func__ = "crash"
 #4  <signal handler called>
 No symbol table info available.
 #5  0x0000e14d99d2944c in unit_active_state (u=u@entry=0xbb50c8aeeb10) at ../src/core/unit.c:941
        __func__ = "unit_active_state"
 #6  0x0000e14d99d2d454 in unit_may_gc (u=0xbb50c8aeeb10) at ../src/core/unit.c:465
        state = <optimized out>
        r = <optimized out>
        __func__ = "unit_may_gc"
 #7  0x0000e14d99d2e7d8 [PAC] in unit_add_to_gc_queue (u=u@entry=0xbb50c8aeeb10) at ../src/core/unit.c:535
        __func__ = "unit_add_to_gc_queue"
 #8  0x0000e14d99d2efe0 [PAC] in unit_clear_dependencies (u=0xbb50c8b0a7c0) at ../src/core/unit.c:656
        other_deps = 0x0
        other = 0xbb50c8aeeb10
        deps = 0xbb50c8bd9270
        __func__ = <optimized out>
 #9  unit_free (u=0xbb50c8b0a7c0) at ../src/core/unit.c:797
        slice = 0x0
        t = 0x0
        __func__ = "unit_free"
 #10 0x0000e14d99ce228c [PAC] in manager_clear_jobs_and_units.part.0.lto_priv.0 (m=m@entry=0xbb50c8acf790)
    at ../src/core/manager.c:1594
        u = <optimized out>
        __func__ = <optimized out>
 #11 0x0000e14d99ce2624 [PAC] in manager_clear_jobs_and_units (m=0xbb50c8acf790) at ../src/core/manager.c:1591
        u = <optimized out>
        u = <optimized out>
        __func__ = <optimized out>
 #12 manager_reload (m=m@entry=0xbb50c8acf790) at ../src/core/manager.c:3568
        reloading = 0x0
        fds = 0xbb50c8acd8a8
        f = 0xbb50c8d6dfe0
        r = 0
        __func__ = "manager_reload"
 #13 0x0000bb509f709338 [PAC] in invoke_main_loop (ret_error_message=0xffffddda3178,
    ret_switch_root_init=<synthetic pointer>, ret_switch_root_dir=<synthetic pointer>, ret_fds=0xffffddda3168,
    ret_retval=<synthetic pointer>, saved_rlimit_memlock=0xffffddda31a0, saved_rlimit_nofile=0xffffddda31b0,
    m=0xbb50c8acf790) at ../src/core/main.c:1982
        saved_log_target = <optimized out>
        saved_log_level = <optimized out>
        objective = 2
        r = <optimized out>
        r = <optimized out>
        __func__ = <optimized out>
        objective = <optimized out>
        saved_log_target = <optimized out>
        saved_log_level = <optimized out>
        _level = <optimized out>
        _e = <optimized out>
        _level = <optimized out>
        _e = <optimized out>
        _level = <optimized out>
        _e = <optimized out>
        _level = <optimized out>
        _e = <optimized out>
        _pvar_ = <optimized out>
        _var_ = <optimized out>
        _nullvalue_ = <optimized out>
        _pvar_ = <optimized out>
        _var_ = <optimized out>
        _nullvalue_ = <optimized out>
        _level = <optimized out>
        _e = <optimized out>
        _pvar_ = <optimized out>
        _var_ = <optimized out>
        _nullvalue_ = <optimized out>
        _level = <optimized out>
        _e = <optimized out>
        _level = <optimized out>
        _e = <optimized out>
 #14 main (argc=1, argv=0xffffddda3668) at ../src/core/main.c:3106
        initrd_timestamp = <optimized out>
        userspace_timestamp = {realtime = 1739974537959351, monotonic = 743773}
        kernel_timestamp = {realtime = 1739974537215578, monotonic = 0}
        security_start_timestamp = {realtime = 1739974537972541, monotonic = 756963}
        security_finish_timestamp = {realtime = 1739974537973484, monotonic = 757906}
        saved_rlimit_nofile = {rlim_cur = 1024, rlim_max = 4096}
        saved_rlimit_memlock = {rlim_cur = 8388608, rlim_max = 8388608}
        skip_setup = <optimized out>
        loaded_policy = false
        queue_default_job = <optimized out>
        first_boot = <optimized out>
        switch_root_dir = 0x0
        switch_root_init = 0x0
        before_startup = <optimized out>
        after_startup = <optimized out>
        error_message = 0x0
        r = <optimized out>
        retval = 1
        m = 0xbb50c8acf790
        fds = 0x0
        systemd = "systemd"
        __func__ = "main"
        _found = <optimized out>
        __assert_in_set = <optimized out>
2025-03-07 15:00:36 +00:00
2 changed files with 12 additions and 1 deletions

View File

@ -480,7 +480,7 @@ bool unit_may_gc(Unit *u) {
if (r <= 0 && !IN_SET(r, -ENXIO, -EOWNERDEAD))
return false; /* ENXIO/EOWNERDEAD means: currently not realized */
if (!UNIT_VTABLE(u)->may_gc)
if (!UNIT_VTABLE(u) || !UNIT_VTABLE(u)->may_gc)
return true;
return UNIT_VTABLE(u)->may_gc(u);
@ -877,12 +877,18 @@ UnitActiveState unit_active_state(Unit *u) {
* loaded but still has a process around. That's why we won't
* shortcut failed loading to UNIT_INACTIVE_FAILED. */
if (!UNIT_VTABLE(u) || !UNIT_VTABLE(u)->active_state)
return UNIT_INACTIVE;
return UNIT_VTABLE(u)->active_state(u);
}
const char* unit_sub_state_to_string(Unit *u) {
assert(u);
if (!UNIT_VTABLE(u) || !UNIT_VTABLE(u)->sub_state_to_string)
return NULL;
return UNIT_VTABLE(u)->sub_state_to_string(u);
}

View File

@ -753,6 +753,11 @@ typedef struct UnitVTable {
extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
static inline const UnitVTable* UNIT_VTABLE(const Unit *u) {
assert(u);
if (u->type < 0 || u->type >= _UNIT_TYPE_MAX)
return NULL;
return unit_vtable[u->type];
}