mirror of
https://github.com/golang/go
synced 2024-11-25 08:57:58 -07:00
syscall: make use of StartupInfo.Reserved2 on Windows
On Windows, some applications (for example, Chromium) use _get_osfhandle to retrieve additional open files inherited from its parent process. This C Runtime function requires the parent process to pass the information about the inherited file descriptors in the field StartupInfo.Reserved2. This field is not in use as of now; and making use of it is supposed to not break anything. The MSDN just states that the field is "Reserved for use by the C Run-time". The implementation is found in these projects: nodejs: https://github.com/nodejs/node/blob/71691e53/deps/uv/src/win/process-stdio.c#L32-L37 ReactOS: https://github.com/reactos/reactos/blob/57c84dd6/sdk/lib/crt/stdio/file.c#L405-L532
This commit is contained in:
parent
ceda93ed67
commit
f0e594bdb3
@ -7,18 +7,21 @@
|
|||||||
package exec_test
|
package exec_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerHelperCommand("pipehandle", cmdPipeHandle)
|
registerHelperCommand("pipehandle", cmdPipeHandle)
|
||||||
|
registerHelperCommand("crtpipehandle", cmdCRTPipeHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdPipeHandle(args ...string) {
|
func cmdPipeHandle(args ...string) {
|
||||||
@ -59,6 +62,104 @@ func TestPipePassing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdCRTPipeHandle(args ...string) {
|
||||||
|
get_osfhandle := syscall.NewLazyDLL("msvcrt.dll").NewProc("_get_osfhandle")
|
||||||
|
|
||||||
|
h3, _, _ := get_osfhandle.Call(3)
|
||||||
|
if h3 == uintptr(syscall.InvalidHandle) {
|
||||||
|
fmt.Fprintf(os.Stderr, "_get_osfhandle: pipe 3 is invalid\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
pipe3 := os.NewFile(h3, "in")
|
||||||
|
defer pipe3.Close()
|
||||||
|
|
||||||
|
h4, _, _ := get_osfhandle.Call(4)
|
||||||
|
if h4 == uintptr(syscall.InvalidHandle) {
|
||||||
|
fmt.Fprintf(os.Stderr, "_get_osfhandle: pipe 4 is invalid\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
pipe4 := os.NewFile(h4, "out")
|
||||||
|
defer pipe4.Close()
|
||||||
|
|
||||||
|
br := bufio.NewReader(pipe3)
|
||||||
|
line, _, err := br.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "reading pipe failed: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if string(line) == "ping" {
|
||||||
|
_, err := fmt.Fprintf(pipe4, "%s\n", args[0])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "writing to pipe failed: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "unexpected content from pipe: %q\n", line)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCRTPipePassing(t *testing.T) {
|
||||||
|
crt := syscall.NewLazyDLL("msvcrt.dll")
|
||||||
|
if err := crt.Load(); err != nil {
|
||||||
|
t.Skipf("can't run test due to missing msvcrt.dll: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r3, w3, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create pipe 3: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
r3.Close()
|
||||||
|
w3.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
r4, w4, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create pipe 4: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
r4.Close()
|
||||||
|
w4.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
const marker = "pong"
|
||||||
|
childProc := helperCommand(t, "crtpipehandle", marker)
|
||||||
|
childProc.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
AdditionalInheritedHandles: []syscall.Handle{
|
||||||
|
syscall.Handle(r3.Fd()),
|
||||||
|
syscall.Handle(w4.Fd()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
output, err := childProc.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("child proc exited: %v. output:\n%s", err, output)
|
||||||
|
r3.Close()
|
||||||
|
w4.Close()
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = fmt.Fprint(w3, "ping\n")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("writing pipe failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
br := bufio.NewReader(r4)
|
||||||
|
response, _, err := br.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if string(response) != marker {
|
||||||
|
t.Errorf("got %q; want %q", string(response), marker)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
func TestNoInheritHandles(t *testing.T) {
|
func TestNoInheritHandles(t *testing.T) {
|
||||||
cmd := exec.Command("cmd", "/c exit 88")
|
cmd := exec.Command("cmd", "/c exit 88")
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{NoInheritHandles: true}
|
cmd.SysProcAttr = &syscall.SysProcAttr{NoInheritHandles: true}
|
||||||
|
@ -398,6 +398,9 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
if len(fd) > 3 {
|
||||||
|
si.CbReserved2, si.Reserved2 = createChildStdioBuffer(fd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := new(ProcessInformation)
|
pi := new(ProcessInformation)
|
||||||
@ -420,3 +423,59 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
|
|||||||
func Exec(argv0 string, argv []string, envv []string) (err error) {
|
func Exec(argv0 string, argv []string, envv []string) (err error) {
|
||||||
return EWINDOWS
|
return EWINDOWS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The C Run-time (CRT) file descriptor mode flags. See
|
||||||
|
// https://github.com/nodejs/node/blob/71691e53/deps/uv/src/win/process-stdio.c#L57-L65
|
||||||
|
const (
|
||||||
|
crt_FD_OPEN uint8 = 1 << iota
|
||||||
|
crt_FD_EOFLAG
|
||||||
|
crt_FD_CRLF
|
||||||
|
crt_FD_PIPE
|
||||||
|
crt_FD_NOINHERIT
|
||||||
|
crt_FD_APPEND
|
||||||
|
crt_FD_DEV
|
||||||
|
crt_FD_TEXT
|
||||||
|
)
|
||||||
|
|
||||||
|
// createChildStdioBuffer creates a buffer that describes the handles that will
|
||||||
|
// be inherited by the child process so that the child process can retrieve the
|
||||||
|
// inherited handles by calling the CRT function _get_osfhandle.
|
||||||
|
//
|
||||||
|
// The buffer has the following layout:
|
||||||
|
// int number_of_fds
|
||||||
|
// unsigned char crt_flags[number_of_fds]
|
||||||
|
// HANDLE os_handle[number_of_fds]
|
||||||
|
//
|
||||||
|
// See:
|
||||||
|
// https://github.com/nodejs/node/blob/71691e53/deps/uv/src/win/process-stdio.c#L32-L37
|
||||||
|
// https://github.com/reactos/reactos/blob/57c84dd6/sdk/lib/crt/stdio/file.c#L405-L532
|
||||||
|
// https://github.com/reactos/reactos/blob/57c84dd6/sdk/lib/crt/stdio/file.c#L1588-L1599
|
||||||
|
func createChildStdioBuffer(fd []Handle) (uint16, *byte) {
|
||||||
|
count := int32(len(fd))
|
||||||
|
var crtFlags uint8
|
||||||
|
int32Size := unsafe.Sizeof(count)
|
||||||
|
flagsSize := unsafe.Sizeof(crtFlags)
|
||||||
|
handleSize := unsafe.Sizeof(fd[0])
|
||||||
|
|
||||||
|
size := uint16(int32Size + flagsSize*uintptr(count) + handleSize*uintptr(count))
|
||||||
|
buf := make([]byte, size)
|
||||||
|
|
||||||
|
*(*int32)(unsafe.Pointer(&buf[0])) = count
|
||||||
|
|
||||||
|
for i := uintptr(0); i < uintptr(count); i++ {
|
||||||
|
// (ZekeLu) should it be restrict to report the error for invalid or
|
||||||
|
// unknown handles?
|
||||||
|
ft, _ := GetFileType(fd[i])
|
||||||
|
switch ft {
|
||||||
|
case FILE_TYPE_DISK:
|
||||||
|
crtFlags = crt_FD_OPEN
|
||||||
|
case FILE_TYPE_PIPE:
|
||||||
|
crtFlags = crt_FD_OPEN | crt_FD_PIPE
|
||||||
|
default:
|
||||||
|
crtFlags = crt_FD_OPEN | crt_FD_DEV
|
||||||
|
}
|
||||||
|
*(*uint8)(unsafe.Pointer(&buf[int32Size+flagsSize*i])) = crtFlags
|
||||||
|
*(*Handle)(unsafe.Pointer(&buf[int32Size+flagsSize*uintptr(count)+handleSize*i])) = fd[i]
|
||||||
|
}
|
||||||
|
return size, &buf[0]
|
||||||
|
}
|
||||||
|
@ -483,8 +483,8 @@ type StartupInfo struct {
|
|||||||
FillAttribute uint32
|
FillAttribute uint32
|
||||||
Flags uint32
|
Flags uint32
|
||||||
ShowWindow uint16
|
ShowWindow uint16
|
||||||
_ uint16
|
CbReserved2 uint16
|
||||||
_ *byte
|
Reserved2 *byte
|
||||||
StdInput Handle
|
StdInput Handle
|
||||||
StdOutput Handle
|
StdOutput Handle
|
||||||
StdErr Handle
|
StdErr Handle
|
||||||
|
Loading…
Reference in New Issue
Block a user