cgroup: say exactly why os/signal's gaps cannot hold an agent ignore (#272)
Self-review of the finding 2 fix. The comment on ignoreSignals said the agent's runtime "keeps" SIGPROF and the slotless signals, so the agent could only have them ignored by inheritance. It installs its own SIGPROF handler whatever it inherited, and a CPU profile does not leave SIGPROF ignored (checked on Go 1.26, Linux), so SIGPROF is never in the agent's set; 32 (and 33 with cgo) stay as inherited in agent and shim alike. The read-back check still fails the start on anything this misses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+15
−9
internal/cgroup/shim_linux.go+15 −9
diff --git a/internal/cgroup/shim_linux.go b/internal/cgroup/shim_linux.go| index 77da9eb6f..f35f7214e 100644 |
| --- a/internal/cgroup/shim_linux.go |
| +++ b/internal/cgroup/shim_linux.go |
| @@ -499,15 +499,21 @@ func ignoredSignals() (uint64, error) { |
| // ignoreSignals sets every signal in set (bit n-1 is signal n) to SIG_IGN |
| // through os/signal, which keeps the shim's runtime in step, and then |
| -// checks the kernel's view: each one must read back ignored. os/signal |
| -// cannot ignore a signal its runtime keeps for itself (SIGPROF, and the |
| -// few without a Notify slot), but the agent's runtime keeps those too, so |
| -// the agent cannot have ignored one unless it inherited the ignore, and then |
| -// so did the shim, whose runtime leaves it alone as the agent's did. The |
| -// check turns any case this reasoning misses into a failed start rather |
| -// than a tenant with a handled signal it should have ignored. Signals the |
| -// shim inherited ignored beyond set are left as they are: they come from the |
| -// same agent, so a direct start at that moment would have kept them too. |
| +// checks the kernel's view: each one must read back ignored. |
| +// |
| +// os/signal will not ignore SIGPROF, nor signals 32 and 33, which have no |
| +// Notify slot. None of them can be in the set while it does nothing: |
| +// SIGPROF and (without cgo) 33 get the agent runtime's own handler at |
| +// start, whatever the agent inherited, so the agent never has them ignored |
| +// (a CPU profile does not leave SIGPROF ignored either: checked on Go 1.26, |
| +// Linux); 32, and 33 with cgo, the runtime leaves as inherited, in the |
| +// agent and in the shim alike, so an inherited ignore is still in place. |
| +// The read-back check turns anything this reasoning misses into a failed |
| +// start (a placement failure: a flat start falls back to a direct start, |
| +// a generation start is refused), never a tenant with a handled signal it |
| +// should have ignored. Signals the shim inherited ignored beyond set are |
| +// left as they are: they come from the same agent, so a direct start at |
| +// that moment would have kept them too. |
| func ignoreSignals(set uint64) error { |
| for n := 1; n <= 64; n++ { |
| if set&(1<<(n-1)) != 0 { |