Source file src/runtime/secret/secret.go

     1  // Copyright 2024 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  //go:build goexperiment.runtimesecret
     6  
     7  package secret
     8  
     9  import (
    10  	"runtime"
    11  	_ "unsafe"
    12  )
    13  
    14  // Do invokes f.
    15  //
    16  // Do ensures that any temporary storage used by f is erased in a
    17  // timely manner. (In this context, "f" is shorthand for the
    18  // entire call tree initiated by f.)
    19  //   - Any registers used by f are erased before Do returns.
    20  //   - Any stack used by f is erased before Do returns.
    21  //   - Heap allocations done by f are erased as soon as the garbage
    22  //     collector realizes that all allocated values are no longer reachable.
    23  //   - Do works even if f panics or calls runtime.Goexit.  As part of
    24  //     that, any panic raised by f will appear as if it originates from
    25  //     Do itself.
    26  //
    27  // Any goroutine spawned while executing f will act as if the entire goroutine
    28  // is wrapped inside another call to Do.
    29  //
    30  // Users should be cautious of allocating inside Do.
    31  // Erasing heap memory after Do returns may increase garbage collector sweep times and
    32  // requires additional memory to keep track of allocations until they are to be erased.
    33  // These costs can compound when an allocation is done in the service of growing a value,
    34  // like appending to a slice or inserting into a map. In these cases, the entire new allocation is erased rather
    35  // than just the secret parts of it.
    36  //
    37  // To reduce lifetimes of allocations and avoid unexpected performance issues,
    38  // if a function invoked by Do needs to yield a result that shouldn't be erased,
    39  // it should do so by copying the result into an allocation created by the caller.
    40  //
    41  // Limitations:
    42  //   - Currently only supported on linux/amd64 and linux/arm64.  On unsupported
    43  //     platforms, Do will invoke f directly.
    44  //   - Protection does not extend to any global variables written by f.
    45  //   - If f calls runtime.Goexit, erasure can be delayed by defers
    46  //     higher up on the call stack.
    47  //   - Heap allocations will only be erased if the program drops all
    48  //     references to those allocations, and then the garbage collector
    49  //     notices that those references are gone. The former is under
    50  //     control of the program, but the latter is at the whim of the
    51  //     runtime.
    52  //   - Any value panicked by f may point to allocations from within
    53  //     f. Those allocations will not be erased until (at least) the
    54  //     panicked value is dead.
    55  //   - Pointer addresses may leak into data buffers used by the runtime
    56  //     to perform garbage collection. Users should not encode confidential
    57  //     information into pointers. For example, if an offset into an array or
    58  //     struct is confidential, then users should not create a pointer into
    59  //     the object. Since this function is intended to be used with constant-time
    60  //     cryptographic code, this requirement is usually fulfilled implicitly.
    61  func Do(f func()) {
    62  	const osArch = runtime.GOOS + "/" + runtime.GOARCH
    63  	switch osArch {
    64  	default:
    65  		// unsupported, just invoke f directly.
    66  		f()
    67  		return
    68  	case "linux/amd64", "linux/arm64":
    69  	}
    70  
    71  	// Place to store any panic value.
    72  	var p any
    73  
    74  	// Step 1: increment the nesting count.
    75  	inc()
    76  
    77  	// Step 2: call helper. The helper just calls f
    78  	// and captures (recovers) any panic result.
    79  	p = doHelper(f)
    80  
    81  	// Step 3: erase everything used by f (stack, registers).
    82  	eraseSecrets()
    83  
    84  	// Step 4: decrement the nesting count.
    85  	dec()
    86  
    87  	// Step 5: re-raise any caught panic.
    88  	// This will make the panic appear to come
    89  	// from a stack whose bottom frame is
    90  	// runtime/secret.Do.
    91  	// Anything below that to do with f will be gone.
    92  	//
    93  	// Note that the panic value is not erased. It behaves
    94  	// like any other value that escapes from f. If it is
    95  	// heap allocated, it will be erased when the garbage
    96  	// collector notices it is no longer referenced.
    97  	if p != nil {
    98  		panic(p)
    99  	}
   100  
   101  	// Note: if f calls runtime.Goexit, step 3 and above will not
   102  	// happen, as Goexit is unrecoverable. We handle that case in
   103  	// runtime/proc.go:goexit0.
   104  }
   105  
   106  func doHelper(f func()) (p any) {
   107  	// Step 2b: Pop the stack up to the secret.doHelper frame
   108  	// if we are in the process of panicking.
   109  	// (It is a no-op if we are not panicking.)
   110  	// We return any panicked value to secret.Do, who will
   111  	// re-panic it.
   112  	defer func() {
   113  		// Note: we rely on the go1.21+ behavior that
   114  		// if we are panicking, recover returns non-nil.
   115  		p = recover()
   116  	}()
   117  
   118  	// Step 2a: call the secret function.
   119  	f()
   120  
   121  	return
   122  }
   123  
   124  // Enabled reports whether the current goroutine
   125  // is running in secret mode. This is usually through a call to
   126  // [Do], but can also occur when a goroutine already running in
   127  // secret mode launches another goroutine.
   128  func Enabled() bool {
   129  	return count() > 0
   130  }
   131  
   132  // implemented in runtime
   133  
   134  //go:linkname count
   135  func count() int32
   136  
   137  //go:linkname inc
   138  func inc()
   139  
   140  //go:linkname dec
   141  func dec()
   142  
   143  //go:linkname eraseSecrets
   144  func eraseSecrets()
   145  

View as plain text