Skip to content

Commit 2589a89

Browse files
committed
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 <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent a66a3bf commit 2589a89

File tree

2 files changed

+60
-8
lines changed

2 files changed

+60
-8
lines changed

src/runtime/iface.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
7474
// and thus the hash is irrelevant.
7575
// Note: m.Hash is _not_ the hash used for the runtime itabTable hash table.
7676
m.Hash = 0
77-
itabInit(m)
77+
itabInit(m, true)
7878
itabAdd(m)
7979
unlock(&itabLock)
8080
finish:
@@ -90,7 +90,7 @@ finish:
9090
// The cached result doesn't record which
9191
// interface function was missing, so initialize
9292
// the itab again to get the missing function name.
93-
panic(&TypeAssertionError{concrete: typ, asserted: &inter.Type, missingMethod: itabInit(m)})
93+
panic(&TypeAssertionError{concrete: typ, asserted: &inter.Type, missingMethod: itabInit(m, false)})
9494
}
9595

9696
// find finds the given interface/type pair in t.
@@ -186,11 +186,13 @@ func (t *itabTableType) add(m *itab) {
186186
}
187187
}
188188

189-
// init fills in the m.Fun array with all the code pointers for
189+
// itabInit fills in the m.Fun array with all the code pointers for
190190
// the m.Inter/m.Type pair. If the type does not implement the interface,
191191
// it sets m.Fun[0] to 0 and returns the name of an interface function that is missing.
192-
// It is ok to call this multiple times on the same m, even concurrently.
193-
func itabInit(m *itab) string {
192+
// If !firstTime, itabInit will not write anything to m.Fun (see issue 65962).
193+
// It is ok to call this multiple times on the same m, even concurrently
194+
// (although it will only be called once with firstTime==true).
195+
func itabInit(m *itab, firstTime bool) string {
194196
inter := m.Inter
195197
typ := m.Type
196198
x := typ.Uncommon()
@@ -228,18 +230,20 @@ imethods:
228230
ifn := rtyp.textOff(t.Ifn)
229231
if k == 0 {
230232
fun0 = ifn // we'll set m.Fun[0] at the end
231-
} else {
233+
} else if firstTime {
232234
methods[k] = ifn
233235
}
234236
continue imethods
235237
}
236238
}
237239
}
238240
// didn't find method
239-
m.Fun[0] = 0
241+
// Leaves m.Fun[0] set to 0.
240242
return iname
241243
}
242-
m.Fun[0] = uintptr(fun0)
244+
if firstTime {
245+
m.Fun[0] = uintptr(fun0)
246+
}
243247
return ""
244248
}
245249

test/fixedbugs/issue65962.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// run
2+
3+
// Copyright 2024 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
func main() {
10+
test1()
11+
test2()
12+
}
13+
14+
type I interface {
15+
f()
16+
g()
17+
h()
18+
}
19+
20+
//go:noinline
21+
func ld[T any]() {
22+
var x I
23+
if _, ok := x.(T); ok {
24+
}
25+
}
26+
27+
func isI(x any) {
28+
_ = x.(I)
29+
}
30+
31+
func test1() {
32+
defer func() { recover() }()
33+
ld[bool]() // add <bool,I> itab to binary
34+
_ = any(false).(I)
35+
}
36+
37+
type B bool
38+
39+
func (B) f() {
40+
}
41+
func (B) g() {
42+
}
43+
44+
func test2() {
45+
defer func() { recover() }()
46+
ld[B]() // add <B,I> itab to binary
47+
_ = any(B(false)).(I)
48+
}

0 commit comments

Comments
 (0)