]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/cgocheck.go
[dev.typeparams] all: merge master (16e82be) into dev.typeparams
[gostls13.git] / src / runtime / cgocheck.go
1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Code to check that pointer writes follow the cgo rules.
6 // These functions are invoked via the write barrier when debug.cgocheck > 1.
7
8 package runtime
9
10 import (
11         "internal/goarch"
12         "unsafe"
13 )
14
15 const cgoWriteBarrierFail = "Go pointer stored into non-Go memory"
16
17 // cgoCheckWriteBarrier is called whenever a pointer is stored into memory.
18 // It throws if the program is storing a Go pointer into non-Go memory.
19 //
20 // This is called from the write barrier, so its entire call tree must
21 // be nosplit.
22 //
23 //go:nosplit
24 //go:nowritebarrier
25 func cgoCheckWriteBarrier(dst *uintptr, src uintptr) {
26         if !cgoIsGoPointer(unsafe.Pointer(src)) {
27                 return
28         }
29         if cgoIsGoPointer(unsafe.Pointer(dst)) {
30                 return
31         }
32
33         // If we are running on the system stack then dst might be an
34         // address on the stack, which is OK.
35         g := getg()
36         if g == g.m.g0 || g == g.m.gsignal {
37                 return
38         }
39
40         // Allocating memory can write to various mfixalloc structs
41         // that look like they are non-Go memory.
42         if g.m.mallocing != 0 {
43                 return
44         }
45
46         // It's OK if writing to memory allocated by persistentalloc.
47         // Do this check last because it is more expensive and rarely true.
48         // If it is false the expense doesn't matter since we are crashing.
49         if inPersistentAlloc(uintptr(unsafe.Pointer(dst))) {
50                 return
51         }
52
53         systemstack(func() {
54                 println("write of Go pointer", hex(src), "to non-Go memory", hex(uintptr(unsafe.Pointer(dst))))
55                 throw(cgoWriteBarrierFail)
56         })
57 }
58
59 // cgoCheckMemmove is called when moving a block of memory.
60 // dst and src point off bytes into the value to copy.
61 // size is the number of bytes to copy.
62 // It throws if the program is copying a block that contains a Go pointer
63 // into non-Go memory.
64 //go:nosplit
65 //go:nowritebarrier
66 func cgoCheckMemmove(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
67         if typ.ptrdata == 0 {
68                 return
69         }
70         if !cgoIsGoPointer(src) {
71                 return
72         }
73         if cgoIsGoPointer(dst) {
74                 return
75         }
76         cgoCheckTypedBlock(typ, src, off, size)
77 }
78
79 // cgoCheckSliceCopy is called when copying n elements of a slice.
80 // src and dst are pointers to the first element of the slice.
81 // typ is the element type of the slice.
82 // It throws if the program is copying slice elements that contain Go pointers
83 // into non-Go memory.
84 //go:nosplit
85 //go:nowritebarrier
86 func cgoCheckSliceCopy(typ *_type, dst, src unsafe.Pointer, n int) {
87         if typ.ptrdata == 0 {
88                 return
89         }
90         if !cgoIsGoPointer(src) {
91                 return
92         }
93         if cgoIsGoPointer(dst) {
94                 return
95         }
96         p := src
97         for i := 0; i < n; i++ {
98                 cgoCheckTypedBlock(typ, p, 0, typ.size)
99                 p = add(p, typ.size)
100         }
101 }
102
103 // cgoCheckTypedBlock checks the block of memory at src, for up to size bytes,
104 // and throws if it finds a Go pointer. The type of the memory is typ,
105 // and src is off bytes into that type.
106 //go:nosplit
107 //go:nowritebarrier
108 func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) {
109         // Anything past typ.ptrdata is not a pointer.
110         if typ.ptrdata <= off {
111                 return
112         }
113         if ptrdataSize := typ.ptrdata - off; size > ptrdataSize {
114                 size = ptrdataSize
115         }
116
117         if typ.kind&kindGCProg == 0 {
118                 cgoCheckBits(src, typ.gcdata, off, size)
119                 return
120         }
121
122         // The type has a GC program. Try to find GC bits somewhere else.
123         for _, datap := range activeModules() {
124                 if cgoInRange(src, datap.data, datap.edata) {
125                         doff := uintptr(src) - datap.data
126                         cgoCheckBits(add(src, -doff), datap.gcdatamask.bytedata, off+doff, size)
127                         return
128                 }
129                 if cgoInRange(src, datap.bss, datap.ebss) {
130                         boff := uintptr(src) - datap.bss
131                         cgoCheckBits(add(src, -boff), datap.gcbssmask.bytedata, off+boff, size)
132                         return
133                 }
134         }
135
136         s := spanOfUnchecked(uintptr(src))
137         if s.state.get() == mSpanManual {
138                 // There are no heap bits for value stored on the stack.
139                 // For a channel receive src might be on the stack of some
140                 // other goroutine, so we can't unwind the stack even if
141                 // we wanted to.
142                 // We can't expand the GC program without extra storage
143                 // space we can't easily get.
144                 // Fortunately we have the type information.
145                 systemstack(func() {
146                         cgoCheckUsingType(typ, src, off, size)
147                 })
148                 return
149         }
150
151         // src must be in the regular heap.
152
153         hbits := heapBitsForAddr(uintptr(src))
154         for i := uintptr(0); i < off+size; i += goarch.PtrSize {
155                 bits := hbits.bits()
156                 if i >= off && bits&bitPointer != 0 {
157                         v := *(*unsafe.Pointer)(add(src, i))
158                         if cgoIsGoPointer(v) {
159                                 throw(cgoWriteBarrierFail)
160                         }
161                 }
162                 hbits = hbits.next()
163         }
164 }
165
166 // cgoCheckBits checks the block of memory at src, for up to size
167 // bytes, and throws if it finds a Go pointer. The gcbits mark each
168 // pointer value. The src pointer is off bytes into the gcbits.
169 //go:nosplit
170 //go:nowritebarrier
171 func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) {
172         skipMask := off / goarch.PtrSize / 8
173         skipBytes := skipMask * goarch.PtrSize * 8
174         ptrmask := addb(gcbits, skipMask)
175         src = add(src, skipBytes)
176         off -= skipBytes
177         size += off
178         var bits uint32
179         for i := uintptr(0); i < size; i += goarch.PtrSize {
180                 if i&(goarch.PtrSize*8-1) == 0 {
181                         bits = uint32(*ptrmask)
182                         ptrmask = addb(ptrmask, 1)
183                 } else {
184                         bits >>= 1
185                 }
186                 if off > 0 {
187                         off -= goarch.PtrSize
188                 } else {
189                         if bits&1 != 0 {
190                                 v := *(*unsafe.Pointer)(add(src, i))
191                                 if cgoIsGoPointer(v) {
192                                         throw(cgoWriteBarrierFail)
193                                 }
194                         }
195                 }
196         }
197 }
198
199 // cgoCheckUsingType is like cgoCheckTypedBlock, but is a last ditch
200 // fall back to look for pointers in src using the type information.
201 // We only use this when looking at a value on the stack when the type
202 // uses a GC program, because otherwise it's more efficient to use the
203 // GC bits. This is called on the system stack.
204 //go:nowritebarrier
205 //go:systemstack
206 func cgoCheckUsingType(typ *_type, src unsafe.Pointer, off, size uintptr) {
207         if typ.ptrdata == 0 {
208                 return
209         }
210
211         // Anything past typ.ptrdata is not a pointer.
212         if typ.ptrdata <= off {
213                 return
214         }
215         if ptrdataSize := typ.ptrdata - off; size > ptrdataSize {
216                 size = ptrdataSize
217         }
218
219         if typ.kind&kindGCProg == 0 {
220                 cgoCheckBits(src, typ.gcdata, off, size)
221                 return
222         }
223         switch typ.kind & kindMask {
224         default:
225                 throw("can't happen")
226         case kindArray:
227                 at := (*arraytype)(unsafe.Pointer(typ))
228                 for i := uintptr(0); i < at.len; i++ {
229                         if off < at.elem.size {
230                                 cgoCheckUsingType(at.elem, src, off, size)
231                         }
232                         src = add(src, at.elem.size)
233                         skipped := off
234                         if skipped > at.elem.size {
235                                 skipped = at.elem.size
236                         }
237                         checked := at.elem.size - skipped
238                         off -= skipped
239                         if size <= checked {
240                                 return
241                         }
242                         size -= checked
243                 }
244         case kindStruct:
245                 st := (*structtype)(unsafe.Pointer(typ))
246                 for _, f := range st.fields {
247                         if off < f.typ.size {
248                                 cgoCheckUsingType(f.typ, src, off, size)
249                         }
250                         src = add(src, f.typ.size)
251                         skipped := off
252                         if skipped > f.typ.size {
253                                 skipped = f.typ.size
254                         }
255                         checked := f.typ.size - skipped
256                         off -= skipped
257                         if size <= checked {
258                                 return
259                         }
260                         size -= checked
261                 }
262         }
263 }