Skip to content

fix: prevent IPC socket fd leak into child processes - #388

Open
Anntoin wants to merge 1 commit into
houmain:mainfrom
Anntoin:fix/socket-cloexec
Open

fix: prevent IPC socket fd leak into child processes#388
Anntoin wants to merge 1 commit into
houmain:mainfrom
Anntoin:fix/socket-cloexec

Conversation

@Anntoin

@Anntoin Anntoin commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Problem

Keymapper creates an abstract Unix domain socket (@keymapperctl) for IPC between the client and daemon. This socket fd is inherited by child processes spawned via $(...) config commands (e.g. kitty, tofi, swaymsg).

These children keep the socket fd open and listening. When keymapper is restarted (e.g. by a service manager, or config reload), the new process cannot bind @keymapperctl because the leaked processes still hold it. This produces Binding control port failed errors and keybindings stop working until all the leaked processes are manually killed.

Root Cause

All socket() calls in Host.cpp use plain SOCK_STREAM without SOCK_CLOEXEC, so the fd survives exec() into child processes.

Fix

Add SOCK_CLOEXEC to all Unix socket calls in Host.cpp:

  • listen(): socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)
  • accept(): use accept4() with SOCK_CLOEXEC when available (detected via __GLIBC_PREREQ(2,10) for glibc; musl provides it without any feature-test macro), fall back to accept() + fcntl(FD_CLOEXEC) otherwise
  • connect(): SOCK_CLOEXEC on both initial and retry socket() calls

Testing

  • Verified accept4 and SOCK_CLOEXEC present in the built binary via objdump / nm -D
  • Confirmed spawned terminals (kitty) no longer appear in ss -xlp output for @keymapperctl after keymapper restart
  • Service restarts cleanly without Binding control port failed
  • Tested on Linux x86_64 with keymapper 5.6.0

Compatibility

  • accept4 availability is detected via __GLIBC_PREREQ(2,10) for glibc. On musl it is available without _GNU_SOURCE. Other platforms (macOS, BSD) fall back to accept() + fcntl(FD_CLOEXEC).
  • No behaviour change for existing users — CLOEXEC only affects exec(), not normal fd lifetime.

@Anntoin
Anntoin force-pushed the fix/socket-cloexec branch 5 times, most recently from d9fc1d3 to f048c13 Compare August 10, 2026 14:14
Use SOCK_CLOEXEC on all AF_UNIX sockets (listen, accept, connect) so
that the @keymapperctl abstract socket fd is not inherited by child
processes spawned via $(...) config commands (e.g. kitty, tofi).

Previously, the inherited fd kept listening on the socket, preventing
a restarted keymapper from rebinding it — resulting in 'Binding control
port failed' errors and keybindings silently failing until the leaked
processes were manually killed.

In accept(), use accept4() with SOCK_CLOEXEC when available (detected
via __GLIBC_PREREQ(2,10) for glibc; musl provides it without any
feature-test macro) and fall back to accept() + fcntl(FD_CLOEXEC)
otherwise, matching the existing platform split used for abstract
socket addresses in this file.

Assisted-by: Hermes:glm-5.2 [gh]
@Anntoin
Anntoin force-pushed the fix/socket-cloexec branch from f048c13 to 07c3f12 Compare August 10, 2026 14:17
@Anntoin
Anntoin marked this pull request as ready for review August 10, 2026 14:22
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