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.
20 // This is called from the write barrier, so its entire call tree must
25 func cgoCheckWriteBarrier(dst
*uintptr, src
uintptr) {
26 if !cgoIsGoPointer(unsafe
.Pointer(src
)) {
29 if cgoIsGoPointer(unsafe
.Pointer(dst
)) {
33 // If we are running on the system stack then dst might be an
34 // address on the stack, which is OK.
36 if g
== g
.m
.g0 || g
== g
.m
.gsignal
{
40 // Allocating memory can write to various mfixalloc structs
41 // that look like they are non-Go memory.
42 if g
.m
.mallocing
!= 0 {
47 println("write of Go pointer", hex(src
), "to non-Go memory", hex(uintptr(unsafe
.Pointer(dst
))))
48 throw(cgoWriteBarrierFail
)
52 // cgoCheckMemmove is called when moving a block of memory.
53 // dst and src point off bytes into the value to copy.
54 // size is the number of bytes to copy.
55 // It throws if the program is copying a block that contains a Go pointer
56 // into non-Go memory.
59 func cgoCheckMemmove(typ
*_type
, dst
, src unsafe
.Pointer
, off
, size
uintptr) {
60 if typ
.kind
&kindNoPointers
!= 0 {
63 if !cgoIsGoPointer(src
) {
66 if cgoIsGoPointer(dst
) {
69 cgoCheckTypedBlock(typ
, src
, off
, size
)
72 // cgoCheckSliceCopy is called when copying n elements of a slice from
73 // src to dst. typ is the element type of the slice.
74 // It throws if the program is copying slice elements that contain Go pointers
75 // into non-Go memory.
78 func cgoCheckSliceCopy(typ
*_type
, dst
, src slice
, n
int) {
79 if typ
.kind
&kindNoPointers
!= 0 {
82 if !cgoIsGoPointer(src
.array
) {
85 if cgoIsGoPointer(dst
.array
) {
89 for i
:= 0; i
< n
; i
++ {
90 cgoCheckTypedBlock(typ
, p
, 0, typ
.size
)
95 // cgoCheckTypedBlock checks the block of memory at src, for up to size bytes,
96 // and throws if it finds a Go pointer. The type of the memory is typ,
97 // and src is off bytes into that type.
100 func cgoCheckTypedBlock(typ
*_type
, src unsafe
.Pointer
, off
, size
uintptr) {
101 // Anything past typ.ptrdata is not a pointer.
102 if typ
.ptrdata
<= off
{
105 if ptrdataSize
:= typ
.ptrdata
- off
; size
> ptrdataSize
{
109 if typ
.kind
&kindGCProg
== 0 {
110 cgoCheckBits(src
, typ
.gcdata
, off
, size
)
114 // The type has a GC program. Try to find GC bits somewhere else.
117 for i
:= 0; i
< roots
.count
; i
++ {
119 addr
:= uintptr(pr
.decl
)
120 if cgoInRange(src
, addr
, addr
+pr
.size
) {
121 doff
:= uintptr(src
) - addr
122 cgoCheckBits(add(src
, -doff
), pr
.gcdata
, off
+doff
, size
)
129 aoff
:= uintptr(src
) - mheap_
.arena_start
130 idx
:= aoff
>> _PageShift
131 s
:= mheap_
.spans
[idx
]
132 if s
.state
== _MSpanManual
{
133 // There are no heap bits for value stored on the stack.
134 // For a channel receive src might be on the stack of some
135 // other goroutine, so we can't unwind the stack even if
137 // We can't expand the GC program without extra storage
138 // space we can't easily get.
139 // Fortunately we have the type information.
141 cgoCheckUsingType(typ
, src
, off
, size
)
146 // src must be in the regular heap.
148 hbits
:= heapBitsForAddr(uintptr(src
))
149 for i
:= uintptr(0); i
< off
+size
; i
+= sys
.PtrSize
{
151 if i
>= off
&& bits
&bitPointer
!= 0 {
152 v
:= *(*unsafe
.Pointer
)(add(src
, i
))
153 if cgoIsGoPointer(v
) {
155 throw(cgoWriteBarrierFail
)
163 // cgoCheckBits checks the block of memory at src, for up to size
164 // bytes, and throws if it finds a Go pointer. The gcbits mark each
165 // pointer value. The src pointer is off bytes into the gcbits.
168 func cgoCheckBits(src unsafe
.Pointer
, gcbits
*byte, off
, size
uintptr) {
169 skipMask
:= off
/ sys
.PtrSize
/ 8
170 skipBytes
:= skipMask
* sys
.PtrSize
* 8
171 ptrmask
:= addb(gcbits
, skipMask
)
172 src
= add(src
, skipBytes
)
176 for i
:= uintptr(0); i
< size
; i
+= sys
.PtrSize
{
177 if i
&(sys
.PtrSize
*8-1) == 0 {
178 bits
= uint32(*ptrmask
)
179 ptrmask
= addb(ptrmask
, 1)
187 v
:= *(*unsafe
.Pointer
)(add(src
, i
))
188 if cgoIsGoPointer(v
) {
190 throw(cgoWriteBarrierFail
)
198 // cgoCheckUsingType is like cgoCheckTypedBlock, but is a last ditch
199 // fall back to look for pointers in src using the type information.
200 // We only use this when looking at a value on the stack when the type
201 // uses a GC program, because otherwise it's more efficient to use the
202 // GC bits. This is called on the system stack.
205 func cgoCheckUsingType(typ
*_type
, src unsafe
.Pointer
, off
, size
uintptr) {
206 if typ
.kind
&kindNoPointers
!= 0 {
210 // Anything past typ.ptrdata is not a pointer.
211 if typ
.ptrdata
<= off
{
214 if ptrdataSize
:= typ
.ptrdata
- off
; size
> ptrdataSize
{
218 if typ
.kind
&kindGCProg
== 0 {
219 cgoCheckBits(src
, typ
.gcdata
, off
, size
)
222 switch typ
.kind
& kindMask
{
224 throw("can't happen")
226 at
:= (*arraytype
)(unsafe
.Pointer(typ
))
227 for i
:= uintptr(0); i
< at
.len; i
++ {
228 if off
< at
.elem
.size
{
229 cgoCheckUsingType(at
.elem
, src
, off
, size
)
231 src
= add(src
, at
.elem
.size
)
233 if skipped
> at
.elem
.size
{
234 skipped
= at
.elem
.size
236 checked
:= at
.elem
.size
- skipped
244 st
:= (*structtype
)(unsafe
.Pointer(typ
))
245 for _
, f
:= range st
.fields
{
246 if off
< f
.typ
.size
{
247 cgoCheckUsingType(f
.typ
, src
, off
, size
)
249 src
= add(src
, f
.typ
.size
)
251 if skipped
> f
.typ
.size
{
254 checked
:= f
.typ
.size
- skipped