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