github.com/lzhfromustc/gofuzz@v0.0.0-20211116160056-151b3108bbd1/runtime/signal_windows_test.go (about) 1 // +build windows 2 3 package runtime_test 4 5 import ( 6 "bufio" 7 "bytes" 8 "fmt" 9 "internal/testenv" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "runtime" 14 "strconv" 15 "strings" 16 "syscall" 17 "testing" 18 ) 19 20 func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) { 21 if *flagQuick { 22 t.Skip("-quick") 23 } 24 if runtime.GOARCH != "amd64" { 25 t.Skip("this test can only run on windows/amd64") 26 } 27 testenv.MustHaveGoBuild(t) 28 testenv.MustHaveExecPath(t, "gcc") 29 testprog.Lock() 30 defer testprog.Unlock() 31 dir, err := os.MkdirTemp("", "go-build") 32 if err != nil { 33 t.Fatalf("failed to create temp directory: %v", err) 34 } 35 defer os.RemoveAll(dir) 36 37 // build go dll 38 dll := filepath.Join(dir, "testwinlib.dll") 39 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "--buildmode", "c-shared", "testdata/testwinlib/main.go") 40 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 41 if err != nil { 42 t.Fatalf("failed to build go library: %s\n%s", err, out) 43 } 44 45 // build c program 46 exe := filepath.Join(dir, "test.exe") 47 cmd = exec.Command("gcc", "-L"+dir, "-I"+dir, "-ltestwinlib", "-o", exe, "testdata/testwinlib/main.c") 48 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 49 if err != nil { 50 t.Fatalf("failed to build c exe: %s\n%s", err, out) 51 } 52 53 // run test program 54 cmd = exec.Command(exe) 55 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 56 if err != nil { 57 t.Fatalf("failure while running executable: %s\n%s", err, out) 58 } 59 expectedOutput := "exceptionCount: 1\ncontinueCount: 1\n" 60 // cleaning output 61 cleanedOut := strings.ReplaceAll(string(out), "\r\n", "\n") 62 if cleanedOut != expectedOutput { 63 t.Errorf("expected output %q, got %q", expectedOutput, cleanedOut) 64 } 65 } 66 67 func sendCtrlBreak(pid int) error { 68 kernel32, err := syscall.LoadDLL("kernel32.dll") 69 if err != nil { 70 return fmt.Errorf("LoadDLL: %v\n", err) 71 } 72 generateEvent, err := kernel32.FindProc("GenerateConsoleCtrlEvent") 73 if err != nil { 74 return fmt.Errorf("FindProc: %v\n", err) 75 } 76 result, _, err := generateEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid)) 77 if result == 0 { 78 return fmt.Errorf("GenerateConsoleCtrlEvent: %v\n", err) 79 } 80 return nil 81 } 82 83 // TestCtrlHandler tests that Go can gracefully handle closing the console window. 84 // See https://golang.org/issues/41884. 85 func TestCtrlHandler(t *testing.T) { 86 testenv.MustHaveGoBuild(t) 87 t.Parallel() 88 89 // build go program 90 exe := filepath.Join(t.TempDir(), "test.exe") 91 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "testdata/testwinsignal/main.go") 92 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 93 if err != nil { 94 t.Fatalf("failed to build go exe: %v\n%s", err, out) 95 } 96 97 // run test program 98 cmd = exec.Command(exe) 99 var stderr bytes.Buffer 100 cmd.Stderr = &stderr 101 outPipe, err := cmd.StdoutPipe() 102 if err != nil { 103 t.Fatalf("Failed to create stdout pipe: %v", err) 104 } 105 outReader := bufio.NewReader(outPipe) 106 107 // in a new command window 108 const _CREATE_NEW_CONSOLE = 0x00000010 109 cmd.SysProcAttr = &syscall.SysProcAttr{ 110 CreationFlags: _CREATE_NEW_CONSOLE, 111 HideWindow: true, 112 } 113 if err := cmd.Start(); err != nil { 114 t.Fatalf("Start failed: %v", err) 115 } 116 defer func() { 117 cmd.Process.Kill() 118 cmd.Wait() 119 }() 120 121 // wait for child to be ready to receive signals 122 if line, err := outReader.ReadString('\n'); err != nil { 123 t.Fatalf("could not read stdout: %v", err) 124 } else if strings.TrimSpace(line) != "ready" { 125 t.Fatalf("unexpected message: %s", line) 126 } 127 128 // gracefully kill pid, this closes the command window 129 if err := exec.Command("taskkill.exe", "/pid", strconv.Itoa(cmd.Process.Pid)).Run(); err != nil { 130 t.Fatalf("failed to kill: %v", err) 131 } 132 133 // check child received, handled SIGTERM 134 if line, err := outReader.ReadString('\n'); err != nil { 135 t.Fatalf("could not read stdout: %v", err) 136 } else if expected, got := syscall.SIGTERM.String(), strings.TrimSpace(line); expected != got { 137 t.Fatalf("Expected '%s' got: %s", expected, got) 138 } 139 140 // check child exited gracefully, did not timeout 141 if err := cmd.Wait(); err != nil { 142 t.Fatalf("Program exited with error: %v\n%s", err, &stderr) 143 } 144 } 145 146 // TestLibraryCtrlHandler tests that Go DLL allows calling program to handle console control events. 147 // See https://golang.org/issues/35965. 148 func TestLibraryCtrlHandler(t *testing.T) { 149 if *flagQuick { 150 t.Skip("-quick") 151 } 152 if runtime.GOARCH != "amd64" { 153 t.Skip("this test can only run on windows/amd64") 154 } 155 testenv.MustHaveGoBuild(t) 156 testenv.MustHaveExecPath(t, "gcc") 157 testprog.Lock() 158 defer testprog.Unlock() 159 dir, err := os.MkdirTemp("", "go-build") 160 if err != nil { 161 t.Fatalf("failed to create temp directory: %v", err) 162 } 163 defer os.RemoveAll(dir) 164 165 // build go dll 166 dll := filepath.Join(dir, "dummy.dll") 167 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "--buildmode", "c-shared", "testdata/testwinlibsignal/dummy.go") 168 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 169 if err != nil { 170 t.Fatalf("failed to build go library: %s\n%s", err, out) 171 } 172 173 // build c program 174 exe := filepath.Join(dir, "test.exe") 175 cmd = exec.Command("gcc", "-o", exe, "testdata/testwinlibsignal/main.c") 176 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 177 if err != nil { 178 t.Fatalf("failed to build c exe: %s\n%s", err, out) 179 } 180 181 // run test program 182 cmd = exec.Command(exe) 183 var stderr bytes.Buffer 184 cmd.Stderr = &stderr 185 outPipe, err := cmd.StdoutPipe() 186 if err != nil { 187 t.Fatalf("Failed to create stdout pipe: %v", err) 188 } 189 outReader := bufio.NewReader(outPipe) 190 191 cmd.SysProcAttr = &syscall.SysProcAttr{ 192 CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, 193 } 194 if err := cmd.Start(); err != nil { 195 t.Fatalf("Start failed: %v", err) 196 } 197 198 errCh := make(chan error, 1) 199 go func() { 200 if line, err := outReader.ReadString('\n'); err != nil { 201 errCh <- fmt.Errorf("could not read stdout: %v", err) 202 } else if strings.TrimSpace(line) != "ready" { 203 errCh <- fmt.Errorf("unexpected message: %v", line) 204 } else { 205 errCh <- sendCtrlBreak(cmd.Process.Pid) 206 } 207 }() 208 209 if err := <-errCh; err != nil { 210 t.Fatal(err) 211 } 212 if err := cmd.Wait(); err != nil { 213 t.Fatalf("Program exited with error: %v\n%s", err, &stderr) 214 } 215 }