go: hash/crc32: panic on arm64 with go1.21.0 when indexing slice
What version of Go are you using (go version
)?
$ go version go version go1.21.0 darwin/arm64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE='' GOARCH='arm64' GOBIN='' GOCACHE='/Users/edward/Library/Caches/go-build' GOENV='/Users/edward/Library/Application Support/go/env' GOEXE='' GOEXPERIMENT='' GOFLAGS='' GOHOSTARCH='arm64' GOHOSTOS='darwin' GOINSECURE='' GOMODCACHE='/Users/edward/go/pkg/mod' GONOPROXY='' GONOSUMDB='' GOOS='darwin' GOPATH='/Users/edward/go' GOPRIVATE='' GOPROXY='https://proxy.golang.org,direct' GOROOT='/opt/homebrew/Cellar/go/1.21.0/libexec' GOSUMDB='sum.golang.org' GOTMPDIR='' GOTOOLCHAIN='auto' GOTOOLDIR='/opt/homebrew/Cellar/go/1.21.0/libexec/pkg/tool/darwin_arm64' GOVCS='' GOVERSION='go1.21.0' GCCGO='gccgo' AR='ar' CC='cc' CXX='c++' CGO_ENABLED='1' GOMOD='/dev/null' 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 arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/ld/l69fjx9x3gl6qfx056xt89hr0000gp/T/go-build4293017448=/tmp/go-build -gno-record-gcc-switches -fno-common'
What did you do?
This code panics on go 1.21 on arm64 but runs fine on other platforms or go 1.20 arm64:
It doesn’t occur if I add a
fmt.Println(index)
or debug the application, before callingcolors[index]
package main
import (
"fmt"
"hash/crc32"
)
func pickColor(userID string) string {
colors := []string{
"ffd179",
"f388a8",
"b786d2",
"bed3f4",
"88a0f3",
"6fe8d9",
"e86f85",
}
hash := crc32.ChecksumIEEE([]byte(userID))
index := hash % uint32(len(colors))
return colors[index]
}
func main() {
fmt.Println(pickColor("jHjDiYOWTOS53K9t3XbOU7QXAwD3"))
}
What did you expect to see?
A string printed
What did you see instead?
panic: runtime error: index out of range [-4294967295]
goroutine 1 [running]:
main.pickColor({0x10494b15b?, 0x0?})
/Users/edward/Desktop/go_bug.go:20 +0x118
main.main()
/Users/edward/Desktop/go_bug.go:24 +0x28
exit status 2
About this issue
- Original URL
- State: closed
- Created 10 months ago
- Reactions: 7
- Comments: 15 (10 by maintainers)
I’ve hacked up a change that fixes all the rules to preserve the semantics of the upper 32 bits. For example, when rewriting
MULW
with constant -1 argument, useNEGW
and notNEG
. It’s a pretty rote change but kind of large. Not something I would want to backport. I think we should revert CL 427454 on the 1.21 release branch and do my change for 1.22.I worry other architectures have the same issue, as lots of those were started by copying the arm64 port. I will investigate.
Looks like the
MOVWUreg
is removed when its arg is aMSUBW
, which is guaranteed to zero the upper 32 bits. But then later that arg gets changed to something that doesn’t zero the upper 32 bits, aSUB
. So I think the logic that we don’t need the zero extension if its arg does the zero extension itself, is invalid if we also have the rule that rewrites of 32-bit-typed values can change the upper 32 bits. The rule is only valid if all our rewrite rules preserve the upper-32-bits-are-zeroed property. Unless we do theMOVWUreg
removal in a separate pass after all other rewrites are done.Simple reproducer:
Looks like there is a missing
MOVWU
in the%
computation in 1.21.Since you can reproduce the problem in assembly, it seems likely that this is a compiler or runtime change.
Can you build the Go repo from source and use
git bisect
to find the exact commit at which the regression began?