go version)?$ go version go version go1.12.6 darwin/amd64
Yes.
go env)?go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/bartek/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/bartek/gocode"
GOPROXY=""
GORACE=""
GOROOT="/Users/bartek/Downloads/go"
GOTMPDIR=""
GOTOOLDIR="/Users/bartek/Downloads/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/zv/kh35w1j12wn5_4md98dl9zfr0000gn/T/go-build400417808=/tmp/go-build -gno-record-gcc-switches -fno-common"
https://play.golang.org/p/da7SYpb02ed
A switch statement on a custom int32 type with negative values behaves differently in two consecutive calls to the same function. What is strange is that each of the following changes make this application work correctly:
BucketEntryTypeMetaentry to a positive number.case clauses.fmt.Println("ok") between entry.MustMetaEntry() lines (lines 40 and 41).fmt.Println("ok") anywhere inside MustMetaEntry().MetaEntry on BucketEntry.No output:
panic: oh no
goroutine 1 [running]:
main.BucketEntry.MustMetaEntry(...)
/Users/bartek/gocode/src/github.com/stellar/go/custom_type.go:31
main.main()
/Users/bartek/gocode/src/github.com/stellar/go/custom_type.go:41 +0x81
exit status 2
Please note that function call in line 40 does not panic.
Interesting. Works with Go 1.10 and gccgo, fails with Go 1.11, 1.12, and tip.
CC @randall77 @josharian @dr2chase @mdempsky
Inspecting the GOSSAFUNC=main output, it looks like the "generic deadcode" pass gets rid of the normal Return path, and leaves behind only the two panicking paths.
Minimized:
package main
var x int32 = -1
func main() {
if x != -1 {
panic("oh no")
}
if x > 0 {
panic("oh no")
}
}
Actually that minimized test case only fails at tip. Here's one that fails back at Go 1.11 too:
package main
var x int32 = -1
func main() {
if x != -1 {
panic("oh no")
}
if x > 0 || x != -1 {
panic("oh no")
}
}
Bisected to CL 100278 (29162ec9a).
/cc @rasky
And FWIW, the simpler test case bisects to CL 165617 (4a9064ef41).
It's because that CL swaps the order that the bThen and bElse blocks are created, and apparently prove.go is sensitive to that.
I think the problem is this check:
// Check if the recorded limits can prove that the value is positive
if l, has := ft.limits[v.ID]; has && (l.min >= 0 || l.umax <= math.MaxInt64) {
The l.umax <= math.MaxInt64 check doesn't make sense for int8, int16, or int32, because they still have negative values whose unsigned representation is less than MaxInt64.
Change https://golang.org/cl/181797 mentions this issue: cmd/compile: fix range analysis of small signed integers
I'm curious what folks think about this issue.
On the one hand, it seems embarrassingly bad and in need of a fix and backport. (Edit: By "embarassingly bad," I just mean because the minimal repro cases don't involve any tricky Go semantics; the underlying root cause is totally understandable though.)
On the other, it's been in stable releases for almost a year and this is the first we've heard of it?
If the fix seems safe then I would vote for fixing and backporting.
@ianlancetaylor The fix seems straight forward enough to me, but it could definitely use some more eyes from the usual package ssa reviewers. This was my first time looking at prove.go. (Thanks to @josharian for the quick review and +1.)
Thanks for checking this so quickly!
On the one hand, it seems embarrassingly bad and in need of a fix and backport.
One thing I'm wondering about is if it poses any security risk. Consider this code. Obviously, this is a bad code and a simple test (or even testing it manually) would catch it quickly but it shows the potential risks.
On the other, it's been in stable releases for almost a year and this is the first we've heard of it?
Actually, the original code that revealed this bug was different but I submitted the other snippet because it was isolated. The thing is, at first I really thought that it's probably something wrong with my code (not Golang) and I could easily fix this by changing entry.MustMetaEntry() to entry.MetaEntry:
if entry.Type == BucketEntryTypeMetaentry {
meta2 := entry.MetaEntry
fmt.Println(meta2)
}
But I started digging... I guess that maybe (maybe!) there were other instances of this but devs just changed their code to work thinking it's an issue with their code.
cc also @aclements @zdjones for prove
Is the fix enough?
I would expect the minimized test to fail with int8 and int16 but it passes for me in the playground.
I would expect the minimized test to fail with int8 and int16 but it passes for me in the playground.
Hm, interesting.
Just looking at the code, I would guess int8 and int16 don't reproduce the issue simply because prove.go only looks for 32- and 64-bit operations (eg, OpAdd64, OpAdd32, OpConst64, OpConst32).
@gopherbot please open backport issues. This is an important compiler correctness bug with a safe, localized, minimal fix.
Backport issue(s) opened: #32582 (for 1.11), #32583 (for 1.12).
Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://golang.org/wiki/MinorReleases.
Most helpful comment
Thanks for checking this so quickly!
One thing I'm wondering about is if it poses any security risk. Consider this code. Obviously, this is a bad code and a simple test (or even testing it manually) would catch it quickly but it shows the potential risks.
Actually, the original code that revealed this bug was different but I submitted the other snippet because it was isolated. The thing is, at first I really thought that it's probably something wrong with my code (not Golang) and I could easily fix this by changing
entry.MustMetaEntry()toentry.MetaEntry:But I started digging... I guess that maybe (maybe!) there were other instances of this but devs just changed their code to work thinking it's an issue with their code.