runtime/secret: implement goroutine inheriting secret state

Updates #76477.

Change-Id: I58726393363f0dc5a83b8a215a9d854d1e0e2c78
Reviewed-on: https://go-review.googlesource.com/c/go/+/728922
Reviewed-by: Junyang Shao <shaojunyang@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
This commit is contained in:
Daniel Morsing 2025-12-10 11:03:45 +00:00
parent e73e73470e
commit e26a373785
3 changed files with 35 additions and 2 deletions

View file

@ -5442,6 +5442,18 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr, parked bool, waitreaso
// dit bubble
newg.ditWanted = callergp.ditWanted
if goexperiment.RuntimeSecret && callergp.secret > 0 {
// while it might seem weird to have a non-zero gp.secret value
// with no calls to secret.Do on the stack, this case is handled
// just fine by the cleanup logic in goexit0
// TODO: secret mode is invisible to the user if they don't ask about it via secret.Enabled
// and can have severe performance penalties (at time of writing, wrapping the entire
// tls handshake resulted in a 30% slowdown of the benchmarks).
// Whether a goroutine is running in secret mode should be more visible,
// maybe with a stack frame or some sort of bubble inspecting mechanism
newg.secret = 1
}
// Set up race context.
if raceenabled {
newg.racectx = racegostart(callerpc)

View file

@ -24,6 +24,9 @@ import (
// that, any panic raised by f will appear as if it originates from
// Do itself.
//
// Any goroutine spawned while executing f will act as if the entire goroutine
// is wrapped inside another call to Do.
//
// Users should be cautious of allocating inside Do.
// Erasing heap memory after Do returns may increase garbage collector sweep times and
// requires additional memory to keep track of allocations until they are to be erased.
@ -39,7 +42,6 @@ import (
// - Currently only supported on linux/amd64 and linux/arm64. On unsupported
// platforms, Do will invoke f directly.
// - Protection does not extend to any global variables written by f.
// - Protection does not extend to any new goroutines made by f.
// - If f calls runtime.Goexit, erasure can be delayed by defers
// higher up on the call stack.
// - Heap allocations will only be erased if the program drops all
@ -119,7 +121,10 @@ func doHelper(f func()) (p any) {
return
}
// Enabled reports whether [Do] appears anywhere on the call stack.
// Enabled reports whether the current goroutine
// is running in secret mode. This is usually through a call to
// [Do], but can also occur when a goroutine already running in
// secret mode launches another goroutine.
func Enabled() bool {
return count() > 0
}

View file

@ -284,6 +284,22 @@ func TestRegisters(t *testing.T) {
}
}
func TestSecretInheritance(t *testing.T) {
ch := make(chan bool, 2)
Do(func() {
ch <- Enabled()
go func() {
ch <- Enabled()
close(ch)
}()
})
for enabled := range ch {
if !enabled {
t.Error("secret mode not enabled for child goroutine")
}
}
}
func TestSignalStacks(t *testing.T) {
Do(func() {
s := makeS()