Skip to content

fix(dock): hold owned refs in the launch-feedback give-up timer - #37

Open
perlowja wants to merge 1 commit into
singularityos-lab:mainfrom
perlowja:fix/dock-launch-feedback-uaf-timeout
Open

perlowja wants to merge 1 commit into
singularityos-lab:mainfrom
perlowja:fix/dock-launch-feedback-uaf-timeout

Conversation

@perlowja

Copy link
Copy Markdown
Contributor

Generative AI assistance

AI assistance: disclosed

What

DockLaunchFeedback.stretch_dot() arms a GIVE_UP_SECONDS (10 s) GLib.Timeout
whose callback calls wrapper.get_data("launch_dot"). The wrapper dock item and
the bin dot were captured with unowned — raw, non-owning pointers. If the dock
item is rebuilt or destroyed inside that 10 s window (dock rebuild, app-grid change,
session teardown), the timer fires on freed memory.

Symptom

Hard SIGSEGV in the shell main thread:

#0 g_type_check_instance_is_fundamentally_a (libgobject-2.0.so.0)
#1 g_object_get_data                       (libgobject-2.0.so.0)
#2 singularity-desktop + 0x152644           <-- this lambda
#3 libglib-2.0.so.0 (main-loop source dispatch)
...
#6 g_main_context_iteration
#7 g_application_run
#8 main

Observed live on NCZ-OS 26.10 (arm64): coredumpctl core pid 2613, booting a fresh
session, ~4 min after login. The offset maps exactly to this callback against the
pinned sources.

Fix

Capture owned references instead of unowned so both objects stay valid until the
timer fires; the closure releases them when the source is removed. No behavior
change beyond not crashing — the dot is still cleaned up on the same condition.

Assisted-by: opencode:deepseek-flash

stretch_dot() arms a GIVE_UP_SECONDS (10 s) GLib.Timeout whose callback
calls wrapper.get_data("launch_dot"). The wrapper and the dot were
captured with `unowned` -- raw, non-owning pointers. When the dock item is
rebuilt or destroyed inside that window (dock rebuild, app-grid change,
session teardown) the timer fires on freed memory and the shell dies with
SIGSEGV in g_object_get_data ->
g_type_check_instance_is_fundamentally_a.

Confirmed live on O6N (arm64, NCZ-OS 26.10): the core of singularity-desktop
pid 2613 has main-thread frame singularity-desktop+0x152644 called from the
main-loop source dispatch; disassembling the shipped (stripped) binary at
that offset against the pinned sources maps it exactly to this lambda.

Fix: capture owned references instead of unowned so the objects stay valid
until the timer fires; the closure releases them when the source is
removed. Behavior is otherwise unchanged (the dot is still cleaned up on
the same condition).

Assisted-by: opencode:deepseek-flash
AI-Scope: Root-caused the SIGSEGV from the coredumpctl backtrace plus objdump of the stripped binary, and wrote the owned-capture fix.
perlowja added a commit to ncz-os/cix-installer that referenced this pull request Oct 13, 2026
The pinned shell (f9c93296) takes down the whole session with SIGSEGV when a
dock item is destroyed inside the 10 s launch-feedback give-up window:
DockLaunchFeedback.stretch_dot() captured the item wrapper and its dot with
`unowned` (non-owning pointers) and the timer callback then calls
g_object_get_data() on freed memory. Live on O6N arm64: core pid 2613,
main-thread frame singularity-desktop+0x152644 ->
g_object_get_data -> g_type_check_instance_is_fundamentally_a; the wrapper
then escalates to Safe Mode and the operator cannot get a desktop.

Carry upstream PR singularityos-lab/singularity-shell#37 as
assets/singularity/patches/singularity-shell/0006-dock-launch-feedback-owned-timer-captures.patch
so the next arm64 payload stops crash-looping. Verified with
build/singularity-pins.sh check-patches against the pinned commit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant