From 9b2561ef16307ad5f918e81db2521a78807280f5 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Tue, 11 Jun 2013 13:06:38 +1000 Subject: [PATCH] os: request appropriate access rights before calling windows TerminateProcess Fixes #5615. R=golang-dev, rsc CC=golang-dev https://golang.org/cl/9651047 --- src/pkg/os/exec_windows.go | 13 +++++- src/pkg/os/os_test.go | 71 +++++++++++++++++++++++++++++++ src/pkg/syscall/ztypes_windows.go | 1 + 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go index 4aa2ade631e..c4f3d4f8530 100644 --- a/src/pkg/os/exec_windows.go +++ b/src/pkg/os/exec_windows.go @@ -42,13 +42,22 @@ func (p *Process) wait() (ps *ProcessState, err error) { return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil } +func terminateProcess(pid, exitcode int) error { + h, e := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(pid)) + if e != nil { + return NewSyscallError("OpenProcess", e) + } + defer syscall.CloseHandle(h) + e = syscall.TerminateProcess(h, uint32(exitcode)) + return NewSyscallError("TerminateProcess", e) +} + func (p *Process) signal(sig Signal) error { if p.done() { return errors.New("os: process already finished") } if sig == Kill { - e := syscall.TerminateProcess(syscall.Handle(p.handle), 1) - return NewSyscallError("TerminateProcess", e) + return terminateProcess(p.Pid, 1) } // TODO(rsc): Handle Interrupt too? return syscall.Errno(syscall.EWINDOWS) diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go index 1009600fef8..025b709b81c 100644 --- a/src/pkg/os/os_test.go +++ b/src/pkg/os/os_test.go @@ -11,11 +11,13 @@ import ( "io" "io/ioutil" . "os" + osexec "os/exec" "path/filepath" "runtime" "strings" "syscall" "testing" + "text/template" "time" ) @@ -1130,3 +1132,72 @@ func TestReadAtEOF(t *testing.T) { t.Fatalf("ReadAt failed: %s", err) } } + +func testKillProcess(t *testing.T, processKiller func(p *Process)) { + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("Failed to create temp directory: %v", err) + } + defer RemoveAll(dir) + + src := filepath.Join(dir, "main.go") + f, err := Create(src) + if err != nil { + t.Fatalf("Failed to create %v: %v", src, err) + } + st := template.Must(template.New("source").Parse(` +package main +import "time" +func main() { + time.Sleep(time.Second) +} +`)) + err = st.Execute(f, nil) + if err != nil { + f.Close() + t.Fatalf("Failed to execute template: %v", err) + } + f.Close() + + exe := filepath.Join(dir, "main.exe") + output, err := osexec.Command("go", "build", "-o", exe, src).CombinedOutput() + if err != nil { + t.Fatalf("Failed to build exe %v: %v %v", exe, err, string(output)) + } + + cmd := osexec.Command(exe) + err = cmd.Start() + if err != nil { + t.Fatalf("Failed to start test process: %v", err) + } + go func() { + time.Sleep(100 * time.Millisecond) + processKiller(cmd.Process) + }() + err = cmd.Wait() + if err == nil { + t.Errorf("Test process succeeded, but expected to fail") + } +} + +func TestKillStartProcess(t *testing.T) { + testKillProcess(t, func(p *Process) { + err := p.Kill() + if err != nil { + t.Fatalf("Failed to kill test process: %v", err) + } + }) +} + +func TestKillFindProcess(t *testing.T) { + testKillProcess(t, func(p *Process) { + p2, err := FindProcess(p.Pid) + if err != nil { + t.Fatalf("Failed to find test process: %v", err) + } + err = p2.Kill() + if err != nil { + t.Fatalf("Failed to kill test process: %v", err) + } + }) +} diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go index fb74b3e57ce..2e9508b1fe2 100644 --- a/src/pkg/syscall/ztypes_windows.go +++ b/src/pkg/syscall/ztypes_windows.go @@ -151,6 +151,7 @@ const ( CREATE_NEW_PROCESS_GROUP = 0x00000200 CREATE_UNICODE_ENVIRONMENT = 0x00000400 + PROCESS_TERMINATE = 1 PROCESS_QUERY_INFORMATION = 0x00000400 SYNCHRONIZE = 0x00100000