Skip to content

Fix GTK GUI crash: don't reap children ettercap doesn't own (#1310) - #1315

Open
eaescob wants to merge 1 commit into
masterfrom
fix-1310-sigchld-glycin
Open

eaescob wants to merge 1 commit into
masterfrom
fix-1310-sigchld-glycin

Conversation

@eaescob

@eaescob eaescob commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Addresses #1310.

Root cause

The GTK GUI aborts with:

Gtk:ERROR ...ensure_surface_for_gicon: assertion failed (error == NULL):
Failed to load .../image-missing.svg: IO error: No child processes (os error 10)
"bwrap" ... /usr/libexec/glycin-loaders/2+/glycin-svg ... Bail out!

No child processes is ECHILD, returned from a wait()/waitpid() call. Glycin isn't failing to render the SVG — it's failing to wait on the bwrap sandbox process it just forked, because something reaped that child first.

That something is ettercap's global SIGCHLD handler, which reaped any child:

/* src/ec_signals.c */
static void signal_CHLD(int sig) {
   ...
   while (waitpid (-1, &stat, WNOHANG) > 0);   /* reaps ANY child, not just ours */
}

When GTK/glycin forks its sandbox loader and waits on that specific PID, ettercap's handler wins the race and reaps it, so glycin's own wait returns ECHILD and it aborts.

It shows up after the GUI has been idle because glycin tears down its loader subprocess on an idle timeout and respawns it on the next icon load; the respawn-and-wait path reliably loses the race with the global reaper.

Fix

Stop reaping children ettercap doesn't own. Drop the waitpid(-1) SIGCHLD handler (leave SIGCHLD at SIG_DFL — not SIG_IGN, which would auto-reap and break glycin the same way) and reap every child ettercap forks by its specific PID at the fork site:

  • ec_filter.c func_execreplace: waitpid(child_pid) after reading the child's output (already synchronous there)
  • ec_filter.c func_exec: double-fork so the long-running command reparents to init; the parent reaps the short-lived intermediate child
  • ec_redirect.c, os/ec_linux.c: wait() → waitpid(child_pid, ...)
  • remote_browser plugin: double-fork the browser launch (another fire-and-forget fork that relied on the global reaper)

The existing popen()/system() call sites already wait internally and also benefit, since the old reaper could make their waits fail with ECHILD too.

Testing

  • Full build verified in a debian:testing container with GTK + GeoIP enabled (clean, no new warnings).
  • The runtime crash needs the GTK/glycin-as-root-then-idle environment to confirm end-to-end. @elboulangero — could you build this branch and check whether the crash is gone? Note: this fixes the ECHILD abort specifically; if bwrap also fails for a privilege/namespace reason after the privilege drop, that would surface as a different error message worth reporting separately.

🤖 Generated with Claude Code

The global SIGCHLD handler reaped any child with waitpid(-1, ...). When
GTK/glycin forks a bwrap sandbox to load an SVG icon and then waits on
it, ettercap's handler could reap that child first, so glycin's own wait
returned ECHILD ("No child processes") and aborted the process:

  Gtk:ERROR ...ensure_surface_for_gicon: assertion failed (error == NULL):
  Failed to load .../image-missing.svg: IO error: No child processes
  (os error 10) "bwrap" ... Bail out!

It surfaces after the GUI has been idle because glycin tears down its
loader subprocess on an idle timeout and respawns it on the next icon
load; the respawn-and-wait path loses the race with the global reaper.

Stop reaping children we don't own: drop the waitpid(-1) SIGCHLD handler
(leave SIGCHLD at SIG_DFL, not SIG_IGN which would auto-reap and break
glycin the same way) and reap every child ettercap forks by its specific
PID at the fork site instead:

  - ec_filter.c func_execreplace: waitpid(child_pid) after reading output
  - ec_filter.c func_exec: double-fork so the long-running command is
    reparented to init; the parent reaps the short-lived intermediate
  - ec_redirect.c, os/ec_linux.c: wait() -> waitpid(child_pid, ...)
  - remote_browser plugin: double-fork the browser launch (another
    fire-and-forget fork that relied on the global reaper)

The popen()/system() call sites already wait internally and also benefit,
since the old reaper could make their waits fail with ECHILD too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ViA4iMt6akg2RoxdN2QNVS
@0x03c1

0x03c1 commented Oct 12, 2026

Copy link
Copy Markdown

Tested the equivalent of this fix on Kali Linux Rolling ARM64 (ettercap 1:0.8.4.1-2, gdk-pixbuf 2.44.7, glycin-loaders 2.1.5) using an LD_PRELOAD shim to prevent Ettercap's SIGCHLD handler from reaping Glycin's child process. This confirmed that the ECHILD abort disappears.

However, this exposes the second failure anticipated in the PR description: once the SIGCHLD race is removed, Glycin proceeds to launch its sandboxed loader, but Bubblewrap fails with:

bwrap: setuid use of bubblewrap is not supported

In this setup, Ettercap runs with ruid=0 and euid=65534 after dropping privileges. The loader exits with status 1, icon loading fails, and GTK aborts again at gtkiconhelper.c:495.

This suggests that the SIGCHLD fix addresses the original race but does not resolve the full startup/runtime failure on this configuration. The remaining issue appears to be the interaction between Ettercap's privilege handling and Bubblewrap's sandbox requirements, with no working unsandboxed fallback in this Glycin loading path.

Could we also investigate whether Ettercap can avoid this real/effective UID combination for the GUI, or whether a supported fallback is possible when Bubblewrap cannot start? Either approach would need to preserve the intended security boundaries.

I can provide the strace excerpt and details of the reproduction steps if useful.

For local testing only, I was able to keep the GUI running by combining the shim with a bwrap passthrough wrapper that executes the loader directly. This bypasses the sandbox and should not be considered a production workaround.

@koeppea

koeppea commented Oct 16, 2026

Copy link
Copy Markdown
Member

and details of the reproduction steps if useful

I've now setup a latest Kali in a KVM. Providing info how to reproduces would be appreciated.

@elboulangero

Copy link
Copy Markdown

Steps to reproduce are given in the bug report that this MR intends to fix: #1310.

@eaescob

eaescob commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

@koeppea let me know if you're able to reproduce this. I don't have access to a Kali VM.

@0x03c1

0x03c1 commented Oct 19, 2026

Copy link
Copy Markdown

and details of the reproduction steps if useful

I've now setup a latest Kali in a KVM. Providing info how to reproduces would be appreciated.

Adding one more piece on the second failure (the one beyond the SIGCHLD race), with a deterministic 5-second repro that removes the "sporadic" factor.

Context: ettercap opens its pcap socket as root, then drops privileges to EUID 65534 (nobody) but keeps RUID 0. In that exact state, bubblewrap refuses to run, and that is what kills the glycin image loader once the SIGCHLD race is out of the way.

Repro (simulates ettercap's post-privilege-drop state, ruid=0, euid=65534):

$ sudo setpriv --ruid 0 --euid 65534 --regid 65534 --clear-groups \
    bwrap --unshare-all --die-with-parent --chdir / --ro-bind /usr /usr \
    --dev /dev --symlink usr/lib /lib --symlink usr/bin /bin /usr/bin/true
bwrap: setuid use of bubblewrap is not supported
$ echo $?
1

Controls (same bwrap command, only the UIDs change; both succeed):

$ sudo setpriv --ruid 0     --euid 0     ...   # exit 0
$ sudo setpriv --ruid 65534 --euid 65534 ...   # exit 0

So bubblewrap refuses only in the mixed-UID state ettercap leaves the process in (ruid != euid). This is structural, not a race. It was simply masked because the SIGCHLD reaper usually killed the process first (ECHILD).

Resulting failure chain:

  1. bwrap refuses to start
  2. glycin loader exits with status 1
  3. icon load fails
  4. fatal GTK abort at gtkiconhelper.c:495

@koeppea

koeppea commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Hi @eaescob

yes I've been able to reproduce the issue:

┌──(kali㉿kali)-[~]
└─$ sudo ettercap -G 
[sudo] password for kali: 

ettercap 0.8.4.1 copyright 2001-2026 Ettercap Development Team


(ettercap:3419): IBUS-WARNING **: 13:49:20.907: Failed to mkdir /root/.config/ibus/bus: Not a directory

(ettercap:3419): Gtk-WARNING **: 13:49:21.053: Could not load a pixbuf from icon theme.
This may indicate that pixbuf loaders or the mime database could not be found.
**
Gtk:ERROR:../../../gtk/gtkiconhelper.c:495:ensure_surface_for_gicon: assertion failed (error == NULL): Failed to load /usr/share/icons/Tango/16x16/status/image-missing.png: Loader process exited early with status '1'Command:
 "bwrap" "--unshare-all" "--die-with-parent" "--chdir" "/" "--ro-bind" "/usr" "/usr" "--dev" "/dev" "--ro-bind-try" "/etc/ld.so.cache" "/etc/ld.so.cache" "--ro-bind-try" "/nix/store" "/nix/store" "--tmpfs" "/tmp-home" "--tmpfs" "/tmp-run" "--clearenv" "--setenv" "HOME" "/tmp-home" "--setenv" "XDG_RUNTIME_DIR" "/tmp-run" "--symlink" "/usr/lib64" "/lib64" "--symlink" "/usr/lib" "/lib" "--symlink" "/usr/lib32" "/lib32" "--seccomp" "27" "/usr/libexec/glycin-loaders/2+/glycin-image-rs" "--dbus-fd" "26" (gdk-pixbuf-error-quark, 0)
Bail out! Gtk:ERROR:../../../gtk/gtkiconhelper.c:495:ensure_surface_for_gicon: assertion failed (error == NULL): Failed to load /usr/share/icons/Tango/16x16/status/image-missing.png: Loader process exited early with status '1'Command:  "bwrap" "--unshare-all" "--die-with-parent" "--chdir" "/" "--ro-bind" "/usr" "/usr" "--dev" "/dev" "--ro-bind-try" "/etc/ld.so.cache" "/etc/ld.so.cache" "--ro-bind-try" "/nix/store" "/nix/store" "--tmpfs" "/tmp-home" "--tmpfs" "/tmp-run" "--clearenv" "--setenv" "HOME" "/tmp-home" "--setenv" "XDG_RUNTIME_DIR" "/tmp-run" "--symlink" "/usr/lib64" "/lib64" "--symlink" "/usr/lib" "/lib" "--symlink" "/usr/lib32" "/lib32" "--seccomp" "27" "/usr/libexec/glycin-loaders/2+/glycin-image-rs" "--dbus-fd" "26" (gdk-pixbuf-error-quark, 0)
zsh: abort      sudo ettercap -G
                                                                                                                      
┌──(kali㉿kali)-[~]
└─$ 

Opened Ettercap in setup-screen and left it there. Opened some other applications for a couple of minutes. Then proceeded Ettercap from setup screen hitting the Accept Button, then it crashed with the above output on its calling terminal.

@eaescob

eaescob commented Oct 26, 2026 via email

Copy link
Copy Markdown
Contributor Author

@devin-ai-integration

Copy link
Copy Markdown
Contributor

@koeppea @0x03c1 @elboulangero, thanks for the repro. The second failure (Loader process exited early with status '1', with bwrap refusing to start because of ruid=0/euid=65534) is addressed in #1338.

drop_privs() now uses setresuid(uid, uid, 0) / setresgid(gid, gid, 0). The real and effective IDs both become the drop user, and only the saved UID stays root, so regain_privs() keeps working for redirect commands and the ip_forward restore. Children such as bwrap and glycin then run fully as nobody, which bwrap accepts.

The two PRs are independent, so the full fix for #1310 needs both. To test them together:

git fetch origin fix-1310-sigchld-glycin devin/1793039564-drop-privs-ruid
git checkout -b test-1310 origin/fix-1310-sigchld-glycin
git merge --no-edit origin/devin/1793039564-drop-privs-ruid
mkdir build && cd build && cmake .. -DGTK_BUILD_TYPE=GTK3 && make -j$(nproc) && sudo make install
sudo ettercap -G

Then:

  1. Click Accept. This is where privileges are dropped.
  2. Check grep Uid /proc/$(pgrep -x ettercap)/status. It should show 65534 65534 0 65534.
  3. Leave the GUI idle for over a minute, then open the menus.

I reproduced the abort on master in a debian:testing container (glycin-loaders 2.x, bubblewrap). With #1338, and with #1338 + #1315 combined, ettercap stayed up after the idle period. Confirmation on Kali would help.

@koeppea

koeppea commented Oct 26, 2026

Copy link
Copy Markdown
Member

Confirmation on Kali would help.

Taking care of that.

This branch has not been deployed

No deployments
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.

4 participants