Source file src/runtime/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 (amd64 || arm64) && linux 6 7 package runtime 8 9 import ( 10 "internal/goarch" 11 "unsafe" 12 ) 13 14 //go:linkname secret_count runtime/secret.count 15 func secret_count() int32 { 16 return getg().secret 17 } 18 19 //go:linkname secret_inc runtime/secret.inc 20 func secret_inc() { 21 gp := getg() 22 gp.secret++ 23 } 24 25 //go:linkname secret_dec runtime/secret.dec 26 func secret_dec() { 27 gp := getg() 28 gp.secret-- 29 } 30 31 //go:linkname secret_eraseSecrets runtime/secret.eraseSecrets 32 func secret_eraseSecrets() { 33 // zero all the stack memory that might be dirtied with 34 // secrets. We do this from the systemstack so that we 35 // don't have to figure out which holes we have to keep 36 // to ensure that we can return from memclr. gp.sched will 37 // act as a pigeonhole for our actual return. 38 lo := getg().stack.lo 39 systemstack(func() { 40 // Note, this systemstack call happens within the secret mode, 41 // so we don't have to call out to erase our registers, the systemstack 42 // code will do that. 43 mp := acquirem() 44 sp := mp.curg.sched.sp 45 // we need to keep systemstack return on top of the stack being cleared 46 // for traceback 47 sp -= goarch.PtrSize 48 // TODO: keep some sort of low water mark so that we don't have 49 // to zero a potentially large stack if we used just a little 50 // bit of it. That will allow us to use a higher value for 51 // lo than gp.stack.lo. 52 memclrNoHeapPointers(unsafe.Pointer(lo), sp-lo) 53 releasem(mp) 54 }) 55 // Don't put any code here: the stack frame's contents are gone! 56 } 57 58 // addSecret records the fact that we need to zero p immediately 59 // when it is freed. 60 func addSecret(p unsafe.Pointer, size uintptr) { 61 // TODO(dmo): figure out the cost of these. These are mostly 62 // intended to catch allocations that happen via the runtime 63 // that the user has no control over and not big buffers that user 64 // code is allocating. The cost should be relatively low, 65 // but we have run into a wall with other special allocations before. 66 lock(&mheap_.speciallock) 67 s := (*specialSecret)(mheap_.specialSecretAlloc.alloc()) 68 s.special.kind = _KindSpecialSecret 69 s.size = size 70 unlock(&mheap_.speciallock) 71 addspecial(p, &s.special, false) 72 } 73 74 // secret_getStack returns the memory range of the 75 // current goroutine's stack. 76 // For testing only. 77 // Note that this is kind of tricky, as the goroutine can 78 // be copied and/or exit before the result is used, at which 79 // point it may no longer be valid. 80 // 81 //go:linkname secret_getStack runtime/secret.getStack 82 func secret_getStack() (uintptr, uintptr) { 83 gp := getg() 84 return gp.stack.lo, gp.stack.hi 85 } 86 87 // erase any secrets that may have been spilled onto the signal stack during 88 // signal handling. Must be called on g0 or inside STW to make sure we don't 89 // get rescheduled onto a different M. 90 // 91 //go:nosplit 92 func eraseSecretsSignalStk() { 93 mp := getg().m 94 if mp.signalSecret { 95 mp.signalSecret = false 96 // signal handlers get invoked atomically 97 // so it's fine for us to zero out the stack while a signal 98 // might get delivered. Worst case is we are currently running 99 // in secret mode and the signal spills fresh secret info onto 100 // the stack, but since we haven't returned from the secret.Do 101 // yet, we make no guarantees about that information. 102 // 103 // It might be tempting to only erase the part of the signal 104 // stack that has the context, but when running with forwarded 105 // signals, they might pull arbitrary data out of the context and 106 // store it elsewhere on the stack. We can't stop them from storing 107 // the data in arbitrary places, but we can erase the stack where 108 // they are likely to put it in cases of a register spill. 109 size := mp.gsignal.stack.hi - mp.gsignal.stack.lo 110 memclrNoHeapPointers(unsafe.Pointer(mp.gsignal.stack.lo), size) 111 } 112 } 113 114 // return a slice of all Ms signal stacks 115 // For testing only. 116 // 117 //go:linkname secret_appendSignalStacks runtime/secret.appendSignalStacks 118 func secret_appendSignalStacks(sigstacks []stack) []stack { 119 // This is probably overkill, but it's what 120 // doAllThreadsSyscall does 121 stw := stopTheWorld(stwAllThreadsSyscall) 122 allocmLock.lock() 123 acquirem() 124 for mp := allm; mp != nil; mp = mp.alllink { 125 sigstacks = append(sigstacks, mp.gsignal.stack) 126 } 127 releasem(getg().m) 128 allocmLock.unlock() 129 startTheWorld(stw) 130 return sigstacks 131 } 132