]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: don't re-initialize itab while looking for missing function
authorkhr@golang.org <khr@golang.org>
Wed, 28 Feb 2024 00:00:52 +0000 (16:00 -0800)
committerKeith Randall <khr@google.com>
Thu, 29 Feb 2024 18:30:40 +0000 (18:30 +0000)
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 <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@google.com>
src/runtime/iface.go
test/fixedbugs/issue65962.go [new file with mode: 0644]

index 4caf7176855bd9c26ab8b49a9ff26917f96bf42b..99e9a367f55740edd05a8e4099c05719ea25fa62 100644 (file)
@@ -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 (file)
index 0000000..a60cd32
--- /dev/null
@@ -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 <bool,I> itab to binary
+       _ = any(false).(I)
+}
+
+type B bool
+
+func (B) f() {
+}
+func (B) g() {
+}
+
+func test2() {
+       defer func() { recover() }()
+       ld[B]() // add <B,I> itab to binary
+       _ = any(B(false)).(I)
+}