Source file
src/runtime/cgocheck.go
Documentation: runtime
1
2
3
4
5
6
7
8 package runtime
9
10 import (
11 "internal/goarch"
12 "internal/goexperiment"
13 "unsafe"
14 )
15
16 const cgoWriteBarrierFail = "unpinned Go pointer stored into non-Go memory"
17
18
19
20
21
22
23
24
25
26 func cgoCheckPtrWrite(dst *unsafe.Pointer, src unsafe.Pointer) {
27 if !mainStarted {
28
29
30
31 return
32 }
33 if !cgoIsGoPointer(src) {
34 return
35 }
36 if cgoIsGoPointer(unsafe.Pointer(dst)) {
37 return
38 }
39
40
41
42 gp := getg()
43 if gp == gp.m.g0 || gp == gp.m.gsignal {
44 return
45 }
46
47
48
49 if gp.m.mallocing != 0 {
50 return
51 }
52
53
54
55 if isPinned(src) {
56 return
57 }
58
59
60
61
62 if inPersistentAlloc(uintptr(unsafe.Pointer(dst))) {
63 return
64 }
65
66 systemstack(func() {
67 println("write of unpinned Go pointer", hex(uintptr(src)), "to non-Go memory", hex(uintptr(unsafe.Pointer(dst))))
68 throw(cgoWriteBarrierFail)
69 })
70 }
71
72
73
74
75
76
77
78
79
80 func cgoCheckMemmove(typ *_type, dst, src unsafe.Pointer) {
81 cgoCheckMemmove2(typ, dst, src, 0, typ.Size_)
82 }
83
84
85
86
87
88
89
90
91
92 func cgoCheckMemmove2(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
93 if typ.PtrBytes == 0 {
94 return
95 }
96 if !cgoIsGoPointer(src) {
97 return
98 }
99 if cgoIsGoPointer(dst) {
100 return
101 }
102 cgoCheckTypedBlock(typ, src, off, size)
103 }
104
105
106
107
108
109
110
111
112
113 func cgoCheckSliceCopy(typ *_type, dst, src unsafe.Pointer, n int) {
114 if typ.PtrBytes == 0 {
115 return
116 }
117 if !cgoIsGoPointer(src) {
118 return
119 }
120 if cgoIsGoPointer(dst) {
121 return
122 }
123 p := src
124 for i := 0; i < n; i++ {
125 cgoCheckTypedBlock(typ, p, 0, typ.Size_)
126 p = add(p, typ.Size_)
127 }
128 }
129
130
131
132
133
134
135
136 func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) {
137
138 if typ.PtrBytes <= off {
139 return
140 }
141 if ptrdataSize := typ.PtrBytes - off; size > ptrdataSize {
142 size = ptrdataSize
143 }
144
145 if typ.Kind_&kindGCProg == 0 {
146 cgoCheckBits(src, typ.GCData, off, size)
147 return
148 }
149
150
151 for _, datap := range activeModules() {
152 if cgoInRange(src, datap.data, datap.edata) {
153 doff := uintptr(src) - datap.data
154 cgoCheckBits(add(src, -doff), datap.gcdatamask.bytedata, off+doff, size)
155 return
156 }
157 if cgoInRange(src, datap.bss, datap.ebss) {
158 boff := uintptr(src) - datap.bss
159 cgoCheckBits(add(src, -boff), datap.gcbssmask.bytedata, off+boff, size)
160 return
161 }
162 }
163
164 s := spanOfUnchecked(uintptr(src))
165 if s.state.get() == mSpanManual {
166
167
168
169
170
171
172
173 systemstack(func() {
174 cgoCheckUsingType(typ, src, off, size)
175 })
176 return
177 }
178
179
180 if goexperiment.AllocHeaders {
181 tp := s.typePointersOf(uintptr(src), size)
182 for {
183 var addr uintptr
184 if tp, addr = tp.next(uintptr(src) + size); addr == 0 {
185 break
186 }
187 v := *(*unsafe.Pointer)(unsafe.Pointer(addr))
188 if cgoIsGoPointer(v) && !isPinned(v) {
189 throw(cgoWriteBarrierFail)
190 }
191 }
192 } else {
193 hbits := heapBitsForAddr(uintptr(src), size)
194 for {
195 var addr uintptr
196 if hbits, addr = hbits.next(); addr == 0 {
197 break
198 }
199 v := *(*unsafe.Pointer)(unsafe.Pointer(addr))
200 if cgoIsGoPointer(v) && !isPinned(v) {
201 throw(cgoWriteBarrierFail)
202 }
203 }
204 }
205 }
206
207
208
209
210
211
212
213 func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) {
214 skipMask := off / goarch.PtrSize / 8
215 skipBytes := skipMask * goarch.PtrSize * 8
216 ptrmask := addb(gcbits, skipMask)
217 src = add(src, skipBytes)
218 off -= skipBytes
219 size += off
220 var bits uint32
221 for i := uintptr(0); i < size; i += goarch.PtrSize {
222 if i&(goarch.PtrSize*8-1) == 0 {
223 bits = uint32(*ptrmask)
224 ptrmask = addb(ptrmask, 1)
225 } else {
226 bits >>= 1
227 }
228 if off > 0 {
229 off -= goarch.PtrSize
230 } else {
231 if bits&1 != 0 {
232 v := *(*unsafe.Pointer)(add(src, i))
233 if cgoIsGoPointer(v) && !isPinned(v) {
234 throw(cgoWriteBarrierFail)
235 }
236 }
237 }
238 }
239 }
240
241
242
243
244
245
246
247
248
249 func cgoCheckUsingType(typ *_type, src unsafe.Pointer, off, size uintptr) {
250 if typ.PtrBytes == 0 {
251 return
252 }
253
254
255 if typ.PtrBytes <= off {
256 return
257 }
258 if ptrdataSize := typ.PtrBytes - off; size > ptrdataSize {
259 size = ptrdataSize
260 }
261
262 if typ.Kind_&kindGCProg == 0 {
263 cgoCheckBits(src, typ.GCData, off, size)
264 return
265 }
266 switch typ.Kind_ & kindMask {
267 default:
268 throw("can't happen")
269 case kindArray:
270 at := (*arraytype)(unsafe.Pointer(typ))
271 for i := uintptr(0); i < at.Len; i++ {
272 if off < at.Elem.Size_ {
273 cgoCheckUsingType(at.Elem, src, off, size)
274 }
275 src = add(src, at.Elem.Size_)
276 skipped := off
277 if skipped > at.Elem.Size_ {
278 skipped = at.Elem.Size_
279 }
280 checked := at.Elem.Size_ - skipped
281 off -= skipped
282 if size <= checked {
283 return
284 }
285 size -= checked
286 }
287 case kindStruct:
288 st := (*structtype)(unsafe.Pointer(typ))
289 for _, f := range st.Fields {
290 if off < f.Typ.Size_ {
291 cgoCheckUsingType(f.Typ, src, off, size)
292 }
293 src = add(src, f.Typ.Size_)
294 skipped := off
295 if skipped > f.Typ.Size_ {
296 skipped = f.Typ.Size_
297 }
298 checked := f.Typ.Size_ - skipped
299 off -= skipped
300 if size <= checked {
301 return
302 }
303 size -= checked
304 }
305 }
306 }
307
View as plain text