From a3fb92a7100f3f2824d483ee0cbcf1264584b3e4 Mon Sep 17 00:00:00 2001 From: Daniel Morsing Date: Thu, 25 Sep 2025 17:26:03 +0100 Subject: runtime/secret: implement new secret package Implement secret.Do. - When secret.Do returns: - Clear stack that is used by the argument function. - Clear all the registers that might contain secrets. - On stack growth in secret mode, clear the old stack. - When objects are allocated in secret mode, mark them and then zero the marked objects immediately when they are freed. - If the argument function panics, raise that panic as if it originated from secret.Do. This removes anything about the secret function from tracebacks. For now, this is only implemented on linux for arm64 and amd64. This is a rebased version of Keith Randalls initial implementation at CL 600635. I have added arm64 support, signal handling, preemption handling and dealt with vDSOs spilling into system stacks. Fixes #21865 Change-Id: I6fbd5a233beeaceb160785e0c0199a5c94d8e520 Co-authored-by: Keith Randall Reviewed-on: https://go-review.googlesource.com/c/go/+/704615 Reviewed-by: Roland Shoemaker LUCI-TryBot-Result: Go LUCI Auto-Submit: Filippo Valsorda Reviewed-by: Cherry Mui --- src/runtime/stack.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src/runtime/stack.go') diff --git a/src/runtime/stack.go b/src/runtime/stack.go index c92accf188..d1c80276a5 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -8,6 +8,7 @@ import ( "internal/abi" "internal/cpu" "internal/goarch" + "internal/goexperiment" "internal/goos" "internal/runtime/atomic" "internal/runtime/gc" @@ -985,6 +986,16 @@ func copystack(gp *g, newsize uintptr) { } // free old stack + if goexperiment.RuntimeSecret && gp.secret > 0 { + // Some portion of the old stack has secret stuff on it. + // We don't really know where we entered secret mode, + // so just clear the whole thing. + // TODO(dmo): traceback until we hit secret.Do? clearing + // is fast and optimized, might not be worth it. + memclrNoHeapPointers(unsafe.Pointer(old.lo), old.hi-old.lo) + // The memmove call above might put secrets from the stack into registers. + secretEraseRegisters() + } if stackPoisonCopy != 0 { fillstack(old, 0xfc) } @@ -1026,6 +1037,14 @@ func newstack() { } gp := thisg.m.curg + if goexperiment.RuntimeSecret && gp.secret > 0 { + // If we're entering here from a secret context, clear + // all the registers. This is important because we + // might context switch to a different goroutine which + // is not in secret mode, and it will not be careful + // about clearing its registers. + secretEraseRegisters() + } if thisg.m.curg.throwsplit { // Update syscallsp, syscallpc in case traceback uses them. -- cgit v1.3