Source file src/runtime/checkptr.go

     1  // Copyright 2019 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  package runtime
     6  
     7  import "unsafe"
     8  
     9  func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
    10  	// nil pointer is always suitably aligned (#47430).
    11  	if p == nil {
    12  		return
    13  	}
    14  
    15  	// Check that (*[n]elem)(p) is appropriately aligned.
    16  	// Note that we allow unaligned pointers if the types they point to contain
    17  	// no pointers themselves. See issue 37298.
    18  	// TODO(mdempsky): What about fieldAlign?
    19  	if elem.ptrdata != 0 && uintptr(p)&(uintptr(elem.align)-1) != 0 {
    20  		throw("checkptr: misaligned pointer conversion")
    21  	}
    22  
    23  	// Check that (*[n]elem)(p) doesn't straddle multiple heap objects.
    24  	// TODO(mdempsky): Fix #46938 so we don't need to worry about overflow here.
    25  	if checkptrStraddles(p, n*elem.size) {
    26  		throw("checkptr: converted pointer straddles multiple allocations")
    27  	}
    28  }
    29  
    30  // checkptrStraddles reports whether the first size-bytes of memory
    31  // addressed by ptr is known to straddle more than one Go allocation.
    32  func checkptrStraddles(ptr unsafe.Pointer, size uintptr) bool {
    33  	if size <= 1 {
    34  		return false
    35  	}
    36  
    37  	// Check that add(ptr, size-1) won't overflow. This avoids the risk
    38  	// of producing an illegal pointer value (assuming ptr is legal).
    39  	if uintptr(ptr) >= -(size - 1) {
    40  		return true
    41  	}
    42  	end := add(ptr, size-1)
    43  
    44  	// TODO(mdempsky): Detect when [ptr, end] contains Go allocations,
    45  	// but neither ptr nor end point into one themselves.
    46  
    47  	return checkptrBase(ptr) != checkptrBase(end)
    48  }
    49  
    50  func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
    51  	if 0 < uintptr(p) && uintptr(p) < minLegalPointer {
    52  		throw("checkptr: pointer arithmetic computed bad pointer value")
    53  	}
    54  
    55  	// Check that if the computed pointer p points into a heap
    56  	// object, then one of the original pointers must have pointed
    57  	// into the same object.
    58  	base := checkptrBase(p)
    59  	if base == 0 {
    60  		return
    61  	}
    62  
    63  	for _, original := range originals {
    64  		if base == checkptrBase(original) {
    65  			return
    66  		}
    67  	}
    68  
    69  	throw("checkptr: pointer arithmetic result points to invalid allocation")
    70  }
    71  
    72  // checkptrBase returns the base address for the allocation containing
    73  // the address p.
    74  //
    75  // Importantly, if p1 and p2 point into the same variable, then
    76  // checkptrBase(p1) == checkptrBase(p2). However, the converse/inverse
    77  // is not necessarily true as allocations can have trailing padding,
    78  // and multiple variables may be packed into a single allocation.
    79  func checkptrBase(p unsafe.Pointer) uintptr {
    80  	// stack
    81  	if gp := getg(); gp.stack.lo <= uintptr(p) && uintptr(p) < gp.stack.hi {
    82  		// TODO(mdempsky): Walk the stack to identify the
    83  		// specific stack frame or even stack object that p
    84  		// points into.
    85  		//
    86  		// In the mean time, use "1" as a pseudo-address to
    87  		// represent the stack. This is an invalid address on
    88  		// all platforms, so it's guaranteed to be distinct
    89  		// from any of the addresses we might return below.
    90  		return 1
    91  	}
    92  
    93  	// heap (must check after stack because of #35068)
    94  	if base, _, _ := findObject(uintptr(p), 0, 0); base != 0 {
    95  		return base
    96  	}
    97  
    98  	// data or bss
    99  	for _, datap := range activeModules() {
   100  		if datap.data <= uintptr(p) && uintptr(p) < datap.edata {
   101  			return datap.data
   102  		}
   103  		if datap.bss <= uintptr(p) && uintptr(p) < datap.ebss {
   104  			return datap.bss
   105  		}
   106  	}
   107  
   108  	return 0
   109  }
   110  

View as plain text