gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/go_marshal/analysis/analysis_unsafe.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package analysis implements common functionality used by generated
    16  // go_marshal tests.
    17  package analysis
    18  
    19  // All functions in this package are unsafe and are not intended for general
    20  // consumption. They contain sharp edge cases and the caller is responsible for
    21  // ensuring none of them are hit. Callers must be carefully to pass in only sane
    22  // arguments. Failure to do so may cause panics at best and arbitrary memory
    23  // corruption at worst.
    24  //
    25  // Never use outside of tests.
    26  
    27  import (
    28  	"fmt"
    29  	"math/rand"
    30  	"reflect"
    31  	"testing"
    32  	"unsafe"
    33  )
    34  
    35  // RandomizeValue assigns random value(s) to an abitrary type. This is intended
    36  // for used with ABI structs from go_marshal, meaning the typical restrictions
    37  // apply (fixed-size types, no pointers, maps, channels, etc), and should only
    38  // be used on zeroed values to avoid overwriting pointers to active go objects.
    39  //
    40  // Internally, we populate the type with random data by doing an unsafe cast to
    41  // access the underlying memory of the type and filling it as if it were a byte
    42  // slice. This almost gets us what we want, but padding fields named "_" are
    43  // normally not accessible, so we walk the type and recursively zero all "_"
    44  // fields.
    45  //
    46  // Precondition: x must be a pointer. x must not contain any valid
    47  // pointers to active go objects (pointer fields aren't allowed in ABI
    48  // structs anyways), or we'd be violating the go runtime contract and
    49  // the GC may malfunction.
    50  func RandomizeValue(x any) {
    51  	v := reflect.Indirect(reflect.ValueOf(x))
    52  	if !v.CanSet() {
    53  		panic("RandomizeType() called with an unaddressable value. You probably need to pass a pointer to the argument")
    54  	}
    55  
    56  	// Cast the underlying memory for the type into a byte slice.
    57  	var b []byte
    58  	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
    59  	// Note: v.UnsafeAddr panics if x is passed by value. x should be a pointer.
    60  	hdr.Data = v.UnsafeAddr()
    61  	hdr.Len = int(v.Type().Size())
    62  	hdr.Cap = hdr.Len
    63  
    64  	// Fill the byte slice with random data, which in effect fills the type with
    65  	// random values.
    66  	n, err := rand.Read(b)
    67  	if err != nil || n != len(b) {
    68  		panic("unreachable")
    69  	}
    70  
    71  	// Normally, padding fields are not accessible, so zero them out.
    72  	reflectZeroPaddingFields(v.Type(), b, false)
    73  }
    74  
    75  // reflectZeroPaddingFields assigns zero values to padding fields for the value
    76  // of type r, represented by the memory in data. Padding fields are defined as
    77  // fields with the name "_". If zero is true, the immediate value itself is
    78  // zeroed. In addition, the type is recursively scanned for padding fields in
    79  // inner types.
    80  //
    81  // This is used for zeroing padding fields after calling RandomizeValue.
    82  func reflectZeroPaddingFields(r reflect.Type, data []byte, zero bool) {
    83  	if zero {
    84  		clear(data)
    85  	}
    86  	switch r.Kind() {
    87  	case reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64:
    88  		// These types are explicitly allowed in an ABI type, but we don't need
    89  		// to recurse further as they're scalar types.
    90  	case reflect.Struct:
    91  		for i, numFields := 0, r.NumField(); i < numFields; i++ {
    92  			f := r.Field(i)
    93  			off := f.Offset
    94  			len := f.Type.Size()
    95  			window := data[off : off+len]
    96  			reflectZeroPaddingFields(f.Type, window, f.Name == "_")
    97  		}
    98  	case reflect.Array:
    99  		eLen := int(r.Elem().Size())
   100  		if int(r.Size()) != eLen*r.Len() {
   101  			panic("Array has unexpected size?")
   102  		}
   103  		for i, n := 0, r.Len(); i < n; i++ {
   104  			reflectZeroPaddingFields(r.Elem(), data[i*eLen:(i+1)*eLen], false)
   105  		}
   106  	default:
   107  		panic(fmt.Sprintf("Type %v not allowed in ABI struct", r.Kind()))
   108  
   109  	}
   110  }
   111  
   112  // AlignmentCheck ensures the definition of the type represented by typ doesn't
   113  // cause the go compiler to emit implicit padding between elements of the type
   114  // (i.e. fields in a struct).
   115  //
   116  // AlignmentCheck doesn't explicitly recurse for embedded structs because any
   117  // struct present in an ABI struct must also be Marshallable, and therefore
   118  // they're aligned by definition (or their alignment check would have failed).
   119  func AlignmentCheck(t *testing.T, typ reflect.Type) (ok bool, delta uint64) {
   120  	switch typ.Kind() {
   121  	case reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64:
   122  		// Primitive types are always considered well aligned. Primitive types
   123  		// that are fields in structs are checked independently, this branch
   124  		// exists to handle recursive calls to alignmentCheck.
   125  	case reflect.Struct:
   126  		xOff := 0
   127  		nextXOff := 0
   128  		skipNext := false
   129  		for i, numFields := 0, typ.NumField(); i < numFields; i++ {
   130  			xOff = nextXOff
   131  			f := typ.Field(i)
   132  			fmt.Printf("Checking alignment of %s.%s @ %d [+%d]...\n", typ.Name(), f.Name, f.Offset, f.Type.Size())
   133  			nextXOff = int(f.Offset + f.Type.Size())
   134  
   135  			if f.Name == "_" {
   136  				// Padding fields need not be aligned.
   137  				fmt.Printf("Padding field of type %v\n", f.Type)
   138  				continue
   139  			}
   140  
   141  			if tag, ok := f.Tag.Lookup("marshal"); ok && tag == "unaligned" {
   142  				skipNext = true
   143  				continue
   144  			}
   145  
   146  			if skipNext {
   147  				skipNext = false
   148  				fmt.Printf("Skipping alignment check for field %s.%s explicitly marked as unaligned.\n", typ.Name(), f.Name)
   149  				continue
   150  			}
   151  
   152  			if xOff != int(f.Offset) {
   153  				implicitPad := int(f.Offset) - xOff
   154  				t.Fatalf("Suspect offset for field %s.%s, detected an implicit %d byte padding from offset %d to %d; either add %d bytes of explicit padding before this field or tag it as `marshal:\"unaligned\"`.", typ.Name(), f.Name, implicitPad, xOff, f.Offset, implicitPad)
   155  			}
   156  		}
   157  
   158  		// Ensure structs end on a byte explicitly defined by the type.
   159  		if typ.NumField() > 0 && nextXOff != int(typ.Size()) {
   160  			implicitPad := int(typ.Size()) - nextXOff
   161  			f := typ.Field(typ.NumField() - 1) // Final field
   162  			if tag, ok := f.Tag.Lookup("marshal"); ok && tag == "unaligned" {
   163  				// Final field explicitly marked unaligned.
   164  				break
   165  			}
   166  			t.Fatalf("Suspect offset for field %s.%s at the end of %s, detected an implicit %d byte padding from offset %d to %d at the end of the struct; either add %d bytes of explict padding at end of the struct or tag the final field %s as `marshal:\"unaligned\"`.",
   167  				typ.Name(), f.Name, typ.Name(), implicitPad, nextXOff, typ.Size(), implicitPad, f.Name)
   168  		}
   169  	case reflect.Array:
   170  		// Independent arrays are also always considered well aligned. We only
   171  		// need to worry about their alignment when they're embedded in structs,
   172  		// which we handle above.
   173  	default:
   174  		t.Fatalf("Unsupported type in ABI struct while checking for field alignment for type: %v", typ.Kind())
   175  	}
   176  	return true, uint64(typ.Size())
   177  }