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
16//go:linkname 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{}, arg interface{}) {
51	if debug.cgocheck == 0 {
52		return
53	}
54
55	ep := efaceOf(&ptr)
56	t := ep._type
57
58	top := true
59	if arg != nil && (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 p == nil || !cgoIsGoPointer(p) {
65			return
66		}
67		aep := efaceOf(&arg)
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.ptrdata == 0 || p == nil {
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 p == nil || !cgoIsGoPointer(p) {
162			return
163		}
164		if !top {
165			panic(errorString(msg))
166		}
167		if st.elem.ptrdata == 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			if f.typ.ptrdata == 0 {
193				continue
194			}
195			cgoCheckArg(f.typ, add(p, f.offset()), true, top, msg)
196		}
197	case kindPtr, kindUnsafePointer:
198		if indir {
199			p = *(*unsafe.Pointer)(p)
200			if p == nil {
201				return
202			}
203		}
204
205		if !cgoIsGoPointer(p) {
206			return
207		}
208		if !top {
209			panic(errorString(msg))
210		}
211
212		cgoCheckUnknownPointer(p, msg)
213	}
214}
215
216// cgoCheckUnknownPointer is called for an arbitrary pointer into Go
217// memory. It checks whether that Go memory contains any other
218// pointer into Go memory. If it does, we panic.
219// The return values are unused but useful to see in panic tracebacks.
220func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) {
221	if inheap(uintptr(p)) {
222		b, span, _ := findObject(uintptr(p), 0, 0, false)
223		base = b
224		if base == 0 {
225			return
226		}
227		hbits := heapBitsForAddr(base)
228		n := span.elemsize
229		for i = uintptr(0); i < n; i += sys.PtrSize {
230			if i != 1*sys.PtrSize && !hbits.morePointers() {
231				// No more possible pointers.
232				break
233			}
234			if hbits.isPointer() && cgoIsGoPointer(*(*unsafe.Pointer)(unsafe.Pointer(base + i))) {
235				panic(errorString(msg))
236			}
237			hbits = hbits.next()
238		}
239
240		return
241	}
242
243	lo := 0
244	hi := len(gcRootsIndex)
245	for lo < hi {
246		m := lo + (hi-lo)/2
247		pr := gcRootsIndex[m]
248		addr := uintptr(pr.decl)
249		if cgoInRange(p, addr, addr+pr.size) {
250			cgoCheckBits(pr.decl, pr.gcdata, 0, pr.ptrdata)
251			return
252		}
253		if uintptr(p) < addr {
254			hi = m
255		} else {
256			lo = m + 1
257		}
258	}
259
260	return
261}
262
263// cgoIsGoPointer reports whether the pointer is a Go pointer--a
264// pointer to Go memory. We only care about Go memory that might
265// contain pointers.
266//go:nosplit
267//go:nowritebarrierrec
268func cgoIsGoPointer(p unsafe.Pointer) bool {
269	if p == nil {
270		return false
271	}
272
273	if inHeapOrStack(uintptr(p)) {
274		return true
275	}
276
277	roots := gcRoots
278	for roots != nil {
279		for i := 0; i < roots.count; i++ {
280			pr := roots.roots[i]
281			addr := uintptr(pr.decl)
282			if cgoInRange(p, addr, addr+pr.size) {
283				return true
284			}
285		}
286		roots = roots.next
287	}
288
289	return false
290}
291
292// cgoInRange reports whether p is between start and end.
293//go:nosplit
294//go:nowritebarrierrec
295func cgoInRange(p unsafe.Pointer, start, end uintptr) bool {
296	return start <= uintptr(p) && uintptr(p) < end
297}
298
299// cgoCheckResult is called to check the result parameter of an
300// exported Go function. It panics if the result is or contains a Go
301// pointer.
302func cgoCheckResult(val interface{}) {
303	if debug.cgocheck == 0 {
304		return
305	}
306
307	ep := efaceOf(&val)
308	t := ep._type
309	cgoCheckArg(t, ep.data, t.kind&kindDirectIface == 0, false, cgoResultFail)
310}
311