Skip to content

Commit 255afa2

Browse files
randall77ianlancetaylor
authored andcommitted
[release-branch.go1.15] cmd/compile, runtime: store pointers to go:notinheap types indirectly
pointers to go:notinheap types should be treated as scalars. That means they shouldn't be stored directly in interfaces, or directly in reflect.Value.ptr. Also be sure to use uintpr to compare such pointers in reflect.DeepEqual. Fixes #42169 Change-Id: I53735f6d434e9c3108d4940bd1bae14c61ef2a74 Reviewed-on: https://go-review.googlesource.com/c/go/+/264480 Trust: Keith Randall <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> (cherry picked from commit 009d714) Reviewed-on: https://go-review.googlesource.com/c/go/+/265720 Run-TryBot: Keith Randall <[email protected]> TryBot-Result: Go Bot <[email protected]>
1 parent 8f14c18 commit 255afa2

File tree

6 files changed

+81
-19
lines changed

6 files changed

+81
-19
lines changed

src/cmd/compile/internal/gc/subr.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,8 +1876,10 @@ func isdirectiface(t *types.Type) bool {
18761876
}
18771877

18781878
switch t.Etype {
1879-
case TPTR,
1880-
TCHAN,
1879+
case TPTR:
1880+
// Pointers to notinheap types must be stored indirectly. See issue 42076.
1881+
return !t.Elem().NotInHeap()
1882+
case TCHAN,
18811883
TMAP,
18821884
TFUNC,
18831885
TUNSAFEPTR:

src/cmd/compile/internal/gc/typecheck.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2502,7 +2502,7 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
25022502
n.Left = nod(OADDR, n.Left, nil)
25032503
n.Left.SetImplicit(true)
25042504
n.Left = typecheck(n.Left, ctxType|ctxExpr)
2505-
} else if tt.IsPtr() && !rcvr.IsPtr() && types.Identical(tt.Elem(), rcvr) {
2505+
} else if tt.IsPtr() && (!rcvr.IsPtr() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && types.Identical(tt.Elem(), rcvr) {
25062506
n.Left = nod(ODEREF, n.Left, nil)
25072507
n.Left.SetImplicit(true)
25082508
n.Left = typecheck(n.Left, ctxType|ctxExpr)

src/reflect/deepequal.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,17 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
3737
// and it's safe and valid to get Value's internal pointer.
3838
hard := func(v1, v2 Value) bool {
3939
switch v1.Kind() {
40-
case Map, Slice, Ptr, Interface:
40+
case Ptr:
41+
if v1.typ.ptrdata == 0 {
42+
// go:notinheap pointers can't be cyclic.
43+
// At least, all of our current uses of go:notinheap have
44+
// that property. The runtime ones aren't cyclic (and we don't use
45+
// DeepEqual on them anyway), and the cgo-generated ones are
46+
// all empty structs.
47+
return false
48+
}
49+
fallthrough
50+
case Map, Slice, Interface:
4151
// Nil pointers cannot be cyclic. Avoid putting them in the visited map.
4252
return !v1.IsNil() && !v2.IsNil()
4353
}

src/reflect/value.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ func (f flag) ro() flag {
9090

9191
// pointer returns the underlying pointer represented by v.
9292
// v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer
93+
// if v.Kind() == Ptr, the base type must not be go:notinheap.
9394
func (v Value) pointer() unsafe.Pointer {
9495
if v.typ.size != ptrSize || !v.typ.pointers() {
9596
panic("can't call pointer on a non-pointer Value")
@@ -1452,7 +1453,16 @@ func (v Value) Pointer() uintptr {
14521453
// TODO: deprecate
14531454
k := v.kind()
14541455
switch k {
1455-
case Chan, Map, Ptr, UnsafePointer:
1456+
case Ptr:
1457+
if v.typ.ptrdata == 0 {
1458+
// Handle pointers to go:notinheap types directly,
1459+
// so we never materialize such pointers as an
1460+
// unsafe.Pointer. (Such pointers are always indirect.)
1461+
// See issue 42076.
1462+
return *(*uintptr)(v.ptr)
1463+
}
1464+
fallthrough
1465+
case Chan, Map, UnsafePointer:
14561466
return uintptr(v.pointer())
14571467
case Func:
14581468
if v.flag&flagMethod != 0 {

src/runtime/netpoll.go

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,17 @@ type pollDesc struct {
7979
lock mutex // protects the following fields
8080
fd uintptr
8181
closing bool
82-
everr bool // marks event scanning error happened
83-
user uint32 // user settable cookie
84-
rseq uintptr // protects from stale read timers
85-
rg uintptr // pdReady, pdWait, G waiting for read or nil
86-
rt timer // read deadline timer (set if rt.f != nil)
87-
rd int64 // read deadline
88-
wseq uintptr // protects from stale write timers
89-
wg uintptr // pdReady, pdWait, G waiting for write or nil
90-
wt timer // write deadline timer
91-
wd int64 // write deadline
82+
everr bool // marks event scanning error happened
83+
user uint32 // user settable cookie
84+
rseq uintptr // protects from stale read timers
85+
rg uintptr // pdReady, pdWait, G waiting for read or nil
86+
rt timer // read deadline timer (set if rt.f != nil)
87+
rd int64 // read deadline
88+
wseq uintptr // protects from stale write timers
89+
wg uintptr // pdReady, pdWait, G waiting for write or nil
90+
wt timer // write deadline timer
91+
wd int64 // write deadline
92+
self *pollDesc // storage for indirect interface. See (*pollDesc).makeArg.
9293
}
9394

9495
type pollCache struct {
@@ -157,6 +158,7 @@ func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) {
157158
pd.wseq++
158159
pd.wg = 0
159160
pd.wd = 0
161+
pd.self = pd
160162
unlock(&pd.lock)
161163

162164
var errno int32
@@ -271,14 +273,14 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
271273
// Copy current seq into the timer arg.
272274
// Timer func will check the seq against current descriptor seq,
273275
// if they differ the descriptor was reused or timers were reset.
274-
pd.rt.arg = pd
276+
pd.rt.arg = pd.makeArg()
275277
pd.rt.seq = pd.rseq
276278
resettimer(&pd.rt, pd.rd)
277279
}
278280
} else if pd.rd != rd0 || combo != combo0 {
279281
pd.rseq++ // invalidate current timers
280282
if pd.rd > 0 {
281-
modtimer(&pd.rt, pd.rd, 0, rtf, pd, pd.rseq)
283+
modtimer(&pd.rt, pd.rd, 0, rtf, pd.makeArg(), pd.rseq)
282284
} else {
283285
deltimer(&pd.rt)
284286
pd.rt.f = nil
@@ -287,14 +289,14 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
287289
if pd.wt.f == nil {
288290
if pd.wd > 0 && !combo {
289291
pd.wt.f = netpollWriteDeadline
290-
pd.wt.arg = pd
292+
pd.wt.arg = pd.makeArg()
291293
pd.wt.seq = pd.wseq
292294
resettimer(&pd.wt, pd.wd)
293295
}
294296
} else if pd.wd != wd0 || combo != combo0 {
295297
pd.wseq++ // invalidate current timers
296298
if pd.wd > 0 && !combo {
297-
modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd, pd.wseq)
299+
modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd.makeArg(), pd.wseq)
298300
} else {
299301
deltimer(&pd.wt)
300302
pd.wt.f = nil
@@ -547,3 +549,20 @@ func (c *pollCache) alloc() *pollDesc {
547549
unlock(&c.lock)
548550
return pd
549551
}
552+
553+
// makeArg converts pd to an interface{}.
554+
// makeArg does not do any allocation. Normally, such
555+
// a conversion requires an allocation because pointers to
556+
// go:notinheap types (which pollDesc is) must be stored
557+
// in interfaces indirectly. See issue 42076.
558+
func (pd *pollDesc) makeArg() (i interface{}) {
559+
x := (*eface)(unsafe.Pointer(&i))
560+
x._type = pdType
561+
x.data = unsafe.Pointer(&pd.self)
562+
return
563+
}
564+
565+
var (
566+
pdEface interface{} = (*pollDesc)(nil)
567+
pdType *_type = efaceOf(&pdEface)._type
568+
)

test/fixedbugs/issue42076.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run
2+
3+
// Copyright 2020 The Go Authors. All rights reserved. Use of this
4+
// source code is governed by a BSD-style license that can be found in
5+
// the LICENSE file.
6+
7+
package main
8+
9+
import "reflect"
10+
11+
//go:notinheap
12+
type NIH struct {
13+
}
14+
15+
var x, y NIH
16+
17+
func main() {
18+
if reflect.DeepEqual(&x, &y) != true {
19+
panic("should report true")
20+
}
21+
}

0 commit comments

Comments
 (0)