From 01c413d3ff9b41c7b079eaa1fcf653fa5b512a6f Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Tue, 26 Sep 2023 10:32:10 -0400 Subject: [PATCH] windows: document the return type mismatch for CommandLineToArgv For golang/go#63236. Change-Id: Id6c458e2ee2291e28685d24e86c05702d9fd132a Cq-Include-Trybots: luci.golang.try:x_sys-gotip-windows-amd64-longtest Reviewed-on: https://go-review.googlesource.com/c/sys/+/531175 Reviewed-by: Quim Muntal Auto-Submit: Bryan Mills Reviewed-by: Than McIntosh LUCI-TryBot-Result: Go LUCI --- windows/exec_windows.go | 25 +++++++++++++++++++------ windows/syscall_windows.go | 2 +- windows/zsyscall_windows.go | 4 ++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/windows/exec_windows.go b/windows/exec_windows.go index d1baeb244..9cabbb694 100644 --- a/windows/exec_windows.go +++ b/windows/exec_windows.go @@ -153,22 +153,35 @@ func DecomposeCommandLine(commandLine string) ([]string, error) { return nil, errorspkg.New("string with NUL passed to DecomposeCommandLine") } var argc int32 - argv8192, err := CommandLineToArgv(&utf16CommandLine[0], &argc) + argv, err := commandLineToArgv(&utf16CommandLine[0], &argc) if err != nil { return nil, err } - defer LocalFree(Handle(unsafe.Pointer(argv8192))) + defer LocalFree(Handle(unsafe.Pointer(argv))) var args []string - // Note: CommandLineToArgv hard-codes an incorrect return type - // (see https://go.dev/issue/63236). - // We use an unsafe.Pointer conversion here to work around it. - for _, p := range unsafe.Slice((**uint16)(unsafe.Pointer(argv8192)), argc) { + for _, p := range unsafe.Slice(argv, argc) { args = append(args, UTF16PtrToString(p)) } return args, nil } +// CommandLineToArgv parses a Unicode command line string and sets +// argc to the number of parsed arguments. +// +// The returned memory should be freed using a single call to LocalFree. +// +// Note that although the return type of CommandLineToArgv indicates 8192 +// entries of up to 8192 characters each, the actual count of parsed arguments +// may exceed 8192, and the documentation for CommandLineToArgvW does not mention +// any bound on the lengths of the individual argument strings. +// (See https://go.dev/issue/63236.) +func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) { + argp, err := commandLineToArgv(cmd, argc) + argv = (*[8192]*[8192]uint16)(unsafe.Pointer(argp)) + return argv, err +} + func CloseOnExec(fd Handle) { SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0) } diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go index 825277bda..35cfc57ca 100644 --- a/windows/syscall_windows.go +++ b/windows/syscall_windows.go @@ -238,7 +238,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetFileAttributes(name *uint16, attrs uint32) (err error) = kernel32.SetFileAttributesW //sys GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) = kernel32.GetFileAttributesExW //sys GetCommandLine() (cmd *uint16) = kernel32.GetCommandLineW -//sys CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) [failretval==nil] = shell32.CommandLineToArgvW +//sys commandLineToArgv(cmd *uint16, argc *int32) (argv **uint16, err error) [failretval==nil] = shell32.CommandLineToArgvW //sys LocalFree(hmem Handle) (handle Handle, err error) [failretval!=0] //sys LocalAlloc(flags uint32, length uint32) (ptr uintptr, err error) //sys SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index 2e193c3a7..8b1688de4 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -3844,9 +3844,9 @@ func setupUninstallOEMInf(infFileName *uint16, flags SUOI, reserved uintptr) (er return } -func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) { +func commandLineToArgv(cmd *uint16, argc *int32) (argv **uint16, err error) { r0, _, e1 := syscall.Syscall(procCommandLineToArgvW.Addr(), 2, uintptr(unsafe.Pointer(cmd)), uintptr(unsafe.Pointer(argc)), 0) - argv = (*[8192]*[8192]uint16)(unsafe.Pointer(r0)) + argv = (**uint16)(unsafe.Pointer(r0)) if argv == nil { err = errnoErr(e1) }