mirror of
https://github.com/golang/go.git
synced 2026-04-03 17:59:55 +09:00
os/signal: completely ignore bogus signals
signal.Notify should ignore bogus signals (syscall.Signals with invalid signal numbers). Ignoring means Notify/Stop/Reset all work fine, but the channel will never receive any events. Today, Stop hangs if Notify has only ever been called with bogus signals because Stop assumes that runtime.signal_recv is running if a handler is present. Notify unconditionally registers the handler, but only starts signal_recv if the signal was valid. Address this by avoiding registering the handler at all if the signal is bogus. We additionally add a bogus signal check to cancel (used by Ignore/Reset). Currently those are calling into the runtime signal_ignore and signal_disable, which do ignore bogus signals, but is now inconsistent with the rest of the package. For #77076. Change-Id: I6a6a636c27c41a158e203bbf470be5f1f3f631bd Reviewed-on: https://go-review.googlesource.com/c/go/+/735040 Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alex Brainman <alex.brainman@gmail.com> Auto-Submit: Michael Pratt <mpratt@google.com>
This commit is contained in:
committed by
Gopher Robot
parent
01b795bc4c
commit
c2fabf1a26
@@ -54,6 +54,10 @@ func cancel(sigs []os.Signal, action func(int)) {
|
||||
defer handlers.Unlock()
|
||||
|
||||
remove := func(n int) {
|
||||
if n < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var zerohandler handler
|
||||
|
||||
for c, h := range handlers.m {
|
||||
@@ -127,19 +131,27 @@ func Notify(c chan<- os.Signal, sig ...os.Signal) {
|
||||
handlers.Lock()
|
||||
defer handlers.Unlock()
|
||||
|
||||
h := handlers.m[c]
|
||||
if h == nil {
|
||||
if handlers.m == nil {
|
||||
handlers.m = make(map[chan<- os.Signal]*handler)
|
||||
// Lazily create the handler. If all of the signals are bogus there is
|
||||
// no need to install a handler at all.
|
||||
getHandler := func() *handler {
|
||||
h := handlers.m[c]
|
||||
if h == nil {
|
||||
if handlers.m == nil {
|
||||
handlers.m = make(map[chan<- os.Signal]*handler)
|
||||
}
|
||||
h = new(handler)
|
||||
handlers.m[c] = h
|
||||
}
|
||||
h = new(handler)
|
||||
handlers.m[c] = h
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
add := func(n int) {
|
||||
if n < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
h := getHandler()
|
||||
if !h.want(n) {
|
||||
h.set(n)
|
||||
if handlers.ref[n] == 0 {
|
||||
|
||||
26
src/runtime/signal_test.go
Normal file
26
src/runtime/signal_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2026 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSignalBogus(t *testing.T) {
|
||||
if runtime.GOOS == "plan9" {
|
||||
t.Skip("No syscall.Signal on plan9")
|
||||
}
|
||||
|
||||
// This test more properly belongs in os/signal, but it depends on
|
||||
// containing the only call to signal.Notify in the process, so it must
|
||||
// run as an isolated subprocess, which is simplest with testprog.
|
||||
t.Parallel()
|
||||
output := runTestProg(t, "testprog", "SignalBogus")
|
||||
want := "OK\n"
|
||||
if output != want {
|
||||
t.Fatalf("output is not %q\n%s", want, output)
|
||||
}
|
||||
}
|
||||
29
src/runtime/testdata/testprog/signal_bogus.go
vendored
Normal file
29
src/runtime/testdata/testprog/signal_bogus.go
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2026 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !plan9
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("SignalBogus", SignalBogus)
|
||||
}
|
||||
|
||||
// signal.Notify should effectively ignore bogus signal numbers. Never writing
|
||||
// to the channel, but otherwise allowing Notify/Stop as normal.
|
||||
//
|
||||
// This is a regression test for https://go.dev/issue/77076, where bogus
|
||||
// signals used to make Stop hang if there were no real signals installed.
|
||||
func SignalBogus() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.Signal(0xdead))
|
||||
signal.Stop(ch)
|
||||
println("OK")
|
||||
}
|
||||
Reference in New Issue
Block a user