go: syscall & x/sys/windows: buffer overflow in GetQueuedCompletionStatus

The third argument to GetQueuedCompletionStatus is a pointer to a uintptr, not a uint32. Users of this functions have therefore been corrupting their memory every time they used it. Either that memory corruption was silent (dangerous), or their programs didn’t work so they chose a different API to use.

I intend to submit CLs fixing the function signature. This could perturb compatibility. But either nobody used these functions because they were broken or there’s a lingering security issue, so I think it may be justified.

CC @bradfitz @alexbrainman @FiloSottile

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 30 (21 by maintainers)

Commits related to this issue

Most upvoted comments

Those two call sites don’t include the known uses in x/exp and x/benchmarks, so there’s gotta be something missing from that search.

You are mistaken. Correct observation, but incorrect conclusion. x/exp and x/benchmarks – and virtually everything else – uses syscall, not x/sys/windows. The search is for uses of x/sys/windows, because that is the relevant package for that part of the discussion.

Again, in case there’s confusion about what I’m suggesting:

  1. We don’t break the signature of syscall, but instead do https://golang.org/cl/296149 and encourage people to switch to x/sys/windows.
  2. We make x/sys/windows use the right signature, via https://golang.org/cl/295174 .

We are on the same page about (1). Hence I discussed (2) here. Please re-read https://github.com/golang/go/issues/44538#issuecomment-785427786 in light of that.

Reproduces easily:

package main

import "golang.org/x/sys/windows"

func main() {
	k32 := windows.NewLazySystemDLL("kernel32.dll")
	port, _, _ := k32.NewProc("CreateIoCompletionPort").Call(^uintptr(0), 0, 0, 0)
	k32.NewProc("PostQueuedCompletionStatus").Call(port, 1, uintptr(77)<<32, 0)

	var things struct {
		o            *windows.Overlapped
		bytes        uint32
		key          uint32
		MYSTERYVALUE uint32
	}
	things.MYSTERYVALUE = 99
	println(things.MYSTERYVALUE)
	windows.GetQueuedCompletionStatus(windows.Handle(port), &things.bytes, &things.key, &things.o, windows.INFINITE)
	println(things.MYSTERYVALUE)
}

Output:

99
77