github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/runtime/cgocheck.go (about) 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 "internal/goarch" 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 // 20 // This is called from the write barrier, so its entire call tree must 21 // be nosplit. 22 // 23 //go:nosplit 24 //go:nowritebarrier 25 func cgoCheckWriteBarrier(dst *uintptr, src uintptr) { 26 if !cgoIsGoPointer(unsafe.Pointer(src)) { 27 return 28 } 29 if cgoIsGoPointer(unsafe.Pointer(dst)) { 30 return 31 } 32 33 // If we are running on the system stack then dst might be an 34 // address on the stack, which is OK. 35 gp := getg() 36 if gp == gp.m.g0 || gp == gp.m.gsignal { 37 return 38 } 39 40 // Allocating memory can write to various mfixalloc structs 41 // that look like they are non-Go memory. 42 if gp.m.mallocing != 0 { 43 return 44 } 45 46 // It's OK if writing to memory allocated by persistentalloc. 47 // Do this check last because it is more expensive and rarely true. 48 // If it is false the expense doesn't matter since we are crashing. 49 if inPersistentAlloc(uintptr(unsafe.Pointer(dst))) { 50 return 51 } 52 53 systemstack(func() { 54 println("write of Go pointer", hex(src), "to non-Go memory", hex(uintptr(unsafe.Pointer(dst)))) 55 throw(cgoWriteBarrierFail) 56 }) 57 } 58 59 // cgoCheckMemmove is called when moving a block of memory. 60 // dst and src point off bytes into the value to copy. 61 // size is the number of bytes to copy. 62 // It throws if the program is copying a block that contains a Go pointer 63 // into non-Go memory. 64 // 65 //go:nosplit 66 //go:nowritebarrier 67 func cgoCheckMemmove(typ *_type, dst, src unsafe.Pointer, off, size uintptr) { 68 if typ.ptrdata == 0 { 69 return 70 } 71 if !cgoIsGoPointer(src) { 72 return 73 } 74 if cgoIsGoPointer(dst) { 75 return 76 } 77 cgoCheckTypedBlock(typ, src, off, size) 78 } 79 80 // cgoCheckSliceCopy is called when copying n elements of a slice. 81 // src and dst are pointers to the first element of the slice. 82 // typ is the element type of the slice. 83 // It throws if the program is copying slice elements that contain Go pointers 84 // into non-Go memory. 85 // 86 //go:nosplit 87 //go:nowritebarrier 88 func cgoCheckSliceCopy(typ *_type, dst, src unsafe.Pointer, n int) { 89 if typ.ptrdata == 0 { 90 return 91 } 92 if !cgoIsGoPointer(src) { 93 return 94 } 95 if cgoIsGoPointer(dst) { 96 return 97 } 98 p := src 99 for i := 0; i < n; i++ { 100 cgoCheckTypedBlock(typ, p, 0, typ.size) 101 p = add(p, typ.size) 102 } 103 } 104 105 // cgoCheckTypedBlock checks the block of memory at src, for up to size bytes, 106 // and throws if it finds a Go pointer. The type of the memory is typ, 107 // and src is off bytes into that type. 108 // 109 //go:nosplit 110 //go:nowritebarrier 111 func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) { 112 // Anything past typ.ptrdata is not a pointer. 113 if typ.ptrdata <= off { 114 return 115 } 116 if ptrdataSize := typ.ptrdata - off; size > ptrdataSize { 117 size = ptrdataSize 118 } 119 120 if typ.kind&kindGCProg == 0 { 121 cgoCheckBits(src, typ.gcdata, off, size) 122 return 123 } 124 125 // The type has a GC program. Try to find GC bits somewhere else. 126 for _, datap := range activeModules() { 127 if cgoInRange(src, datap.data, datap.edata) { 128 doff := uintptr(src) - datap.data 129 cgoCheckBits(add(src, -doff), datap.gcdatamask.bytedata, off+doff, size) 130 return 131 } 132 if cgoInRange(src, datap.bss, datap.ebss) { 133 boff := uintptr(src) - datap.bss 134 cgoCheckBits(add(src, -boff), datap.gcbssmask.bytedata, off+boff, size) 135 return 136 } 137 } 138 139 s := spanOfUnchecked(uintptr(src)) 140 if s.state.get() == mSpanManual { 141 // There are no heap bits for value stored on the stack. 142 // For a channel receive src might be on the stack of some 143 // other goroutine, so we can't unwind the stack even if 144 // we wanted to. 145 // We can't expand the GC program without extra storage 146 // space we can't easily get. 147 // Fortunately we have the type information. 148 systemstack(func() { 149 cgoCheckUsingType(typ, src, off, size) 150 }) 151 return 152 } 153 154 // src must be in the regular heap. 155 156 hbits := heapBitsForAddr(uintptr(src), size) 157 for { 158 var addr uintptr 159 if hbits, addr = hbits.next(); addr == 0 { 160 break 161 } 162 v := *(*unsafe.Pointer)(unsafe.Pointer(addr)) 163 if cgoIsGoPointer(v) { 164 throw(cgoWriteBarrierFail) 165 } 166 } 167 } 168 169 // cgoCheckBits checks the block of memory at src, for up to size 170 // bytes, and throws if it finds a Go pointer. The gcbits mark each 171 // pointer value. The src pointer is off bytes into the gcbits. 172 // 173 //go:nosplit 174 //go:nowritebarrier 175 func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) { 176 skipMask := off / goarch.PtrSize / 8 177 skipBytes := skipMask * goarch.PtrSize * 8 178 ptrmask := addb(gcbits, skipMask) 179 src = add(src, skipBytes) 180 off -= skipBytes 181 size += off 182 var bits uint32 183 for i := uintptr(0); i < size; i += goarch.PtrSize { 184 if i&(goarch.PtrSize*8-1) == 0 { 185 bits = uint32(*ptrmask) 186 ptrmask = addb(ptrmask, 1) 187 } else { 188 bits >>= 1 189 } 190 if off > 0 { 191 off -= goarch.PtrSize 192 } else { 193 if bits&1 != 0 { 194 v := *(*unsafe.Pointer)(add(src, i)) 195 if cgoIsGoPointer(v) { 196 throw(cgoWriteBarrierFail) 197 } 198 } 199 } 200 } 201 } 202 203 // cgoCheckUsingType is like cgoCheckTypedBlock, but is a last ditch 204 // fall back to look for pointers in src using the type information. 205 // We only use this when looking at a value on the stack when the type 206 // uses a GC program, because otherwise it's more efficient to use the 207 // GC bits. This is called on the system stack. 208 // 209 //go:nowritebarrier 210 //go:systemstack 211 func cgoCheckUsingType(typ *_type, src unsafe.Pointer, off, size uintptr) { 212 if typ.ptrdata == 0 { 213 return 214 } 215 216 // Anything past typ.ptrdata is not a pointer. 217 if typ.ptrdata <= off { 218 return 219 } 220 if ptrdataSize := typ.ptrdata - off; size > ptrdataSize { 221 size = ptrdataSize 222 } 223 224 if typ.kind&kindGCProg == 0 { 225 cgoCheckBits(src, typ.gcdata, off, size) 226 return 227 } 228 switch typ.kind & kindMask { 229 default: 230 throw("can't happen") 231 case kindArray: 232 at := (*arraytype)(unsafe.Pointer(typ)) 233 for i := uintptr(0); i < at.len; i++ { 234 if off < at.elem.size { 235 cgoCheckUsingType(at.elem, src, off, size) 236 } 237 src = add(src, at.elem.size) 238 skipped := off 239 if skipped > at.elem.size { 240 skipped = at.elem.size 241 } 242 checked := at.elem.size - skipped 243 off -= skipped 244 if size <= checked { 245 return 246 } 247 size -= checked 248 } 249 case kindStruct: 250 st := (*structtype)(unsafe.Pointer(typ)) 251 for _, f := range st.fields { 252 if off < f.typ.size { 253 cgoCheckUsingType(f.typ, src, off, size) 254 } 255 src = add(src, f.typ.size) 256 skipped := off 257 if skipped > f.typ.size { 258 skipped = f.typ.size 259 } 260 checked := f.typ.size - skipped 261 off -= skipped 262 if size <= checked { 263 return 264 } 265 size -= checked 266 } 267 } 268 }