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  //   - Any heap allocation done by f is erased as soon as the garbage
    22  //     collector realizes that it is 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  // Limitations:
    28  //   - Currently only supported on linux/amd64 and linux/arm64.  On unsupported
    29  //     platforms, Do will invoke f directly.
    30  //   - Protection does not extend to any global variables written by f.
    31  //   - Any attempt to launch a goroutine by f will result in a panic.
    32  //   - If f calls runtime.Goexit, erasure can be delayed by defers
    33  //     higher up on the call stack.
    34  //   - Heap allocations will only be erased if the program drops all
    35  //     references to those allocations, and then the garbage collector
    36  //     notices that those references are gone. The former is under
    37  //     control of the program, but the latter is at the whim of the
    38  //     runtime.
    39  //   - Any value panicked by f may point to allocations from within
    40  //     f. Those allocations will not be erased until (at least) the
    41  //     panicked value is dead.
    42  //   - Pointer addresses may leak into data buffers used by the runtime
    43  //     to perform garbage collection. Users should not encode confidential
    44  //     information into pointers. For example, if an offset into an array or
    45  //     struct is confidential, then users should not create a pointer into
    46  //     the object. Since this function is intended to be used with constant-time
    47  //     cryptographic code, this requirement is usually fulfilled implicitly.
    48  func Do(f func()) {
    49  	const osArch = runtime.GOOS + "/" + runtime.GOARCH
    50  	switch osArch {
    51  	default:
    52  		// unsupported, just invoke f directly.
    53  		f()
    54  		return
    55  	case "linux/amd64", "linux/arm64":
    56  	}
    57  
    58  	// Place to store any panic value.
    59  	var p any
    60  
    61  	// Step 1: increment the nesting count.
    62  	inc()
    63  
    64  	// Step 2: call helper. The helper just calls f
    65  	// and captures (recovers) any panic result.
    66  	p = doHelper(f)
    67  
    68  	// Step 3: erase everything used by f (stack, registers).
    69  	eraseSecrets()
    70  
    71  	// Step 4: decrement the nesting count.
    72  	dec()
    73  
    74  	// Step 5: re-raise any caught panic.
    75  	// This will make the panic appear to come
    76  	// from a stack whose bottom frame is
    77  	// runtime/secret.Do.
    78  	// Anything below that to do with f will be gone.
    79  	//
    80  	// Note that the panic value is not erased. It behaves
    81  	// like any other value that escapes from f. If it is
    82  	// heap allocated, it will be erased when the garbage
    83  	// collector notices it is no longer referenced.
    84  	if p != nil {
    85  		panic(p)
    86  	}
    87  
    88  	// Note: if f calls runtime.Goexit, step 3 and above will not
    89  	// happen, as Goexit is unrecoverable. We handle that case in
    90  	// runtime/proc.go:goexit0.
    91  }
    92  
    93  func doHelper(f func()) (p any) {
    94  	// Step 2b: Pop the stack up to the secret.doHelper frame
    95  	// if we are in the process of panicking.
    96  	// (It is a no-op if we are not panicking.)
    97  	// We return any panicked value to secret.Do, who will
    98  	// re-panic it.
    99  	defer func() {
   100  		// Note: we rely on the go1.21+ behavior that
   101  		// if we are panicking, recover returns non-nil.
   102  		p = recover()
   103  	}()
   104  
   105  	// Step 2a: call the secret function.
   106  	f()
   107  
   108  	return
   109  }
   110  
   111  // Enabled reports whether [Do] appears anywhere on the call stack.
   112  func Enabled() bool {
   113  	return count() > 0
   114  }
   115  
   116  // implemented in runtime
   117  
   118  //go:linkname count
   119  func count() int32
   120  
   121  //go:linkname inc
   122  func inc()
   123  
   124  //go:linkname dec
   125  func dec()
   126  
   127  //go:linkname eraseSecrets
   128  func eraseSecrets()
   129  

View as plain text