From 3a8a7cac389ea4ebaaf4b7dfbfacf210359326ec Mon Sep 17 00:00:00 2001 From: wineandchord Date: Fri, 15 Nov 2024 17:34:27 +0800 Subject: [PATCH 1/3] os/signal: check int type of signal using reflection to fix windows notification --- src/os/signal/signal_unix.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/os/signal/signal_unix.go b/src/os/signal/signal_unix.go index 21dfa416911b2..cd1111cab41ec 100644 --- a/src/os/signal/signal_unix.go +++ b/src/os/signal/signal_unix.go @@ -8,6 +8,7 @@ package signal import ( "os" + "reflect" "syscall" ) @@ -41,7 +42,24 @@ func signum(sig os.Signal) int { } return i default: - return -1 + // Use reflection to determine if sig has an underlying integer type. + // In some systems like Windows, os.Signal may have implementations + // with underlying integer types that are not directly accessible, + // as they might be defined in external packages like golang.org/x/sys. + // Since we cannot import those platform-specific signal types, + // reflection allows us to handle them in a generic way. + kind := reflect.TypeOf(sig).Kind() + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + // Extract the integer value from sig and validate it. + i := int(reflect.ValueOf(sig).Int()) + if i < 0 || i >= numSig { + return -1 + } + return i + default: + return -1 + } } } From 35c8f700a70f16f18b3f26ae7af78982af52a7ab Mon Sep 17 00:00:00 2001 From: wineandchord Date: Fri, 15 Nov 2024 18:06:46 +0800 Subject: [PATCH 2/3] use single reflect --- src/os/signal/signal_unix.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/os/signal/signal_unix.go b/src/os/signal/signal_unix.go index cd1111cab41ec..e7d2ed991d997 100644 --- a/src/os/signal/signal_unix.go +++ b/src/os/signal/signal_unix.go @@ -48,11 +48,11 @@ func signum(sig os.Signal) int { // as they might be defined in external packages like golang.org/x/sys. // Since we cannot import those platform-specific signal types, // reflection allows us to handle them in a generic way. - kind := reflect.TypeOf(sig).Kind() - switch kind { + v := reflect.ValueOf(sig) + switch v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // Extract the integer value from sig and validate it. - i := int(reflect.ValueOf(sig).Int()) + i := int(v.Int()) if i < 0 || i >= numSig { return -1 } From 3204c91d3b3e5df81864813083f4e8c009a8e3bf Mon Sep 17 00:00:00 2001 From: wineandchord Date: Fri, 15 Nov 2024 18:32:33 +0800 Subject: [PATCH 3/3] add test case --- src/os/signal/signal_windows_test.go | 100 +++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/os/signal/signal_windows_test.go b/src/os/signal/signal_windows_test.go index 145a805c50b34..67b2025e8d6a6 100644 --- a/src/os/signal/signal_windows_test.go +++ b/src/os/signal/signal_windows_test.go @@ -96,3 +96,103 @@ func main() { t.Fatalf("Program exited with error: %v\n%v", err, buf.String()) } } + +func TestCtrlBreakForCustomizedSignal(t *testing.T) { + // Create source file. + const source = ` +package main + +import ( + "log" + "os" + "os/signal" + "time" +) + +const SIGINT = Signal(0x2) + +// Signal is copied from x/sys. +type Signal int + +func (s Signal) Signal() {} + +func (s Signal) String() string { + if 0 <= s && int(s) < len(signals) { + str := signals[s] + if str != "" { + return str + } + } + return "signal " + itoa(int(s)) +} + +var signals = [...]string{ + 1: "hangup", + 2: "interrupt", + 3: "quit", + 4: "illegal instruction", + 5: "trace/breakpoint trap", + 6: "aborted", + 7: "bus error", + 8: "floating point exception", + 9: "killed", + 10: "user defined signal 1", + 11: "segmentation fault", + 12: "user defined signal 2", + 13: "broken pipe", + 14: "alarm clock", + 15: "terminated", +} + +func main() { + c := make(chan os.Signal, 10) + signal.Notify(c, SIGINT) + select { + case s := <-c: + if s != os.Interrupt { + log.Fatalf("Wrong signal received: got %q, want %q\n", s, os.Interrupt) + } + case <-time.After(3 * time.Second): + log.Fatalf("Timeout waiting for Ctrl+Break\n") + } +} +` + tmp := t.TempDir() + + // Write ctrlbreak.go. + name := filepath.Join(tmp, "ctlbreak_customized_signal") + src := name + ".go" + f, err := os.Create(src) + if err != nil { + t.Fatalf("Failed to create %v: %v", src, err) + } + defer f.Close() + f.Write([]byte(source)) + + // Compile it. + exe := name + ".exe" + defer os.Remove(exe) + o, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src).CombinedOutput() + if err != nil { + t.Fatalf("Failed to compile: %v\n%v", err, string(o)) + } + + // Run it. + cmd := testenv.Command(t, exe) + var buf strings.Builder + cmd.Stdout = &buf + cmd.Stderr = &buf + cmd.SysProcAttr = &syscall.SysProcAttr{ + CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, + } + if err := cmd.Start(); err != nil { + t.Fatalf("Start failed: %v", err) + } + go func() { + time.Sleep(1 * time.Second) + sendCtrlBreak(t, cmd.Process.Pid) + }() + if err := cmd.Wait(); err != nil { + t.Fatalf("Program exited with error: %v\n%v", err, buf.String()) + } +}