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.
5 // Code to check that pointer writes follow the cgo rules.
6 // These functions are invoked via the write barrier when debug.cgocheck > 1.
11 "runtime/internal/sys"
15 const cgoWriteBarrierFail
= "Go pointer stored into non-Go memory"
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.
21 func cgoCheckWriteBarrier(dst
*uintptr, src
uintptr) {
22 if !cgoIsGoPointer(unsafe
.Pointer(src
)) {
25 if cgoIsGoPointer(unsafe
.Pointer(dst
)) {
29 // If we are running on the system stack then dst might be an
30 // address on the stack, which is OK.
32 if g
== g
.m
.g0 || g
== g
.m
.gsignal
{
36 // Allocating memory can write to various mfixalloc structs
37 // that look like they are non-Go memory.
38 if g
.m
.mallocing
!= 0 {
43 println("write of Go pointer", hex(src
), "to non-Go memory", hex(uintptr(unsafe
.Pointer(dst
))))
44 throw(cgoWriteBarrierFail
)
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.
55 func cgoCheckMemmove(typ
*_type
, dst
, src unsafe
.Pointer
, off
, size
uintptr) {
56 if typ
.kind
&kindNoPointers
!= 0 {
59 if !cgoIsGoPointer(src
) {
62 if cgoIsGoPointer(dst
) {
65 cgoCheckTypedBlock(typ
, src
, off
, size
)
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.
74 func cgoCheckSliceCopy(typ
*_type
, dst
, src slice
, n
int) {
75 if typ
.kind
&kindNoPointers
!= 0 {
78 if !cgoIsGoPointer(src
.array
) {
81 if cgoIsGoPointer(dst
.array
) {
85 for i
:= 0; i
< n
; i
++ {
86 cgoCheckTypedBlock(typ
, p
, 0, typ
.size
)
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.
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
{
101 if ptrdataSize
:= typ
.ptrdata
- off
; size
> ptrdataSize
{
105 if typ
.kind
&kindGCProg
== 0 {
106 cgoCheckBits(src
, typ
.gcdata
, off
, size
)
110 // The type has a GC program. Try to find GC bits somewhere else.
111 for datap
:= &firstmoduledata
; datap
!= nil; datap
= datap
.next
{
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
)
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
)
124 aoff
:= uintptr(src
) - mheap_
.arena_start
125 idx
:= aoff
>> _PageShift
127 if s
.state
== _MSpanStack
{
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
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.
136 cgoCheckUsingType(typ
, src
, off
, size
)
141 // src must be in the regular heap.
143 hbits
:= heapBitsForAddr(uintptr(src
))
144 for i
:= uintptr(0); i
< off
+size
; i
+= sys
.PtrSize
{
146 if i
>= off
&& bits
&bitPointer
!= 0 {
147 v
:= *(*unsafe
.Pointer
)(add(src
, i
))
148 if cgoIsGoPointer(v
) {
150 throw(cgoWriteBarrierFail
)
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.
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
)
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)
182 v
:= *(*unsafe
.Pointer
)(add(src
, i
))
183 if cgoIsGoPointer(v
) {
185 throw(cgoWriteBarrierFail
)
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.
200 func cgoCheckUsingType(typ
*_type
, src unsafe
.Pointer
, off
, size
uintptr) {
201 if typ
.kind
&kindNoPointers
!= 0 {
205 // Anything past typ.ptrdata is not a pointer.
206 if typ
.ptrdata
<= off
{
209 if ptrdataSize
:= typ
.ptrdata
- off
; size
> ptrdataSize
{
213 if typ
.kind
&kindGCProg
== 0 {
214 cgoCheckBits(src
, typ
.gcdata
, off
, size
)
217 switch typ
.kind
& kindMask
{
219 throw("can't happen")
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
)
226 src
= add(src
, at
.elem
.size
)
228 if skipped
> at
.elem
.size
{
229 skipped
= at
.elem
.size
231 checked
:= at
.elem
.size
- skipped
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
)
244 src
= add(src
, f
.typ
.size
)
246 if skipped
> f
.typ
.size
{
249 checked
:= f
.typ
.size
- skipped