Skip to content

Commit 009d714

Browse files
committed
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 #42076 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]>
1 parent 933721b commit 009d714

File tree

6 files changed

+81
-19
lines changed

6 files changed

+81
-19
lines changed

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -1854,8 +1854,10 @@ func isdirectiface(t *types.Type) bool {
18541854
}
18551855

18561856
switch t.Etype {
1857-
case TPTR,
1858-
TCHAN,
1857+
case TPTR:
1858+
// Pointers to notinheap types must be stored indirectly. See issue 42076.
1859+
return !t.Elem().NotInHeap()
1860+
case TCHAN,
18591861
TMAP,
18601862
TFUNC,
18611863
TUNSAFEPTR:

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -2516,7 +2516,7 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
25162516
n.Left = nod(OADDR, n.Left, nil)
25172517
n.Left.SetImplicit(true)
25182518
n.Left = typecheck(n.Left, ctxType|ctxExpr)
2519-
} else if tt.IsPtr() && !rcvr.IsPtr() && types.Identical(tt.Elem(), rcvr) {
2519+
} else if tt.IsPtr() && (!rcvr.IsPtr() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && types.Identical(tt.Elem(), rcvr) {
25202520
n.Left = nod(ODEREF, n.Left, nil)
25212521
n.Left.SetImplicit(true)
25222522
n.Left = typecheck(n.Left, ctxType|ctxExpr)

src/reflect/deepequal.go

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

src/reflect/value.go

+11-1
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")
@@ -1453,7 +1454,16 @@ func (v Value) Pointer() uintptr {
14531454
// TODO: deprecate
14541455
k := v.kind()
14551456
switch k {
1456-
case Chan, Map, Ptr, UnsafePointer:
1457+
case Ptr:
1458+
if v.typ.ptrdata == 0 {
1459+
// Handle pointers to go:notinheap types directly,
1460+
// so we never materialize such pointers as an
1461+
// unsafe.Pointer. (Such pointers are always indirect.)
1462+
// See issue 42076.
1463+
return *(*uintptr)(v.ptr)
1464+
}
1465+
fallthrough
1466+
case Chan, Map, UnsafePointer:
14571467
return uintptr(v.pointer())
14581468
case Func:
14591469
if v.flag&flagMethod != 0 {

src/runtime/netpoll.go

+33-14
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

+21
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)