go: x/sys/windows/svc: session change notification triggers "checkptr: pointer arithmetic result points to invalid allocation"
What version of Go are you using (go version)?
PS C:\Users\Lars Meyer\src\sys> go version go version go1.20.3 windows/amd64
Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (go env)?
go env Output
PS C:\Users\Lars Meyer\src\sys> go env set GO111MODULE= set GOARCH=amd64 set GOBIN= set GOCACHE=C:\Users\Lars Meyer\AppData\Local\go-build set GOENV=C:\Users\Lars Meyer\AppData\Roaming\go\env set GOEXE=.exe set GOEXPERIMENT= set GOFLAGS= set GOHOSTARCH=amd64 set GOHOSTOS=windows set GOINSECURE= set GOMODCACHE=C:\Program Files\Go\pkg\mod set GONOPROXY=code.enginsight.org set GONOSUMDB=code.enginsight.org set GOOS=windows set GOPATH=C:\Program Files\Go set GOPRIVATE=code.enginsight.org set GOPROXY=https://proxy.golang.org,direct set GOROOT=C:\Program Files\Go set GOSUMDB=sum.golang.org set GOTMPDIR= set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64 set GOVCS= set GOVERSION=go1.20.3 set GCCGO=gccgo set GOAMD64=v1 set AR=ar set CC=gcc set CXX=g++ set CGO_ENABLED=1 set GOMOD=C:\Users\Lars Meyer\src\sys\go.mod set GOWORK= set CGO_CFLAGS=-O2 -g set CGO_CPPFLAGS= set CGO_CXXFLAGS=-O2 -g set CGO_FFLAGS=-O2 -g set CGO_LDFLAGS=-O2 -g set PKG_CONFIG=pkg-config set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 '-fdebug-prefix-map=C:\Users\Lars Meyer\AppData\Local\Temp\go-build2366456271=/tmp/go-build' -gno-record-gcc-switches
What did you do?
I am running the Windows service example.
I have forked the sys repository and added svc.AcceptSessionChange to the accepted service commands. (I have also redirected stderr/stdout to a file example.log next to the executable to be able to catch the crash output.)
https://github.com/elmeyer/sys/tree/windows-svc-example-checkptr
What did you expect to see?
No errors when running the service example built with -race.
What did you see instead?
When logging out and logging back in, a session change notification is triggered. The resulting call to svc.ctlHandler receives a uintptr to svc.theService in the context parameter. Its conversion back to a *svc.service seems to be what triggers this crash.
example.log
fatal error: checkptr: pointer arithmetic result points to invalid allocationgoroutine 1 [running, locked to thread]: runtime.throw({0x58bdc4?, 0x4aa139?}) C:/Program Files/Go/src/runtime/panic.go:1047 +0x65 fp=0xc00014b5d0 sp=0xc00014b5a0 pc=0x479305 runtime.checkptrArithmetic(0x0?, {0x0, 0x0, 0x41d750?}) C:/Program Files/Go/src/runtime/checkptr.go:69 +0xaa fp=0xc00014b600 sp=0xc00014b5d0 pc=0x44ac4a golang.org/x/sys/windows/svc.ctlHandler(0xe, 0x6, 0x1614550, 0x6ca6c0) C:/Users/Lars Meyer/src/sys/windows/svc/service.go:204 +0x4c fp=0xc00014b658 sp=0xc00014b600 pc=0x54f28c runtime.call32(0x0, 0x58c580, 0xc00014b6f0, 0x0, 0x0, 0x20, 0xc00014b948) C:/Program Files/Go/src/runtime/asm_amd64.s:729 +0x4e fp=0xc00014b688 sp=0xc00014b658 pc=0x4a6cae runtime.callbackWrap(0x146fab0) C:/Program Files/Go/src/runtime/syscall_windows.go:396 +0x1ef fp=0xc00014ba68 sp=0xc00014b688 pc=0x496fef runtime.cgocallbackg1(0x496e00, 0x4513c7?, 0x0) C:/Program Files/Go/src/runtime/cgocall.go:315 +0x2c5 fp=0xc00014bb30 sp=0xc00014ba68 pc=0x4481c5 runtime.cgocallbackg(0xc000042000?, 0x300000002?, 0xc000042000?) C:/Program Files/Go/src/runtime/cgocall.go:234 +0x105 fp=0xc00014bbc0 sp=0xc00014bb30 pc=0x447e25 runtime.cgocallbackg(0x496e00, 0x146fab0, 0x0) <autogenerated>:1 +0x34 fp=0xc00014bbe8 sp=0xc00014bbc0 pc=0x4ab414 runtime.cgocallback(0x447c99, 0x4aa9a0, 0x6cb140) C:/Program Files/Go/src/runtime/asm_amd64.s:998 +0xcf fp=0xc00014bc10 sp=0xc00014bbe8 pc=0x4a876f runtime.systemstack_switch() C:/Program Files/Go/src/runtime/asm_amd64.s:463 fp=0xc00014bc18 sp=0xc00014bc10 pc=0x4a66c0 runtime.cgocall(0x4aa9a0, 0x6cb140) C:/Program Files/Go/src/runtime/cgocall.go:167 +0xb9 fp=0xc00014bc50 sp=0xc00014bc18 pc=0x447c99 syscall.SyscallN(0x7ffa72487cd0?, {0xc00014bce8?, 0x3?, 0x0?}) C:/Program Files/Go/src/runtime/syscall_windows.go:557 +0x109 fp=0xc00014bcc8 sp=0xc00014bc50 pc=0x4a5509 syscall.Syscall(0xc00013fce0?, 0xc000108120?, 0xc0001401e0?, 0xc0001401e0?, 0x54fe18?) C:/Program Files/Go/src/runtime/syscall_windows.go:495 +0x3b fp=0xc00014bd10 sp=0xc00014bcc8 pc=0x4a51db golang.org/x/sys/windows.StartServiceCtrlDispatcher(0xc00014bda8) C:/Users/Lars Meyer/src/sys/windows/zsyscall_windows.go:1331 +0xa5 fp=0xc00014bd80 sp=0xc00014bd10 pc=0x526d25 golang.org/x/sys/windows/svc.Run({0x580b70, 0x9}, {0x605b98?, 0x71d810}) C:/Users/Lars Meyer/src/sys/windows/svc/service.go:301 +0x176 fp=0xc00014bdd8 sp=0xc00014bd80 pc=0x54fe56 main.runService({0x580b70, 0x9}, 0x0) C:/Users/Lars Meyer/src/sys/windows/svc/example/service.go:90 +0x2e9 fp=0xc00014bed0 sp=0xc00014bdd8 pc=0x558169 main.main() C:/Users/Lars Meyer/src/sys/windows/svc/example/main.go:86 +0x40d fp=0xc00014bf80 sp=0xc00014bed0 pc=0x556c0d runtime.main() C:/Program Files/Go/src/runtime/proc.go:250 +0x1f7 fp=0xc00014bfe0 sp=0xc00014bf80 pc=0x47ba77 runtime.goexit() C:/Program Files/Go/src/runtime/asm_amd64.s:1598 +0x1 fp=0xc00014bfe8 sp=0xc00014bfe0 pc=0x4a89c1
goroutine 17 [select, locked to thread]: runtime.gopark(0xc0001119e8?, 0x4?, 0x0?, 0xc0?, 0xc0001117d4?) C:/Program Files/Go/src/runtime/proc.go:381 +0xd6 fp=0xc0001115e8 sp=0xc0001115c8 pc=0x47be96 runtime.selectgo(0xc0001119e8, 0xc0001117cc, 0xc000111790?, 0x1, 0x0?, 0x1) C:/Program Files/Go/src/runtime/select.go:327 +0x8be fp=0xc000111748 sp=0xc0001115e8 pc=0x48b9fe golang.org/x/sys/windows/svc.serviceMain(0x3, 0x161ace8) C:/Users/Lars Meyer/src/sys/windows/svc/service.go:253 +0x676 fp=0xc000111a38 sp=0xc000111748 pc=0x54f9d6 runtime.call16(0x0, 0x58c590, 0xc000111ac0, 0x0, 0x0, 0x10, 0xc000111d18) C:/Program Files/Go/src/runtime/asm_amd64.s:728 +0x4e fp=0xc000111a58 sp=0xc000111a38 pc=0x4a6c0e runtime.callbackWrap(0x28c5fdf0) C:/Program Files/Go/src/runtime/syscall_windows.go:396 +0x1ef fp=0xc000111e38 sp=0xc000111a58 pc=0x496fef runtime.cgocallbackg1(0x496e00, 0x0?, 0x0) C:/Program Files/Go/src/runtime/cgocall.go:315 +0x2c5 fp=0xc000111f00 sp=0xc000111e38 pc=0x4481c5 runtime.cgocallbackg(0x0?, 0x0?, 0x0?) C:/Program Files/Go/src/runtime/cgocall.go:234 +0x105 fp=0xc000111f90 sp=0xc000111f00 pc=0x447e25 runtime.cgocallbackg(0x496e00, 0x28c5fdf0, 0x0) <autogenerated>:1 +0x34 fp=0xc000111fb8 sp=0xc000111f90 pc=0x4ab414 runtime.cgocallback(0x0, 0x0, 0x0) C:/Program Files/Go/src/runtime/asm_amd64.s:998 +0xcf fp=0xc000111fe0 sp=0xc000111fb8 pc=0x4a876f runtime.goexit() C:/Program Files/Go/src/runtime/asm_amd64.s:1598 +0x1 fp=0xc000111fe8 sp=0xc000111fe0 pc=0x4a89c1
goroutine 2 [force gc (idle)]: runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?) C:/Program Files/Go/src/runtime/proc.go:381 +0xd6 fp=0xc000045fb0 sp=0xc000045f90 pc=0x47be96 runtime.goparkunlock(…) C:/Program Files/Go/src/runtime/proc.go:387 runtime.forcegchelper() C:/Program Files/Go/src/runtime/proc.go:305 +0xb2 fp=0xc000045fe0 sp=0xc000045fb0 pc=0x47bcb2 runtime.goexit() C:/Program Files/Go/src/runtime/asm_amd64.s:1598 +0x1 fp=0xc000045fe8 sp=0xc000045fe0 pc=0x4a89c1 created by runtime.init.6 C:/Program Files/Go/src/runtime/proc.go:293 +0x25
goroutine 3 [GC sweep wait]: runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?) C:/Program Files/Go/src/runtime/proc.go:381 +0xd6 fp=0xc000047f80 sp=0xc000047f60 pc=0x47be96 runtime.goparkunlock(…) C:/Program Files/Go/src/runtime/proc.go:387 runtime.bgsweep(0x0?) C:/Program Files/Go/src/runtime/mgcsweep.go:278 +0x8e fp=0xc000047fc8 sp=0xc000047f80 pc=0x466a4e runtime.gcenable.func1() C:/Program Files/Go/src/runtime/mgc.go:178 +0x26 fp=0xc000047fe0 sp=0xc000047fc8 pc=0x45bec6 runtime.goexit() C:/Program Files/Go/src/runtime/asm_amd64.s:1598 +0x1 fp=0xc000047fe8 sp=0xc000047fe0 pc=0x4a89c1 created by runtime.gcenable C:/Program Files/Go/src/runtime/mgc.go:178 +0x6b
goroutine 4 [GC scavenge wait]: runtime.gopark(0xc00001a070?, 0x6044b8?, 0x1?, 0x0?, 0x0?) C:/Program Files/Go/src/runtime/proc.go:381 +0xd6 fp=0xc000057f70 sp=0xc000057f50 pc=0x47be96 runtime.goparkunlock(…) C:/Program Files/Go/src/runtime/proc.go:387 runtime.(*scavengerState).park(0x6ca800) C:/Program Files/Go/src/runtime/mgcscavenge.go:400 +0x53 fp=0xc000057fa0 sp=0xc000057f70 pc=0x4649b3 runtime.bgscavenge(0x0?) C:/Program Files/Go/src/runtime/mgcscavenge.go:628 +0x45 fp=0xc000057fc8 sp=0xc000057fa0 pc=0x464f65 runtime.gcenable.func2() C:/Program Files/Go/src/runtime/mgc.go:179 +0x26 fp=0xc000057fe0 sp=0xc000057fc8 pc=0x45be66 runtime.goexit() C:/Program Files/Go/src/runtime/asm_amd64.s:1598 +0x1 fp=0xc000057fe8 sp=0xc000057fe0 pc=0x4a89c1 created by runtime.gcenable C:/Program Files/Go/src/runtime/mgc.go:179 +0xaa
goroutine 18 [finalizer wait]: runtime.gopark(0x6cae80?, 0x6ab7d0?, 0x0?, 0x0?, 0xc000049f70?) C:/Program Files/Go/src/runtime/proc.go:381 +0xd6 fp=0xc000049e28 sp=0xc000049e08 pc=0x47be96 runtime.runfinq() C:/Program Files/Go/src/runtime/mfinal.go:193 +0x147 fp=0xc000049fe0 sp=0xc000049e28 pc=0x45af27 runtime.goexit() C:/Program Files/Go/src/runtime/asm_amd64.s:1598 +0x1 fp=0xc000049fe8 sp=0xc000049fe0 pc=0x4a89c1 created by runtime.createfing C:/Program Files/Go/src/runtime/mfinal.go:163 +0x45
goroutine 5 [select]: runtime.gopark(0xc00004bf38?, 0x2?, 0x40?, 0x30?, 0xc00004be44?) C:/Program Files/Go/src/runtime/proc.go:381 +0xd6 fp=0xc00004bca0 sp=0xc00004bc80 pc=0x47be96 runtime.selectgo(0xc00004bf38, 0xc00004be40, 0x57fefe?, 0x0, 0x2?, 0x1) C:/Program Files/Go/src/runtime/select.go:327 +0x8be fp=0xc00004be00 sp=0xc00004bca0 pc=0x48b9fe main.(*myservice).Execute(0xc00004c000?, {0xc00001c030, 0x3, 0x3}, 0xc00005c000, 0x0?) C:/Users/Lars Meyer/src/sys/windows/svc/example/service.go:33 +0x193 fp=0xc00004bf68 sp=0xc00004be00 pc=0x557a53 golang.org/x/sys/windows/svc.serviceMain.func2() C:/Users/Lars Meyer/src/sys/windows/svc/service.go:241 +0x95 fp=0xc00004bfe0 sp=0xc00004bf68 pc=0x54fc95 runtime.goexit() C:/Program Files/Go/src/runtime/asm_amd64.s:1598 +0x1 fp=0xc00004bfe8 sp=0xc00004bfe0 pc=0x4a89c1 created by golang.org/x/sys/windows/svc.serviceMain C:/Users/Lars Meyer/src/sys/windows/svc/service.go:240 +0x4d6
I fundamentally don’t understand why a pointer to svc.theService is being passed to windows.RegisterServiceCtrlHandlerEx. The comment for svc.theService explicitly states:
var theService service // This is, unfortunately, a global, which means only one service per process.
while the documentation for RegisterServiceCtrlHandlerExW states:
[in, optional] lpContextAny user-defined data. This parameter, which is passed to the handler function, can help identify the service when multiple services share a process.
(emphasis mine)
The crash naturally disappears when this parameter is replaced with 0 and svc.ctlHandler is modified to assume that all calls to it refer to svc.theService. However, the root cause of this checkptr failure remains unclear.
cc @ericrange who helped reproduce this problem.
About this issue
- Original URL
- State: open
- Created a year ago
- Comments: 19 (14 by maintainers)
Commits related to this issue
- windows: use cgo.Handle for service object This fixes go vet complaints and checkptr crashes when the handler receives an event, e.g. a session change notification. Fixes golang/go#59687 — committed to elmeyer/sys by elmeyer a year ago
- windows: use cgo.Handle for service object This fixes go vet complaints and checkptr crashes when the handler receives an event, e.g. a session change notification. Fixes golang/go#59687 — committed to elmeyer/sys by elmeyer a year ago
Oh dear, that’s embarrassing. Thanks for catching that!
@qmuntal Thanks for your response. This does seem like the best way to go short of removing the
contextparameter entirely. I will prepare a CL.