cmd/compile: ignore out-of-bounds reads from readonly constants

Out-of-bounds reads of globals can happen in dead code. For code
like this:

s := "a"
if len(s) == 3 {
   load s[0], s[1], and s[2]
}

The out-of-bounds loads are dead code, but aren't removed yet
when lowering. We need to not panic when compile-time evaluating
those loads. This can only happen for dead code, so the result
doesn't matter.

Fixes #29215

Change-Id: I7fb765766328b9524c6f2a1e6ab8d8edd9875097
Reviewed-on: https://go-review.googlesource.com/c/154057
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alberto Donizetti <alb.donizetti@gmail.com>
This commit is contained in:
Keith Randall 2018-12-13 09:31:21 -08:00 committed by Keith Randall
parent 4422319fbf
commit 443990742e
2 changed files with 34 additions and 0 deletions

View file

@ -1137,12 +1137,22 @@ func symIsRO(sym interface{}) bool {
// read8 reads one byte from the read-only global sym at offset off. // read8 reads one byte from the read-only global sym at offset off.
func read8(sym interface{}, off int64) uint8 { func read8(sym interface{}, off int64) uint8 {
lsym := sym.(*obj.LSym) lsym := sym.(*obj.LSym)
if off >= int64(len(lsym.P)) {
// Invalid index into the global sym.
// This can happen in dead code, so we don't want to panic.
// Just return any value, it will eventually get ignored.
// See issue 29215.
return 0
}
return lsym.P[off] return lsym.P[off]
} }
// read16 reads two bytes from the read-only global sym at offset off. // read16 reads two bytes from the read-only global sym at offset off.
func read16(sym interface{}, off int64, bigEndian bool) uint16 { func read16(sym interface{}, off int64, bigEndian bool) uint16 {
lsym := sym.(*obj.LSym) lsym := sym.(*obj.LSym)
if off >= int64(len(lsym.P))-1 {
return 0
}
if bigEndian { if bigEndian {
return binary.BigEndian.Uint16(lsym.P[off:]) return binary.BigEndian.Uint16(lsym.P[off:])
} else { } else {
@ -1153,6 +1163,9 @@ func read16(sym interface{}, off int64, bigEndian bool) uint16 {
// read32 reads four bytes from the read-only global sym at offset off. // read32 reads four bytes from the read-only global sym at offset off.
func read32(sym interface{}, off int64, bigEndian bool) uint32 { func read32(sym interface{}, off int64, bigEndian bool) uint32 {
lsym := sym.(*obj.LSym) lsym := sym.(*obj.LSym)
if off >= int64(len(lsym.P))-3 {
return 0
}
if bigEndian { if bigEndian {
return binary.BigEndian.Uint32(lsym.P[off:]) return binary.BigEndian.Uint32(lsym.P[off:])
} else { } else {
@ -1163,6 +1176,9 @@ func read32(sym interface{}, off int64, bigEndian bool) uint32 {
// read64 reads eight bytes from the read-only global sym at offset off. // read64 reads eight bytes from the read-only global sym at offset off.
func read64(sym interface{}, off int64, bigEndian bool) uint64 { func read64(sym interface{}, off int64, bigEndian bool) uint64 {
lsym := sym.(*obj.LSym) lsym := sym.(*obj.LSym)
if off >= int64(len(lsym.P))-7 {
return 0
}
if bigEndian { if bigEndian {
return binary.BigEndian.Uint64(lsym.P[off:]) return binary.BigEndian.Uint64(lsym.P[off:])
} else { } else {

View file

@ -0,0 +1,18 @@
// compile
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
func f() {
var s string
var p, q bool
s = "a"
for p {
p = false == (true != q)
s = ""
}
_ = s == "bbb"
}