Skip to content

fix(ping): eliminate seteuid race by pre-opening ICMP socket at init#521

Closed
somethingwithproof wants to merge 3 commits intoCacti:developfrom
somethingwithproof:fix/seteuid-icmp-race
Closed

fix(ping): eliminate seteuid race by pre-opening ICMP socket at init#521
somethingwithproof wants to merge 3 commits intoCacti:developfrom
somethingwithproof:fix/seteuid-icmp-race

Conversation

@somethingwithproof
Copy link
Copy Markdown
Contributor

Problem

ping_icmp() called seteuid(0) inside each worker thread to open a raw ICMP socket. seteuid is process-wide, so while one thread held LOCK_SETEUID with euid=0, other threads that did not check the mutex could inherit the elevated privilege. The mutex serialized the seteuid calls but did not close the privilege window for concurrent threads.

Fix

Open the ICMP raw socket once in spine.c main(), before any worker threads start. The single seteuid(0)/seteuid(getuid()) pair now happens in a single-threaded context with no race.

ping_icmp() calls dup(icmp_socket) to get a per-thread fd, so select(), setsockopt(), and close() in each thread operate on independent descriptors without interfering with other threads or the global socket.

If the socket cannot be opened at startup (permissions, capability check), icmp_avail is set to FALSE and spine falls back to UDP ping as before.

Changes

  • spine.c: add int icmp_socket = -1 global; open raw socket before thread pool starts
  • spine.h: extern int icmp_socket
  • ping.c: remove all seteuid/LOCK_SETEUID blocks from ping_icmp; replace local socket open loop with dup(icmp_socket); rename local icmp_socket to icmp_fd throughout

Verification

Built clean on macOS aarch64 (gcc -std=gnu23, no errors or warnings).
Net change: 49 insertions, 103 deletions.

…RITICAL)

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…HIGH)

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
seteuid(0) is process-wide; the previous approach of acquiring
LOCK_SETEUID per-thread serialized the seteuid calls but left a window
where other threads inherited euid=0 while the mutex was held.

Open the ICMP raw socket once during single-threaded initialization in
spine.c main(), before any worker threads start. Store it as a global
(icmp_socket). ping_icmp() now dup()s that fd per call so each thread
has an independent fd for select()/setsockopt()/close() without
interfering with other threads.

All seteuid()/LOCK_SETEUID blocks are removed from ping_icmp(). If the
socket could not be opened at startup, icmp_avail is set to FALSE and
the poller falls back to UDP ping as before.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
@somethingwithproof
Copy link
Copy Markdown
Contributor Author

Consolidated into mega PR #522 for independent mergeability.

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