1// Copyright 2009 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// Cgo call and callback support.
6
7package runtime
8
9import (
10	"runtime/internal/sys"
11	"unsafe"
12)
13
14// Functions called by cgo-generated code.
15//go:linkname cgoCheckPointer runtime.cgoCheckPointer
16//go:linkname cgoCheckResult runtime.cgoCheckResult
17
18// Pointer checking for cgo code.
19
20// We want to detect all cases where a program that does not use
21// unsafe makes a cgo call passing a Go pointer to memory that
22// contains a Go pointer. Here a Go pointer is defined as a pointer
23// to memory allocated by the Go runtime. Programs that use unsafe
24// can evade this restriction easily, so we don't try to catch them.
25// The cgo program will rewrite all possibly bad pointer arguments to
26// call cgoCheckPointer, where we can catch cases of a Go pointer
27// pointing to a Go pointer.
28
29// Complicating matters, taking the address of a slice or array
30// element permits the C program to access all elements of the slice
31// or array. In that case we will see a pointer to a single element,
32// but we need to check the entire data structure.
33
34// The cgoCheckPointer call takes additional arguments indicating that
35// it was called on an address expression. An additional argument of
36// true means that it only needs to check a single element. An
37// additional argument of a slice or array means that it needs to
38// check the entire slice/array, but nothing else. Otherwise, the
39// pointer could be anything, and we check the entire heap object,
40// which is conservative but safe.
41
42// When and if we implement a moving garbage collector,
43// cgoCheckPointer will pin the pointer for the duration of the cgo
44// call.  (This is necessary but not sufficient; the cgo program will
45// also have to change to pin Go pointers that cannot point to Go
46// pointers.)
47
48// cgoCheckPointer checks if the argument contains a Go pointer that
49// points to a Go pointer, and panics if it does.
50func cgoCheckPointer(ptr interface{}, args ...interface{}) {
51	if debug.cgocheck == 0 {
52		return
53	}
54
55	ep := (*eface)(unsafe.Pointer(&ptr))
56	t := ep._type
57
58	top := true
59	if len(args) > 0 && (t.kind&kindMask == kindPtr || t.kind&kindMask == kindUnsafePointer) {
60		p := ep.data
61		if t.kind&kindDirectIface == 0 {
62			p = *(*unsafe.Pointer)(p)
63		}
64		if !cgoIsGoPointer(p) {
65			return
66		}
67		aep := (*eface)(unsafe.Pointer(&args[0]))
68		switch aep._type.kind & kindMask {
69		case kindBool:
70			if t.kind&kindMask == kindUnsafePointer {
71				// We don't know the type of the element.
72				break
73			}
74			pt := (*ptrtype)(unsafe.Pointer(t))
75			cgoCheckArg(pt.elem, p, true, false, cgoCheckPointerFail)
76			return
77		case kindSlice:
78			// Check the slice rather than the pointer.
79			ep = aep
80			t = ep._type
81		case kindArray:
82			// Check the array rather than the pointer.
83			// Pass top as false since we have a pointer
84			// to the array.
85			ep = aep
86			t = ep._type
87			top = false
88		default:
89			throw("can't happen")
90		}
91	}
92
93	cgoCheckArg(t, ep.data, t.kind&kindDirectIface == 0, top, cgoCheckPointerFail)
94}
95
96const cgoCheckPointerFail = "cgo argument has Go pointer to Go pointer"
97const cgoResultFail = "cgo result has Go pointer"
98
99// cgoCheckArg is the real work of cgoCheckPointer. The argument p
100// is either a pointer to the value (of type t), or the value itself,
101// depending on indir. The top parameter is whether we are at the top
102// level, where Go pointers are allowed.
103func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) {
104	if t.kind&kindNoPointers != 0 {
105		// If the type has no pointers there is nothing to do.
106		return
107	}
108
109	switch t.kind & kindMask {
110	default:
111		throw("can't happen")
112	case kindArray:
113		at := (*arraytype)(unsafe.Pointer(t))
114		if !indir {
115			if at.len != 1 {
116				throw("can't happen")
117			}
118			cgoCheckArg(at.elem, p, at.elem.kind&kindDirectIface == 0, top, msg)
119			return
120		}
121		for i := uintptr(0); i < at.len; i++ {
122			cgoCheckArg(at.elem, p, true, top, msg)
123			p = add(p, at.elem.size)
124		}
125	case kindChan, kindMap:
126		// These types contain internal pointers that will
127		// always be allocated in the Go heap. It's never OK
128		// to pass them to C.
129		panic(errorString(msg))
130	case kindFunc:
131		if indir {
132			p = *(*unsafe.Pointer)(p)
133		}
134		if !cgoIsGoPointer(p) {
135			return
136		}
137		panic(errorString(msg))
138	case kindInterface:
139		it := *(**_type)(p)
140		if it == nil {
141			return
142		}
143		// A type known at compile time is OK since it's
144		// constant. A type not known at compile time will be
145		// in the heap and will not be OK.
146		if inheap(uintptr(unsafe.Pointer(it))) {
147			panic(errorString(msg))
148		}
149		p = *(*unsafe.Pointer)(add(p, sys.PtrSize))
150		if !cgoIsGoPointer(p) {
151			return
152		}
153		if !top {
154			panic(errorString(msg))
155		}
156		cgoCheckArg(it, p, it.kind&kindDirectIface == 0, false, msg)
157	case kindSlice:
158		st := (*slicetype)(unsafe.Pointer(t))
159		s := (*slice)(p)
160		p = s.array
161		if !cgoIsGoPointer(p) {
162			return
163		}
164		if !top {
165			panic(errorString(msg))
166		}
167		if st.elem.kind&kindNoPointers != 0 {
168			return
169		}
170		for i := 0; i < s.cap; i++ {
171			cgoCheckArg(st.elem, p, true, false, msg)
172			p = add(p, st.elem.size)
173		}
174	case kindString:
175		ss := (*stringStruct)(p)
176		if !cgoIsGoPointer(ss.str) {
177			return
178		}
179		if !top {
180			panic(errorString(msg))
181		}
182	case kindStruct:
183		st := (*structtype)(unsafe.Pointer(t))
184		if !indir {
185			if len(st.fields) != 1 {
186				throw("can't happen")
187			}
188			cgoCheckArg(st.fields[0].typ, p, st.fields[0].typ.kind&kindDirectIface == 0, top, msg)
189			return
190		}
191		for _, f := range st.fields {
192			cgoCheckArg(f.typ, add(p, f.offset()), true, top, msg)
193		}
194	case kindPtr, kindUnsafePointer:
195		if indir {
196			p = *(*unsafe.Pointer)(p)
197		}
198
199		if !cgoIsGoPointer(p) {
200			return
201		}
202		if !top {
203			panic(errorString(msg))
204		}
205
206		cgoCheckUnknownPointer(p, msg)
207	}
208}
209
210// cgoCheckUnknownPointer is called for an arbitrary pointer into Go
211// memory. It checks whether that Go memory contains any other
212// pointer into Go memory. If it does, we panic.
213// The return values are unused but useful to see in panic tracebacks.
214func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) {
215	if cgoInRange(p, mheap_.arena_start, mheap_.arena_used) {
216		if !inheap(uintptr(p)) {
217			// On 32-bit systems it is possible for C's allocated memory
218			// to have addresses between arena_start and arena_used.
219			// Either this pointer is a stack or an unused span or it's
220			// a C allocation. Escape analysis should prevent the first,
221			// garbage collection should prevent the second,
222			// and the third is completely OK.
223			return
224		}
225
226		b, hbits, span, _ := heapBitsForObject(uintptr(p), 0, 0, false)
227		base = b
228		if base == 0 {
229			return
230		}
231		n := span.elemsize
232		for i = uintptr(0); i < n; i += sys.PtrSize {
233			if i != 1*sys.PtrSize && !hbits.morePointers() {
234				// No more possible pointers.
235				break
236			}
237			if hbits.isPointer() && cgoIsGoPointer(*(*unsafe.Pointer)(unsafe.Pointer(base + i))) {
238				panic(errorString(msg))
239			}
240			hbits = hbits.next()
241		}
242
243		return
244	}
245
246	roots := gcRoots
247	for roots != nil {
248		for j := 0; j < roots.count; j++ {
249			pr := roots.roots[j]
250			addr := uintptr(pr.decl)
251			if cgoInRange(p, addr, addr+pr.size) {
252				cgoCheckBits(pr.decl, pr.gcdata, 0, pr.ptrdata)
253				return
254			}
255		}
256		roots = roots.next
257	}
258
259	return
260}
261
262// cgoIsGoPointer returns whether the pointer is a Go pointer--a
263// pointer to Go memory. We only care about Go memory that might
264// contain pointers.
265//go:nosplit
266//go:nowritebarrierrec
267func cgoIsGoPointer(p unsafe.Pointer) bool {
268	if p == nil {
269		return false
270	}
271
272	if inHeapOrStack(uintptr(p)) {
273		return true
274	}
275
276	roots := gcRoots
277	for roots != nil {
278		for i := 0; i < roots.count; i++ {
279			pr := roots.roots[i]
280			addr := uintptr(pr.decl)
281			if cgoInRange(p, addr, addr+pr.size) {
282				return true
283			}
284		}
285		roots = roots.next
286	}
287
288	return false
289}
290
291// cgoInRange returns whether p is between start and end.
292//go:nosplit
293//go:nowritebarrierrec
294func cgoInRange(p unsafe.Pointer, start, end uintptr) bool {
295	return start <= uintptr(p) && uintptr(p) < end
296}
297
298// cgoCheckResult is called to check the result parameter of an
299// exported Go function. It panics if the result is or contains a Go
300// pointer.
301func cgoCheckResult(val interface{}) {
302	if debug.cgocheck == 0 {
303		return
304	}
305
306	ep := (*eface)(unsafe.Pointer(&val))
307	t := ep._type
308	cgoCheckArg(t, ep.data, t.kind&kindDirectIface == 0, false, cgoResultFail)
309}
310