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