From: khr@golang.org Date: Wed, 28 Feb 2024 00:00:52 +0000 (-0800) Subject: runtime: don't re-initialize itab while looking for missing function X-Git-Tag: go1.23rc1~1041 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2589a89468c4cd7a1132a76288fcf082cdaa2454;p=gostls13.git runtime: don't re-initialize itab while looking for missing function The itab we're initializing again, just to figure out which method is missing, might be stored in read-only memory. This can only happen in certain weird generics situations, so it is pretty rare, but it causes a runtime crash when it does happen. Fixes #65962 Change-Id: Ia86e216fe33950a794ad8e475e76317f799e9136 Reviewed-on: https://go-review.googlesource.com/c/go/+/567615 Reviewed-by: Cherry Mui LUCI-TryBot-Result: Go LUCI Reviewed-by: Keith Randall --- diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 4caf717685..99e9a367f5 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -74,7 +74,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { // and thus the hash is irrelevant. // Note: m.Hash is _not_ the hash used for the runtime itabTable hash table. m.Hash = 0 - itabInit(m) + itabInit(m, true) itabAdd(m) unlock(&itabLock) finish: @@ -90,7 +90,7 @@ finish: // The cached result doesn't record which // interface function was missing, so initialize // the itab again to get the missing function name. - panic(&TypeAssertionError{concrete: typ, asserted: &inter.Type, missingMethod: itabInit(m)}) + panic(&TypeAssertionError{concrete: typ, asserted: &inter.Type, missingMethod: itabInit(m, false)}) } // find finds the given interface/type pair in t. @@ -186,11 +186,13 @@ func (t *itabTableType) add(m *itab) { } } -// init fills in the m.Fun array with all the code pointers for +// itabInit fills in the m.Fun array with all the code pointers for // the m.Inter/m.Type pair. If the type does not implement the interface, // it sets m.Fun[0] to 0 and returns the name of an interface function that is missing. -// It is ok to call this multiple times on the same m, even concurrently. -func itabInit(m *itab) string { +// If !firstTime, itabInit will not write anything to m.Fun (see issue 65962). +// It is ok to call this multiple times on the same m, even concurrently +// (although it will only be called once with firstTime==true). +func itabInit(m *itab, firstTime bool) string { inter := m.Inter typ := m.Type x := typ.Uncommon() @@ -228,7 +230,7 @@ imethods: ifn := rtyp.textOff(t.Ifn) if k == 0 { fun0 = ifn // we'll set m.Fun[0] at the end - } else { + } else if firstTime { methods[k] = ifn } continue imethods @@ -236,10 +238,12 @@ imethods: } } // didn't find method - m.Fun[0] = 0 + // Leaves m.Fun[0] set to 0. return iname } - m.Fun[0] = uintptr(fun0) + if firstTime { + m.Fun[0] = uintptr(fun0) + } return "" } diff --git a/test/fixedbugs/issue65962.go b/test/fixedbugs/issue65962.go new file mode 100644 index 0000000000..a60cd32fd1 --- /dev/null +++ b/test/fixedbugs/issue65962.go @@ -0,0 +1,48 @@ +// run + +// Copyright 2024 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 main() { + test1() + test2() +} + +type I interface { + f() + g() + h() +} + +//go:noinline +func ld[T any]() { + var x I + if _, ok := x.(T); ok { + } +} + +func isI(x any) { + _ = x.(I) +} + +func test1() { + defer func() { recover() }() + ld[bool]() // add itab to binary + _ = any(false).(I) +} + +type B bool + +func (B) f() { +} +func (B) g() { +} + +func test2() { + defer func() { recover() }() + ld[B]() // add itab to binary + _ = any(B(false)).(I) +}