The protobuf API marks PrintToString as nodiscard, so ignoring its
return value broke the build under -Werror=unused-result. Check the
result and bail out with a warning if formatting fails.
ENOENT means the thread has moved on (killed or interrupted).
EINPROGRESS indicates either misuse by sending again after
a successful send (which we're not doing) but we also see it
when a process is exiting, perhaps related to thread shutdown.
Authored-by: rsc@google.com
Tested-by: rsc@google.com
Most of the system calls involved in the unotify poll loop were subtly wrong.
The biggest problem was the reuse of the previous message's req.id with
isTargetAlive to decide whether to exit the loop. The loop can be watching
many threads and many processes, any one of which might exit at any time
(especially if the last observed system call was exit!), so it does not make
sense to focus on a specific thread except within the context of a pending
syscall being evaluated.
SECCOMP_IOCTL_NOTIF_ID_VALID is for one purpose and one purpose only:
checking after an access by pid that the pid was not reused, invalidating
whatever was just read. The only time that purpose applies is between
parseSyscall and addStat, which is now the only time that the loop calls
isTargetAlive.
The loop was misusing isTargetAlive to decide when to exit the loop.
Now it correctly waits until a POLLHUP event.
The loop was misusing isTargetAlive to decide whether to call
SECCOMP_IOCTL_NOTIF_SEND. That's going to do the same check itself,
because otherwise there would be a race between the check and the send.
That redundant check is deleted.
The loop was also misusing isTargetAlive after a failed
SECCOMP_IOCTL_NOTIF_SEND to decide whether to exit the loop.
As before, one target being dead does not imply that all the traced targets
are dead, so that check is deleted. On failure, if the error is EINTR,
the send is tried again. If the error is ENOENT, that means the thread is
no longer blocked in the system call (either due to an interrupt or signal),
so we stop the send attempt but continue the processing loop.
Otherwise we print the error error, stop the send, but continue the processing loop.
The hangs seem to have been caused by a few different failure modes:
1. The loop could have been exiting prematurely, hanging the syscalls
waiting for judgement.
2. In one strace log I read carefully, the loop kept running after POLLHUP,
hanging in SECCOMP_IOCTL_NOTIF_RECV. I believe this may have been
fixed between 6.6 and 6.12.
3. The loop was not reacting well at all to send failing because the syscall
had been interrupted. This manifested as needing to run Go programs
with GODEBUG=asyncpreemptoff=1 to let them run at all.
With these changes, Go programs using signals work just fine.
Authored-by: rsc@google.com
Tested-by: rsc@google.com
* Introduces a dedicated, async worker thread that monitors SECCOMP_RET_USER_NOTIF events
* Produces telemetry using protobufs to track resolved namespace paths, and network endpoints