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.
113 for i
:= 0; i
< roots
.count
; i
++ {
115 addr
:= uintptr(pr
.decl
)
116 if cgoInRange(src
, addr
, addr
+pr
.size
) {
117 doff
:= uintptr(src
) - addr
118 cgoCheckBits(add(src
, -doff
), pr
.gcdata
, off
+doff
, size
)
125 aoff
:= uintptr(src
) - mheap_
.arena_start
126 idx
:= aoff
>> _PageShift
127 s
:= mheap_
.spans
[idx
]
128 if s
.state
== _MSpanStack
{
129 // There are no heap bits for value stored on the stack.
130 // For a channel receive src might be on the stack of some
131 // other goroutine, so we can't unwind the stack even if
133 // We can't expand the GC program without extra storage
134 // space we can't easily get.
135 // Fortunately we have the type information.
137 cgoCheckUsingType(typ
, src
, off
, size
)
142 // src must be in the regular heap.
144 hbits
:= heapBitsForAddr(uintptr(src
))
145 for i
:= uintptr(0); i
< off
+size
; i
+= sys
.PtrSize
{
147 if i
>= off
&& bits
&bitPointer
!= 0 {
148 v
:= *(*unsafe
.Pointer
)(add(src
, i
))
149 if cgoIsGoPointer(v
) {
151 throw(cgoWriteBarrierFail
)
159 // cgoCheckBits checks the block of memory at src, for up to size
160 // bytes, and throws if it finds a Go pointer. The gcbits mark each
161 // pointer value. The src pointer is off bytes into the gcbits.
164 func cgoCheckBits(src unsafe
.Pointer
, gcbits
*byte, off
, size
uintptr) {
165 skipMask
:= off
/ sys
.PtrSize
/ 8
166 skipBytes
:= skipMask
* sys
.PtrSize
* 8
167 ptrmask
:= addb(gcbits
, skipMask
)
168 src
= add(src
, skipBytes
)
172 for i
:= uintptr(0); i
< size
; i
+= sys
.PtrSize
{
173 if i
&(sys
.PtrSize
*8-1) == 0 {
174 bits
= uint32(*ptrmask
)
175 ptrmask
= addb(ptrmask
, 1)
183 v
:= *(*unsafe
.Pointer
)(add(src
, i
))
184 if cgoIsGoPointer(v
) {
186 throw(cgoWriteBarrierFail
)
194 // cgoCheckUsingType is like cgoCheckTypedBlock, but is a last ditch
195 // fall back to look for pointers in src using the type information.
196 // We only use this when looking at a value on the stack when the type
197 // uses a GC program, because otherwise it's more efficient to use the
198 // GC bits. This is called on the system stack.
201 func cgoCheckUsingType(typ
*_type
, src unsafe
.Pointer
, off
, size
uintptr) {
202 if typ
.kind
&kindNoPointers
!= 0 {
206 // Anything past typ.ptrdata is not a pointer.
207 if typ
.ptrdata
<= off
{
210 if ptrdataSize
:= typ
.ptrdata
- off
; size
> ptrdataSize
{
214 if typ
.kind
&kindGCProg
== 0 {
215 cgoCheckBits(src
, typ
.gcdata
, off
, size
)
218 switch typ
.kind
& kindMask
{
220 throw("can't happen")
222 at
:= (*arraytype
)(unsafe
.Pointer(typ
))
223 for i
:= uintptr(0); i
< at
.len; i
++ {
224 if off
< at
.elem
.size
{
225 cgoCheckUsingType(at
.elem
, src
, off
, size
)
227 src
= add(src
, at
.elem
.size
)
229 if skipped
> at
.elem
.size
{
230 skipped
= at
.elem
.size
232 checked
:= at
.elem
.size
- skipped
240 st
:= (*structtype
)(unsafe
.Pointer(typ
))
241 for _
, f
:= range st
.fields
{
242 if off
< f
.typ
.size
{
243 cgoCheckUsingType(f
.typ
, src
, off
, size
)
245 src
= add(src
, f
.typ
.size
)
247 if skipped
> f
.typ
.size
{
250 checked
:= f
.typ
.size
- skipped