...
Run Format

Source file src/runtime/cgocheck.go

     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.
     4	
     5	// Code to check that pointer writes follow the cgo rules.
     6	// These functions are invoked via the write barrier when debug.cgocheck > 1.
     7	
     8	package runtime
     9	
    10	import (
    11		"runtime/internal/sys"
    12		"unsafe"
    13	)
    14	
    15	const cgoWriteBarrierFail = "Go pointer stored into non-Go memory"
    16	
    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.
    19	//go:nosplit
    20	//go:nowritebarrier
    21	func cgoCheckWriteBarrier(dst *uintptr, src uintptr) {
    22		if !cgoIsGoPointer(unsafe.Pointer(src)) {
    23			return
    24		}
    25		if cgoIsGoPointer(unsafe.Pointer(dst)) {
    26			return
    27		}
    28	
    29		// If we are running on the system stack then dst might be an
    30		// address on the stack, which is OK.
    31		g := getg()
    32		if g == g.m.g0 || g == g.m.gsignal {
    33			return
    34		}
    35	
    36		// Allocating memory can write to various mfixalloc structs
    37		// that look like they are non-Go memory.
    38		if g.m.mallocing != 0 {
    39			return
    40		}
    41	
    42		systemstack(func() {
    43			println("write of Go pointer", hex(src), "to non-Go memory", hex(uintptr(unsafe.Pointer(dst))))
    44			throw(cgoWriteBarrierFail)
    45		})
    46	}
    47	
    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.
    53	//go:nosplit
    54	//go:nowritebarrier
    55	func cgoCheckMemmove(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
    56		if typ.kind&kindNoPointers != 0 {
    57			return
    58		}
    59		if !cgoIsGoPointer(src) {
    60			return
    61		}
    62		if cgoIsGoPointer(dst) {
    63			return
    64		}
    65		cgoCheckTypedBlock(typ, src, off, size)
    66	}
    67	
    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.
    72	//go:nosplit
    73	//go:nowritebarrier
    74	func cgoCheckSliceCopy(typ *_type, dst, src slice, n int) {
    75		if typ.kind&kindNoPointers != 0 {
    76			return
    77		}
    78		if !cgoIsGoPointer(src.array) {
    79			return
    80		}
    81		if cgoIsGoPointer(dst.array) {
    82			return
    83		}
    84		p := src.array
    85		for i := 0; i < n; i++ {
    86			cgoCheckTypedBlock(typ, p, 0, typ.size)
    87			p = add(p, typ.size)
    88		}
    89	}
    90	
    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.
    94	//go:nosplit
    95	//go:nowritebarrier
    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 {
    99			return
   100		}
   101		if ptrdataSize := typ.ptrdata - off; size > ptrdataSize {
   102			size = ptrdataSize
   103		}
   104	
   105		if typ.kind&kindGCProg == 0 {
   106			cgoCheckBits(src, typ.gcdata, off, size)
   107			return
   108		}
   109	
   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)
   115				return
   116			}
   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)
   120				return
   121			}
   122		}
   123	
   124		aoff := uintptr(src) - mheap_.arena_start
   125		idx := aoff >> _PageShift
   126		s := h_spans[idx]
   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
   131			// we wanted to.
   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.
   135			systemstack(func() {
   136				cgoCheckUsingType(typ, src, off, size)
   137			})
   138			return
   139		}
   140	
   141		// src must be in the regular heap.
   142	
   143		hbits := heapBitsForAddr(uintptr(src))
   144		for i := uintptr(0); i < off+size; i += sys.PtrSize {
   145			bits := hbits.bits()
   146			if i >= off && bits&bitPointer != 0 {
   147				v := *(*unsafe.Pointer)(add(src, i))
   148				if cgoIsGoPointer(v) {
   149					systemstack(func() {
   150						throw(cgoWriteBarrierFail)
   151					})
   152				}
   153			}
   154			hbits = hbits.next()
   155		}
   156	}
   157	
   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.
   161	//go:nosplit
   162	//go:nowritebarrier
   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)
   168		off -= skipBytes
   169		size += off
   170		var bits uint32
   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)
   175			} else {
   176				bits >>= 1
   177			}
   178			if off > 0 {
   179				off -= sys.PtrSize
   180			} else {
   181				if bits&1 != 0 {
   182					v := *(*unsafe.Pointer)(add(src, i))
   183					if cgoIsGoPointer(v) {
   184						systemstack(func() {
   185							throw(cgoWriteBarrierFail)
   186						})
   187					}
   188				}
   189			}
   190		}
   191	}
   192	
   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.
   198	//go:nowritebarrier
   199	//go:systemstack
   200	func cgoCheckUsingType(typ *_type, src unsafe.Pointer, off, size uintptr) {
   201		if typ.kind&kindNoPointers != 0 {
   202			return
   203		}
   204	
   205		// Anything past typ.ptrdata is not a pointer.
   206		if typ.ptrdata <= off {
   207			return
   208		}
   209		if ptrdataSize := typ.ptrdata - off; size > ptrdataSize {
   210			size = ptrdataSize
   211		}
   212	
   213		if typ.kind&kindGCProg == 0 {
   214			cgoCheckBits(src, typ.gcdata, off, size)
   215			return
   216		}
   217		switch typ.kind & kindMask {
   218		default:
   219			throw("can't happen")
   220		case kindArray:
   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)
   225				}
   226				src = add(src, at.elem.size)
   227				skipped := off
   228				if skipped > at.elem.size {
   229					skipped = at.elem.size
   230				}
   231				checked := at.elem.size - skipped
   232				off -= skipped
   233				if size <= checked {
   234					return
   235				}
   236				size -= checked
   237			}
   238		case kindStruct:
   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)
   243				}
   244				src = add(src, f.typ.size)
   245				skipped := off
   246				if skipped > f.typ.size {
   247					skipped = f.typ.size
   248				}
   249				checked := f.typ.size - skipped
   250				off -= skipped
   251				if size <= checked {
   252					return
   253				}
   254				size -= checked
   255			}
   256		}
   257	}
   258	

View as plain text