Go version
go version go1.24.4 darwin/arm64
Output of go env
in your module/workspace:
AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/fxrlv/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/fxrlv/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/mg/1l0261p97kzflrmx68mm4z7c0000gn/T/go-build2006285917=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/tmp/checkptr/go.mod'
GOMODCACHE='/Users/fxrlv/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/fxrlv/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.24.4/libexec'
GOSUMDB='sum.golang.org'
GOTELEMETRY='on'
GOTELEMETRYDIR='/Users/fxrlv/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.24.4/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.24.4'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
There is an inconsistency in how the checkptr
runtime check validates pointer arithmetic.
The issue is that checkptr
validates manual pointer arithmetic using uintptr
conversions but does not validate the unsafe.Add
function, which can perform equivalent potentially unsafe operations.
package main
import (
"fmt"
"unsafe"
)
func main() {
data := make([]byte, 10)
// This triggers checkptr validation and can panic with:
// "fatal error: checkptr: pointer arithmetic result points to invalid allocation"
manualArithmetic := func(offset int) *byte {
return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(unsafe.SliceData(data))) + uintptr(offset)))
}
// This does NOT trigger checkptr validation, even with equivalent dangerous operations
unsafeAdd := func(offset int) *byte {
return (*byte)(unsafe.Add(unsafe.Pointer(unsafe.SliceData(data)), offset))
}
// Both can be equally dangerous when offset >= len(data) or offset < 0
a := unsafeAdd(10) // No checkptr validation
b := manualArithmetic(10) // May trigger checkptr panic
fmt.Println(a)
fmt.Println(b)
}
$ go run -race main.go
fatal error: checkptr: pointer arithmetic result points to invalid allocation
goroutine 1 gp=0xc0000021c0 m=0 mp=0x100c056e0 [running]:
runtime.throw({0x100b0ac29?, 0x100b3e520?})
/opt/homebrew/Cellar/go/1.24.4/libexec/src/runtime/panic.go:1101 +0x38 fp=0xc000187ea0 sp=0xc000187e70 pc=0x100ab5b68
runtime.checkptrArithmetic(0x100a56ec0?, {0xc000187f30, 0x1, 0x0?})
/opt/homebrew/Cellar/go/1.24.4/libexec/src/runtime/checkptr.go:69 +0xa8 fp=0xc000187ed0 sp=0xc000187ea0 pc=0x100a58238
main.main.func1(...)
/tmp/checkptr/main.go:14
main.main()
/tmp/checkptr/main.go:24 +0x70 fp=0xc000187f40 sp=0xc000187ed0 pc=0x100b01550
runtime.main()
/opt/homebrew/Cellar/go/1.24.4/libexec/src/runtime/proc.go:283 +0x284 fp=0xc000187fd0 sp=0xc000187f40 pc=0x100a84ef4
runtime.goexit({})
/opt/homebrew/Cellar/go/1.24.4/libexec/src/runtime/asm_arm64.s:1223 +0x4 fp=0xc000187fd0 sp=0xc000187fd0 pc=0x100abc744
What did you see happen?
unsafe.Add
calls: No validation, no panics- Manual pointer arithmetic:
checkptr
validates and panics with "pointer arithmetic result points to invalid allocation"
What did you expect to see?
Both manual pointer arithmetic and unsafe.Add
should receive the same level of validation from checkptr
. If manual uintptr arithmetic is checked for validity, then unsafe.Add
calls should also be validated.
Comment From: gabyhelp
Related Issues
- runtime: checkptr incorrectly -race flagging when using \&^ arithmetic #40917 (closed)
- x/sys/unix: MmapPtr upsets checkptr #70721 (closed)
- runtime/checkptr: fatal error: checkptr: pointer arithmetic result points to invalid allocation #68415 (closed)
- runtime/checkptr: erroneous "pointer arithmetic result points to invalid allocation" #48501 (closed)
- proposal: cmd/go: consider adding unsafeptr to go vet during test list #57794 (closed)
- cmd/compile: -d=checkptr should not reject unaligned pointers to non-pointer data #37298 (closed)
- cmd/compile: fatal error: checkptr: pointer arithmetic computed bad pointer value #64467
- go build use -asan it panic. #69225 (closed)
- cmd/compile,runtime: checkptr false negatives for tiny-alloced objects #38872 (closed)
- cmd/compile: -d=checkptr should disable inlining reflect.Value.{Pointer,UnsafeAddr} even when package reflect is compiled without it #35073 (closed)
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: zigo101
Isn't it just the purpose of unsafe.Add
? (to avoid checkptr)
Comment From: zigo101
Aha, it looks the effect of -race
is different from -gcflags=all=-d=checkptr
. The latter option doesn't report errors for both ways.
Sorry, my last comment is irrelevant.
Comment From: randall77
I agree it looks like checkptr instrumentation is not added for unsafe.Add
when using the compiler option -d=checkptr
.
I believe we should fix that.
Comment From: thepudds
FYI, I've started to take a look at this and will likely send a CL.
Comment From: cuonglm
@thepudds I can send a fix if you haven't started yet.
Comment From: thepudds
Hi @cuonglm, I already have something that I think is close to working. I'll add you to the CL in case you have feedback. (And if I get stuck or seem like I won't have time, I'll circle back here to let you know). Thanks!
Comment From: cuonglm
Hi @cuonglm, I already have something that I think is close to working. I'll add you to the CL in case you have feedback. (And if I get stuck or seem like I won't have time, I'll circle back here to let you know). Thanks!
Yeah, I mean I have a working one already, but feel free to work on it if you want.
Comment From: gopherbot
Change https://go.dev/cl/690075 mentions this issue: cmd/compile/internal/walk: instrument unsafe.Add for checkptr runtime pointer validation
Comment From: thepudds
(Last week, @cuonglm and I chatted briefly elsewhere, and the conclusion was I am going to try move my version forward).
Comment From: thepudds
Hi @fxrlv, if you are interested, you could try out https://go.dev/cl/690075, such as via the official gotip
utility:
$ go install golang.org/dl/gotip@latest # install gotip
$ gotip download 690075 # download and build CL 690075
$ gotip run -race main.go # use gotip as your normal 'go' command
Running on your original example, I now see:
fatal error: checkptr: pointer arithmetic result points to invalid allocation
goroutine 1 gp=0xc000002380 m=0 mp=0x5f00a0 [running]:
runtime.throw({0x51a629?, 0xa?})
/home/.../sdk/gotip/src/runtime/panic.go:1094 +0x48 fp=0xc00007bea8 sp=0xc00007be78 pc=0x4a7928
runtime.checkptrArithmetic(0x4766f1?, {0xc00007bf38, 0x1, 0x0?})
/home/.../sdk/gotip/src/runtime/checkptr.go:69 +0x9c fp=0xc00007bed8 sp=0xc00007bea8 pc=0x446a7c
main.main.func2(...)
/home/.../work/scratch/checkptr-unsafe-add-74431/main.go:19
main.main()
/home/.../work/scratch/checkptr-unsafe-add-74431/main.go:23 +0x73 fp=0xc00007bf50 sp=0xc00007bed8 pc=0x4eaa33
runtime.main()
[...]
The lines on main.go:19 and main.go:23 cited in the fatal error there correspond to the unsafe.Add
in your original example. (In go1.24, it makes it to line main.go:24, which is past the unsafe.Add
in your original example).
Comment From: gopherbot
Change https://go.dev/cl/692015 mentions this issue: cmd/compile, runtime: add checkptr instrumentation for unsafe.Add