Commit e49627d3 authored by Todd Neal's avatar Todd Neal

plugin: properly handle recursively defined types

Prevent a crash if the same type in two plugins had a recursive
definition, either by referring to a pointer to itself or a map existing
with the type as a value type (which creates a recursive definition
through the overflow bucket type).

Fixes #19258

Change-Id: Iac1cbda4c5b6e8edd5e6859a4d5da3bad539a9c6
Reviewed-on: https://go-review.googlesource.com/40292
Run-TryBot: Todd Neal <todd@tneal.org>
Reviewed-by: default avatarDavid Crawshaw <crawshaw@golang.org>
parent 758d078f
...@@ -9,4 +9,15 @@ import "C" ...@@ -9,4 +9,15 @@ import "C"
func FuncInt() int { return 1 } func FuncInt() int { return 1 }
// Add a recursive type to to check that type equality across plugins doesn't
// crash. See https://golang.org/issues/19258
func FuncRecursive() X { return X{} }
type Y struct {
X *X
}
type X struct {
Y Y
}
func main() {} func main() {}
...@@ -9,4 +9,13 @@ import "C" ...@@ -9,4 +9,13 @@ import "C"
func FuncInt() int { return 2 } func FuncInt() int { return 2 }
func FuncRecursive() X { return X{} }
type Y struct {
X *X
}
type X struct {
Y Y
}
func main() {} func main() {}
...@@ -511,7 +511,8 @@ func typelinksinit() { ...@@ -511,7 +511,8 @@ func typelinksinit() {
for _, tl := range md.typelinks { for _, tl := range md.typelinks {
t := (*_type)(unsafe.Pointer(md.types + uintptr(tl))) t := (*_type)(unsafe.Pointer(md.types + uintptr(tl)))
for _, candidate := range typehash[t.hash] { for _, candidate := range typehash[t.hash] {
if typesEqual(t, candidate) { seen := map[_typePair]struct{}{}
if typesEqual(t, candidate, seen) {
t = candidate t = candidate
break break
} }
...@@ -524,6 +525,11 @@ func typelinksinit() { ...@@ -524,6 +525,11 @@ func typelinksinit() {
} }
} }
type _typePair struct {
t1 *_type
t2 *_type
}
// typesEqual reports whether two types are equal. // typesEqual reports whether two types are equal.
// //
// Everywhere in the runtime and reflect packages, it is assumed that // Everywhere in the runtime and reflect packages, it is assumed that
...@@ -536,7 +542,17 @@ func typelinksinit() { ...@@ -536,7 +542,17 @@ func typelinksinit() {
// back into earlier ones. // back into earlier ones.
// //
// Only typelinksinit needs this function. // Only typelinksinit needs this function.
func typesEqual(t, v *_type) bool { func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool {
tp := _typePair{t, v}
if _, ok := seen[tp]; ok {
return true
}
// mark these types as seen, and thus equivalent which prevents an infinite loop if
// the two types are identical, but recursively defined and loaded from
// different modules
seen[tp] = struct{}{}
if t == v { if t == v {
return true return true
} }
...@@ -568,11 +584,11 @@ func typesEqual(t, v *_type) bool { ...@@ -568,11 +584,11 @@ func typesEqual(t, v *_type) bool {
case kindArray: case kindArray:
at := (*arraytype)(unsafe.Pointer(t)) at := (*arraytype)(unsafe.Pointer(t))
av := (*arraytype)(unsafe.Pointer(v)) av := (*arraytype)(unsafe.Pointer(v))
return typesEqual(at.elem, av.elem) && at.len == av.len return typesEqual(at.elem, av.elem, seen) && at.len == av.len
case kindChan: case kindChan:
ct := (*chantype)(unsafe.Pointer(t)) ct := (*chantype)(unsafe.Pointer(t))
cv := (*chantype)(unsafe.Pointer(v)) cv := (*chantype)(unsafe.Pointer(v))
return ct.dir == cv.dir && typesEqual(ct.elem, cv.elem) return ct.dir == cv.dir && typesEqual(ct.elem, cv.elem, seen)
case kindFunc: case kindFunc:
ft := (*functype)(unsafe.Pointer(t)) ft := (*functype)(unsafe.Pointer(t))
fv := (*functype)(unsafe.Pointer(v)) fv := (*functype)(unsafe.Pointer(v))
...@@ -581,13 +597,13 @@ func typesEqual(t, v *_type) bool { ...@@ -581,13 +597,13 @@ func typesEqual(t, v *_type) bool {
} }
tin, vin := ft.in(), fv.in() tin, vin := ft.in(), fv.in()
for i := 0; i < len(tin); i++ { for i := 0; i < len(tin); i++ {
if !typesEqual(tin[i], vin[i]) { if !typesEqual(tin[i], vin[i], seen) {
return false return false
} }
} }
tout, vout := ft.out(), fv.out() tout, vout := ft.out(), fv.out()
for i := 0; i < len(tout); i++ { for i := 0; i < len(tout); i++ {
if !typesEqual(tout[i], vout[i]) { if !typesEqual(tout[i], vout[i], seen) {
return false return false
} }
} }
...@@ -616,7 +632,7 @@ func typesEqual(t, v *_type) bool { ...@@ -616,7 +632,7 @@ func typesEqual(t, v *_type) bool {
} }
tityp := resolveTypeOff(unsafe.Pointer(tm), tm.ityp) tityp := resolveTypeOff(unsafe.Pointer(tm), tm.ityp)
vityp := resolveTypeOff(unsafe.Pointer(vm), vm.ityp) vityp := resolveTypeOff(unsafe.Pointer(vm), vm.ityp)
if !typesEqual(tityp, vityp) { if !typesEqual(tityp, vityp, seen) {
return false return false
} }
} }
...@@ -624,15 +640,15 @@ func typesEqual(t, v *_type) bool { ...@@ -624,15 +640,15 @@ func typesEqual(t, v *_type) bool {
case kindMap: case kindMap:
mt := (*maptype)(unsafe.Pointer(t)) mt := (*maptype)(unsafe.Pointer(t))
mv := (*maptype)(unsafe.Pointer(v)) mv := (*maptype)(unsafe.Pointer(v))
return typesEqual(mt.key, mv.key) && typesEqual(mt.elem, mv.elem) return typesEqual(mt.key, mv.key, seen) && typesEqual(mt.elem, mv.elem, seen)
case kindPtr: case kindPtr:
pt := (*ptrtype)(unsafe.Pointer(t)) pt := (*ptrtype)(unsafe.Pointer(t))
pv := (*ptrtype)(unsafe.Pointer(v)) pv := (*ptrtype)(unsafe.Pointer(v))
return typesEqual(pt.elem, pv.elem) return typesEqual(pt.elem, pv.elem, seen)
case kindSlice: case kindSlice:
st := (*slicetype)(unsafe.Pointer(t)) st := (*slicetype)(unsafe.Pointer(t))
sv := (*slicetype)(unsafe.Pointer(v)) sv := (*slicetype)(unsafe.Pointer(v))
return typesEqual(st.elem, sv.elem) return typesEqual(st.elem, sv.elem, seen)
case kindStruct: case kindStruct:
st := (*structtype)(unsafe.Pointer(t)) st := (*structtype)(unsafe.Pointer(t))
sv := (*structtype)(unsafe.Pointer(v)) sv := (*structtype)(unsafe.Pointer(v))
...@@ -648,7 +664,7 @@ func typesEqual(t, v *_type) bool { ...@@ -648,7 +664,7 @@ func typesEqual(t, v *_type) bool {
if tf.name.pkgPath() != vf.name.pkgPath() { if tf.name.pkgPath() != vf.name.pkgPath() {
return false return false
} }
if !typesEqual(tf.typ, vf.typ) { if !typesEqual(tf.typ, vf.typ, seen) {
return false return false
} }
if tf.name.tag() != vf.name.tag() { if tf.name.tag() != vf.name.tag() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment