Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: go1.22.0 test with -race will SIGSEGV or SIGBUS or Bad Pointer #65783

Closed
timmy21 opened this issue Feb 19, 2024 · 17 comments
Closed

runtime: go1.22.0 test with -race will SIGSEGV or SIGBUS or Bad Pointer #65783

timmy21 opened this issue Feb 19, 2024 · 17 comments
Assignees
Labels
compiler/runtime Issues related to the Go compiler and/or runtime.

Comments

@timmy21
Copy link

timmy21 commented Feb 19, 2024

Go version

go version go1.22.0 darwin/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/Users/timmy/Library/Caches/go-build'
GOENV='/Users/timmy/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/timmy/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/timmy/go'
GOPRIVATE=''
GOPROXY='https://goproxy.cn,direct'
GOROOT='/Users/timmy/sdk/go1.22.0'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/Users/timmy/sdk/go1.22.0/pkg/tool/darwin_amd64'
GOVCS=''
GOVERSION='go1.22.0'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/Users/timmy/code/test/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/pv/rs024ry56j3cqlcqgjnm48l00000gn/T/go-build2295311270=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

go1.22.0 test -timeout 30s -run ^TestPlanVisitor$ github.com/timmy21/test/planx -v -count 1 -race

What did you see happen?

=== RUN TestPlanVisitor
unexpected fault address 0xb01dfacedebac1e
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x1 addr=0xb01dfacedebac1e pc=0xfa7e9de]

goroutine 9 gp=0xc000186e00 m=0 mp=0x11c19320 [running]:
runtime.throw({0x108da285?, 0x20?})
/Users/timmy/sdk/go1.22.0/src/runtime/panic.go:1023 +0x5c fp=0xc0000ebac8 sp=0xc0000eba98 pc=0xf9647fc
runtime.sigpanic()
/Users/timmy/sdk/go1.22.0/src/runtime/signal_unix.go:895 +0x285 fp=0xc0000ebb28 sp=0xc0000ebac8 pc=0xf97ec45
fmt.(*pp).printArg(0xc000697860, {0xcccccccccccccccc, 0x108d70ed}, 0x76)
/Users/timmy/sdk/go1.22.0/src/fmt/print.go:707 +0x11e fp=0xc0000ebbb8 sp=0xc0000ebb28 pc=0xfa7e9de
fmt.(*pp).doPrintf(0xc000697860, {0x108fa625, 0x1e}, {0xc0000ebd90, 0x1, 0x1})
/Users/timmy/sdk/go1.22.0/src/fmt/print.go:1075 +0x593 fp=0xc0000ebcf0 sp=0xc0000ebbb8 pc=0xfa83bb3
fmt.Sprintf({0x108fa625, 0x1e}, {0xc0000ebd90, 0x1, 0x1})
/Users/timmy/sdk/go1.22.0/src/fmt/print.go:239 +0x5d fp=0xc0000ebd48 sp=0xc0000ebcf0 pc=0xfa79e1d
github.com/stretchr/testify/assert.NoError({0x10e94ae0, 0xc0005dc4e0}, {0xf9a5301, 0x108d70ed}, {0x0, 0x0, 0x0})
/Users/timmy/go/pkg/mod/github.com/stretchr/testify@v1.8.4/assert/assertions.go:1495 +0x11a fp=0xc0000ebdb0 sp=0xc0000ebd48 pc=0x106eaf9a
github.com/stretchr/testify/require.NoError({0x10e983f0, 0xc0005dc4e0}, {0xf9a5301, 0x108d70ed}, {0x0, 0x0, 0x0})
/Users/timmy/go/pkg/mod/github.com/stretchr/testify@v1.8.4/require/require.go:1357 +0xc5 fp=0xc0000ebdf8 sp=0xc0000ebdb0 pc=0x106ebc45
github.com/timmy21/test/planx/planx_test.TestPlanVisitor(0xc0005dc4e0)
/Users/timmy/code/test/planx/planx_test.go:60 +0xade fp=0xc0000ebee8 sp=0xc0000ebdf8 pc=0x108d711e
testing.tRunner(0xc0005dc4e0, 0x10e8e2b8)
/Users/timmy/sdk/go1.22.0/src/testing/testing.go:1689 +0x21f fp=0xc0000ebfb0 sp=0xc0000ebee8 pc=0xfacb35f
testing.(*T).Run.gowrap1()
/Users/timmy/sdk/go1.22.0/src/testing/testing.go:1742 +0x45 fp=0xc0000ebfe0 sp=0xc0000ebfb0 pc=0xfacd0a5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000ebfe8 sp=0xc0000ebfe0 pc=0xf9a1ee1
created by testing.(*T).Run in goroutine 1
/Users/timmy/sdk/go1.22.0/src/testing/testing.go:1742 +0x826

goroutine 1 gp=0xc0000061c0 m=nil [chan receive]:
runtime.gopark(0x18?, 0x11c15c80?, 0x18?, 0x0?, 0x12c8d1a8?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0005c7710 sp=0xc0005c76f0 pc=0xf9676ce
runtime.chanrecv(0xc0006920e0, 0xc0005c77f7, 0x1)
/Users/timmy/sdk/go1.22.0/src/runtime/chan.go:583 +0x36d fp=0xc0005c7788 sp=0xc0005c7710 pc=0xf93166d
runtime.chanrecv1(0x11c15c80?, 0x10d2d4a0?)
/Users/timmy/sdk/go1.22.0/src/runtime/chan.go:442 +0x12 fp=0xc0005c77b0 sp=0xc0005c7788 pc=0xf9312d2
testing.(*T).Run(0xc0005dc340, {0x108f47d3, 0x19}, 0x10e8e2b8)
/Users/timmy/sdk/go1.22.0/src/testing/testing.go:1750 +0x851 fp=0xc0005c78d0 sp=0xc0005c77b0 pc=0xfacce11
testing.runTests.func1(0xc0005dc340)
/Users/timmy/sdk/go1.22.0/src/testing/testing.go:2161 +0x86 fp=0xc0005c7920 sp=0xc0005c78d0 pc=0xfad0fa6
testing.tRunner(0xc0005dc340, 0xc0005c7b10)
/Users/timmy/sdk/go1.22.0/src/testing/testing.go:1689 +0x21f fp=0xc0005c79e8 sp=0xc0005c7920 pc=0xfacb35f
testing.runTests(0xc0000125b8, {0x11bcd900, 0x4, 0x4}, {0xc0005c7bb8?, 0xc0005c7c00?, 0x11c16fa0?})
/Users/timmy/sdk/go1.22.0/src/testing/testing.go:2159 +0x8bf fp=0xc0005c7b40 sp=0xc0005c79e8 pc=0xfad0dff
testing.(*M).Run(0xc0005de1e0)
/Users/timmy/sdk/go1.22.0/src/testing/testing.go:2027 +0xf18 fp=0xc0005c7ec8 sp=0xc0005c7b40 pc=0xface3d8
main.main()
_testmain.go:55 +0x2be fp=0xc0005c7f50 sp=0xc0005c7ec8 pc=0x108d89fe
runtime.main()
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:271 +0x29d fp=0xc0005c7fe0 sp=0xc0005c7f50 pc=0xf96725d
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0005c7fe8 sp=0xc0005c7fe0 pc=0xf9a1ee1

goroutine 2 gp=0xc000006c40 m=nil [force gc (idle)]:
runtime.gopark(0x11524110?, 0x11c19320?, 0x0?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0000727a8 sp=0xc000072788 pc=0xf9676ce
runtime.goparkunlock(...)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:408
runtime.forcegchelper()
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:326 +0xb3 fp=0xc0000727e0 sp=0xc0000727a8 pc=0xf967533
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000727e8 sp=0xc0000727e0 pc=0xf9a1ee1
created by runtime.init.6 in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:314 +0x1a

goroutine 3 gp=0xc000007500 m=nil [GC sweep wait]:
runtime.gopark(0x1?, 0x0?, 0x0?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc000088f80 sp=0xc000088f60 pc=0xf9676ce
runtime.goparkunlock(...)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:408
runtime.bgsweep(0xc00007e000)
/Users/timmy/sdk/go1.22.0/src/runtime/mgcsweep.go:318 +0xdf fp=0xc000088fc8 sp=0xc000088f80 pc=0xf95199f
runtime.gcenable.gowrap1()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:203 +0x25 fp=0xc000088fe0 sp=0xc000088fc8 pc=0xf9462c5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc000088fe8 sp=0xc000088fe0 pc=0xf9a1ee1
created by runtime.gcenable in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:203 +0x66

goroutine 4 gp=0xc0000076c0 m=nil [GC scavenge wait]:
runtime.gopark(0x10000?, 0x10c9c4e0?, 0x0?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc00008ef78 sp=0xc00008ef58 pc=0xf9676ce
runtime.goparkunlock(...)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:408
runtime.(*scavengerState).park(0x11c17140)
/Users/timmy/sdk/go1.22.0/src/runtime/mgcscavenge.go:425 +0x49 fp=0xc00008efa8 sp=0xc00008ef78 pc=0xf94f369
runtime.bgscavenge(0xc00007e000)
/Users/timmy/sdk/go1.22.0/src/runtime/mgcscavenge.go:658 +0x59 fp=0xc00008efc8 sp=0xc00008efa8 pc=0xf94f8f9
runtime.gcenable.gowrap2()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:204 +0x25 fp=0xc00008efe0 sp=0xc00008efc8 pc=0xf946265
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc00008efe8 sp=0xc00008efe0 pc=0xf9a1ee1
created by runtime.gcenable in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:204 +0xa5

goroutine 5 gp=0xc000007880 m=nil [finalizer wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc000089e20 sp=0xc000089e00 pc=0xf9676ce
runtime.runfinq()
/Users/timmy/sdk/go1.22.0/src/runtime/mfinal.go:194 +0x145 fp=0xc000089fe0 sp=0xc000089e20 pc=0xf945305
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc000089fe8 sp=0xc000089fe0 pc=0xf9a1ee1
created by runtime.createfing in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mfinal.go:164 +0x3d

goroutine 6 gp=0xc000332fc0 m=nil [GC worker (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0000b7f50 sp=0xc0000b7f30 pc=0xf9676ce
runtime.gcBgMarkWorker()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1310 +0xe5 fp=0xc0000b7fe0 sp=0xc0000b7f50 pc=0xf9483a5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000b7fe8 sp=0xc0000b7fe0 pc=0xf9a1ee1
created by runtime.gcBgMarkStartWorkers in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1234 +0x1c

goroutine 18 gp=0xc000186380 m=nil [GC worker (idle)]:
runtime.gopark(0xf9396e5?, 0x48?, 0xe0?, 0x74?, 0x1?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0000b6f50 sp=0xc0000b6f30 pc=0xf9676ce
runtime.gcBgMarkWorker()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1310 +0xe5 fp=0xc0000b6fe0 sp=0xc0000b6f50 pc=0xf9483a5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000b6fe8 sp=0xc0000b6fe0 pc=0xf9a1ee1
created by runtime.gcBgMarkStartWorkers in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1234 +0x1c

goroutine 7 gp=0xc000333180 m=nil [GC worker (idle)]:
runtime.gopark(0x7e557dfca0ae?, 0x0?, 0x0?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0000b5f50 sp=0xc0000b5f30 pc=0xf9676ce
runtime.gcBgMarkWorker()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1310 +0xe5 fp=0xc0000b5fe0 sp=0xc0000b5f50 pc=0xf9483a5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000b5fe8 sp=0xc0000b5fe0 pc=0xf9a1ee1
created by runtime.gcBgMarkStartWorkers in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1234 +0x1c

goroutine 19 gp=0xc000186540 m=nil [GC worker (idle)]:
runtime.gopark(0x7e557df801e9?, 0x10c9c4e0?, 0x1?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0000b4f50 sp=0xc0000b4f30 pc=0xf9676ce
runtime.gcBgMarkWorker()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1310 +0xe5 fp=0xc0000b4fe0 sp=0xc0000b4f50 pc=0xf9483a5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000b4fe8 sp=0xc0000b4fe0 pc=0xf9a1ee1
created by runtime.gcBgMarkStartWorkers in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1234 +0x1c

goroutine 20 gp=0xc000186700 m=nil [GC worker (idle)]:
runtime.gopark(0x7e557df8f189?, 0x3?, 0xe0?, 0xb0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0000b3f50 sp=0xc0000b3f30 pc=0xf9676ce
runtime.gcBgMarkWorker()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1310 +0xe5 fp=0xc0000b3fe0 sp=0xc0000b3f50 pc=0xf9483a5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000b3fe8 sp=0xc0000b3fe0 pc=0xf9a1ee1
created by runtime.gcBgMarkStartWorkers in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1234 +0x1c

goroutine 8 gp=0xc000333340 m=nil [GC worker (idle)]:
runtime.gopark(0x7e557df8f478?, 0x0?, 0x0?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0000b2f50 sp=0xc0000b2f30 pc=0xf9676ce
runtime.gcBgMarkWorker()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1310 +0xe5 fp=0xc0000b2fe0 sp=0xc0000b2f50 pc=0xf9483a5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000b2fe8 sp=0xc0000b2fe0 pc=0xf9a1ee1
created by runtime.gcBgMarkStartWorkers in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1234 +0x1c

goroutine 21 gp=0xc0001868c0 m=nil [GC worker (idle)]:
runtime.gopark(0x7e557e083f7b?, 0x0?, 0x0?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0000b1f50 sp=0xc0000b1f30 pc=0xf9676ce
runtime.gcBgMarkWorker()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1310 +0xe5 fp=0xc0000b1fe0 sp=0xc0000b1f50 pc=0xf9483a5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000b1fe8 sp=0xc0000b1fe0 pc=0xf9a1ee1
created by runtime.gcBgMarkStartWorkers in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1234 +0x1c

goroutine 34 gp=0xc0000a4380 m=nil [GC worker (idle)]:
runtime.gopark(0x7e557df97755?, 0x0?, 0x0?, 0x0?, 0x0?)
/Users/timmy/sdk/go1.22.0/src/runtime/proc.go:402 +0xce fp=0xc0000b0f50 sp=0xc0000b0f30 pc=0xf9676ce
runtime.gcBgMarkWorker()
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1310 +0xe5 fp=0xc0000b0fe0 sp=0xc0000b0f50 pc=0xf9483a5
runtime.goexit({})
/Users/timmy/sdk/go1.22.0/src/runtime/asm_amd64.s:1695 +0x1 fp=0xc0000b0fe8 sp=0xc0000b0fe0 pc=0xf9a1ee1
created by runtime.gcBgMarkStartWorkers in goroutine 1
/Users/timmy/sdk/go1.22.0/src/runtime/mgc.go:1234 +0x1c
FAIL github.com/timmy21/test/planx 1.113s
FAIL

What did you expect to see?

test passed used bellow command:
go1.21.7 test -timeout 30s -run ^TestPlanVisitor$ github.com/timmy21/test/planx -v -count 1 -race
go1.22.0 test -timeout 30s -run ^TestPlanVisitor$ github.com/timmy21/test/planx -v -count 1

@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Feb 19, 2024
@timmy21 timmy21 changed the title runtime: go1.22.0 test with -race will panic runtime: go1.22.0 test with -race will SIGSEGV Feb 19, 2024
@timmy21 timmy21 changed the title runtime: go1.22.0 test with -race will SIGSEGV runtime: go1.22.0 test with -race will SIGSEGV or SIGBUS or Bad Pointer Feb 19, 2024
@randall77
Copy link
Contributor

This looks like an argument slot getting clobbered.
The argument to fmt.(*pp).printArg is clearly a bad interface value, {0xcccccccccccccccc, 0x108d70ed}. The first word looks corrupted. That is the proximal cause of the crash.
A bit higher on the stack, this interface value appears differently, but still corrupted, as the arg to require.NoError and assert.NoError, {0xf9a5301, 0x108d70ed}. The first word should never be mis-aligned like that. Probably the second word is misaligned and is junk also, but hard to be sure.

No obvious indication of what went wrong. The fact that the interface got clobbered twice is interesting. I suspect some sort of general heap corruption.

Is there any way you can give us a means to reproduce this ourselves? It is going to be hard to track down otherwise.

You might audit your use of the unsafe package, if any. Misuse of unsafe can cause behaviors like this.

@timmy21
Copy link
Author

timmy21 commented Feb 19, 2024

@randall77 cannot reproduce if i cherry pick partial code

i have a package "planx",and testcase in planx_test.go file

package planx_test

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	...
)

func TestPlanVisitor(t *testing.T) {
	...
}

now i create a new package "plan2", and copy planx_test.go to plan2/plan2_test.go, then change code line "package planx_test" to "package plan2_test".

package plan2_test

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	...
)

func TestPlanVisitor(t *testing.T) {
	...
}

run command: go1.22.0 test -timeout 30s -run ^TestPlanVisitor$ github.com/timmy21/test/plan2 -v -count 1 -race

test passed!!! what happen?

@randall77
Copy link
Contributor

I don't know. Without something I can run, it will be hard for me to figure it out.

@timmy21
Copy link
Author

timmy21 commented Feb 19, 2024

@randall77 i maybe reproduce it

// plan3.go

package plan3

import (
	"context"
	"errors"
)

type Field struct {
	Name string
}

type Plan interface {
	plan()
}

type basePlan struct{}

func (basePlan) plan() {}

type PlanA struct {
	basePlan
}

type PlanB struct {
	basePlan
}

type PlanC struct {
	basePlan
}

type PlanD struct {
	basePlan
}

type PlanE struct {
	basePlan
}

type PlanF struct {
	basePlan
}
type PlanG struct {
	basePlan
}

type PlanH struct {
	basePlan
}

type PlanI struct {
	basePlan
}
type PlanJ struct {
	basePlan
}

type PlanK struct {
	basePlan
}

type PlanVisitor[T any] interface {
	VisitA(context.Context, *PlanA) (T, error)
	VisitB(context.Context, *PlanB) (T, error)
	VisitC(context.Context, *PlanC) (T, error)
	VisitD(context.Context, *PlanD) (T, error)
	VisitE(context.Context, *PlanE) (T, error)
	VisitF(context.Context, *PlanF) (T, error)
	VisitG(context.Context, *PlanG) (T, error)
	VisitH(context.Context, *PlanH) (T, error)
	VisitI(context.Context, *PlanI) (T, error)
	VisitJ(context.Context, *PlanJ) (T, error)
	VisitK(context.Context, *PlanK) (T, error)
}

func VisitPlan[T any](ctx context.Context, plan Plan, visitor PlanVisitor[T]) (T, error) {
	switch plan := plan.(type) {
	case *PlanA:
		return visitor.VisitA(ctx, plan)
	case *PlanB:
		return visitor.VisitB(ctx, plan)
	case *PlanC:
		return visitor.VisitC(ctx, plan)
	case *PlanD:
		return visitor.VisitD(ctx, plan)
	case *PlanE:
		return visitor.VisitE(ctx, plan)
	case *PlanF:
		return visitor.VisitF(ctx, plan)
	case *PlanG:
		return visitor.VisitG(ctx, plan)
	case *PlanH:
		return visitor.VisitH(ctx, plan)
	case *PlanI:
		return visitor.VisitI(ctx, plan)
	case *PlanJ:
		return visitor.VisitJ(ctx, plan)
	case *PlanK:
		return visitor.VisitK(ctx, plan)
	default:
		panic("unreachable")
	}
}

func Visit1(plan Plan) ([]Field, error) {
	return VisitPlan[[]Field](context.Background(), plan, FakeVisitor1{})
}

type FakeVisitor1 struct{}

func (FakeVisitor1) VisitA(context.Context, *PlanA) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitB(context.Context, *PlanB) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitC(context.Context, *PlanC) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitD(context.Context, *PlanD) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitE(context.Context, *PlanE) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitF(context.Context, *PlanF) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitG(context.Context, *PlanG) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitH(context.Context, *PlanH) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitI(context.Context, *PlanI) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitJ(context.Context, *PlanJ) ([]Field, error) {
	return nil, errors.New("fake")
}

func (FakeVisitor1) VisitK(context.Context, *PlanK) ([]Field, error) {
	return nil, errors.New("fake")
}

// plan3_test.go

package plan3_test

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/timmy21/test/plan3"
)

func TestPlanVisitor(t *testing.T) {
	plan := &plan3.PlanD{}
	fields, err := plan3.VisitPlan[[]plan3.Field](context.Background(), plan, plan3.FakeVisitor1{})
	require.Error(t, err)
	assert.Equal(t, []plan3.Field(nil), fields)
}

command: go1.22.0 test -timeout 30s -run ^TestPlanVisitor$ github.com/timmy21/test/plan3 -v -count 1 -race

A SIGBUS error was generated instead of an SIGSEGV error

test passed if remove function Visit1

func Visit1(plan Plan) ([]TableField, error) {
	return VisitPlan[[]TableField](context.Background(), plan, FakeVisitor1{})
}

test passed if i change VisitPlan[[]Field] to VisitPlan[*Field]。there is some difference bettween []Field and *Field for go1.22.0 generics?

@randall77
Copy link
Contributor

Ok, I can reproduce. Seems to be ok on darwin/arm64, but not on darwin/amd64.
It is strange that the faulting pc is mid-instruction in the assembly dump. Makes me think we're corrupting pcs somehow.

@randall77 randall77 self-assigned this Feb 19, 2024
@randall77
Copy link
Contributor

Ok, this seems sort of a strange situation. The compiler compiles the function module/plan3.VisitPlan[go.shape.[]module/plan3.Field] twice, once when compiling package plan3 and once when compiling package plan3_test (the generic function is instantiated identically in both places).
The compiled code is not identical. But both compiled code chunks reference a jump table data structure stored in RO memory. That jump table is different for the two bodies, but they both have the same name. The linker ends up picking the wrong jump table for the version of the function it keeps, leading to all kinds of bad behavior.

I don't understand yet how the code compiles differently in the two cases. That seems like the underlying problem to fix. Although, we could paper over the problem by coming up with a more unique naming scheme for generated jump tables.

@randall77
Copy link
Contributor

Workaround in case you care: build with -gcflags=-N. That turns off the jump table optimization which will avoid this problem.

@timmy21
Copy link
Author

timmy21 commented Feb 20, 2024

Ok, I may continue to use go1.21 until the issues are fixed

by the way, test passed if remove -race. this is why?

@randall77
Copy link
Contributor

by the way, test passed if remove -race. this is why?

I don't yet understand what -race has to do with it. It may be coincidental, it may be crucial.

@randall77
Copy link
Contributor

Strangely, bisect points to https://go-review.googlesource.com/c/go/+/545357 as the first failing CL.

@randall77
Copy link
Contributor

The difference in compilation is subtle.
The difference occurs when allocating stack slots. Given a variable we want to allocate a stack slot to, we first check if there are any stack slots of the right type that are available. We do that using a map lookup into a map[*types.Type].... Notably, that lookup uses pointer equality. We end up needing a couple stack slots for values of type *plan3.Field.
When we compile the affected function the first time (in the plan3 package), all the slots we need have the same exact *types.Type describing the type we need, so the lookup hits in the map and we reuse the same stack slot several times.
When we compile the affected function the second time (in the plan3_test package), all the slots we need have different, but equivalent, *types.Type structures, so the map misses, and we allocate a few additional stack slots.

CL 545357 is implicated because it is the one that makes the different but equivalent *types.Type structures, when converting from []plan3.Field to *plan3.Field via Elem().PtrTo().

The reason this happens differently when compiling plan3 and plan3_test is that there is a cache in the PtrTo implementation, and it happens to be populated early on in compilation in the former but not in the latter. By the time the latter calls into PtrTo, caching is turned off because we're in the parallel phase of the compiler.

So I think the underlying bug here is that we're using exact pointer equality during stack allocation. We should be using something more forgiving of different-pointer-but-same-type types.

It does make me nervous that we're so dependent on having byte-by-byte identical compilation on two different invocations of the compiler. And that the linker didn't catch the discrepancy.

@randall77
Copy link
Contributor

@gopherbot Please open a backport issue for 1.22.

@gopherbot
Copy link
Contributor

Backport issue(s) opened: #65818 (for 1.22).

Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases.

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/565436 mentions this issue: cmd/compile: soften type matching when allocating stack slots

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/565535 mentions this issue: cmd/compile: make jump table symbol static

gopherbot pushed a commit that referenced this issue Feb 20, 2024
The jump table symbol is accessed only from the function symbol
(in the same package), so it can be static. Also, if the function
is DUPOK and it is, somehow, compiled differently in two different
packages, the linker must choose the jump table symbol associated
to the function symbol it chose. Currently the jump table symbol
is DUPOK, so that is not guaranteed. Making it static will
guarantee that, as each copy of the function symbol refers to its
own jump table symbol.

For #65783.

Change-Id: I27e051d01ef585d07700b75d4dfac5768f16441e
Reviewed-on: https://go-review.googlesource.com/c/go/+/565535
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@golang.org>
@cherrymui
Copy link
Member

CL https://go.dev/cl/565535 should fix this.

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/566475 mentions this issue: [release-branch.go1.22] cmd/compile: make jump table symbol static

gopherbot pushed a commit that referenced this issue Feb 27, 2024
The jump table symbol is accessed only from the function symbol
(in the same package), so it can be static. Also, if the function
is DUPOK and it is, somehow, compiled differently in two different
packages, the linker must choose the jump table symbol associated
to the function symbol it chose. Currently the jump table symbol
is DUPOK, so that is not guaranteed. Making it static will
guarantee that, as each copy of the function symbol refers to its
own jump table symbol.

Updates #65783.
Fixes #65818.

Change-Id: I27e051d01ef585d07700b75d4dfac5768f16441e
Reviewed-on: https://go-review.googlesource.com/c/go/+/565535
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@golang.org>
(cherry picked from commit 2908352)
Reviewed-on: https://go-review.googlesource.com/c/go/+/566475
gopherbot pushed a commit that referenced this issue Feb 29, 2024
Currently we use pointer equality on types when deciding whether we can
reuse a stack slot. That's too strict, as we don't guarantee pointer
equality for the same type. In particular, it can vary based on whether
PtrTo has been called in the frontend or not.

Instead, use the type's LinkString, which is guaranteed to both be
unique for a type, and to not vary given two different type structures
describing the same type.

Update #65783

Change-Id: I64f55138475f04bfa30cfb819b786b7cc06aebe4
Reviewed-on: https://go-review.googlesource.com/c/go/+/565436
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Keith Randall <khr@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
bradfitz pushed a commit to tailscale/go that referenced this issue Mar 5, 2024
The jump table symbol is accessed only from the function symbol
(in the same package), so it can be static. Also, if the function
is DUPOK and it is, somehow, compiled differently in two different
packages, the linker must choose the jump table symbol associated
to the function symbol it chose. Currently the jump table symbol
is DUPOK, so that is not guaranteed. Making it static will
guarantee that, as each copy of the function symbol refers to its
own jump table symbol.

Updates golang#65783.
Fixes golang#65818.

Change-Id: I27e051d01ef585d07700b75d4dfac5768f16441e
Reviewed-on: https://go-review.googlesource.com/c/go/+/565535
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@golang.org>
(cherry picked from commit 2908352)
Reviewed-on: https://go-review.googlesource.com/c/go/+/566475
romaindoumenc pushed a commit to TroutSoftware/go that referenced this issue Mar 6, 2024
The jump table symbol is accessed only from the function symbol
(in the same package), so it can be static. Also, if the function
is DUPOK and it is, somehow, compiled differently in two different
packages, the linker must choose the jump table symbol associated
to the function symbol it chose. Currently the jump table symbol
is DUPOK, so that is not guaranteed. Making it static will
guarantee that, as each copy of the function symbol refers to its
own jump table symbol.

Updates golang#65783.
Fixes golang#65818.

Change-Id: I27e051d01ef585d07700b75d4dfac5768f16441e
Reviewed-on: https://go-review.googlesource.com/c/go/+/565535
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@golang.org>
(cherry picked from commit 2908352)
Reviewed-on: https://go-review.googlesource.com/c/go/+/566475
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime.
Projects
None yet
Development

No branches or pull requests

4 participants