Skip to content

secret,crypto/subtle: make all "bubbles" inherited across goroutines #76477

@aclements

Description

@aclements

Proposal Details

In recent Go releases, we've been growing more types of "goroutine bubbles". As of Go 1.25, we have profile labels, testing/synctest, and crypto/subtle.WithDataIndependentTiming. For Go 1.26, we've accepted secret.Do and are likely to accept fips140.WithoutEnforcement.

However, we've been adding these in a somewhat ad hoc manner. As a result, these are inconsistent in how they're "inherited" if a goroutine within a bubble starts a new goroutine. Currently, synctest and profile label bubbles are inherited. Profile labels, in addition to being inherited, can be passed around via contexts so they can be used temporarily by helper goroutines. However, secret.Do and crypto/subtle.WithDataIndependentTiming aren't inherited at all.

We propose rationalizing these by making secret.Do and crypto/subtle.WithDataIndependentTiming inherited, like the other bubbles.

Rationale

There is no perfect answer here. The main argument for inheritance is that it is more decoupled than the other options: whether or not an API uses goroutines internally must be an implementation detail, and not be part of its API surface. Any form of bubble necessarily chips away at this at least a little because bubbles can reveal whether an implementation hands work off to a pre-existing goroutine, or lazily starts background goroutines. However, inheritance does the right thing for any self-contained parallelism, which is more common, whereas not inheriting exposes internal parallelism in most situations.

The argument for not inheriting for secret.Do and crypto/subtle.WithDataIndependentTiming was that they should only be used very tightly around well-understood code. However, this isn't a strong reason; inheritance just didn't seem useful, and we didn't consider broader similarity.

Profile labels attempt to thread this needle by attaching the bubbled state to a Context. This allows background goroutines to disown the bubbled state, and pre-existing goroutines to opt in to some other bubbled state. However, based on a sampling of Go code, this seems to be rarely used functionality. It's possible there should be a broader mechanism for doing this with other bubbled state, but there almost certainly shouldn't be a per-bubble mechanism.

Alternatives considered

We could make starting a goroutine panic within a secret.Do or crypto/subtle.WithDataIndependentTiming bubble. The argument for this is that it's almost certainly a mistake to start a goroutine in what should be tightly-scoped and tightly-controlled code used in these bubbles. But this runs into the problem of exposing whether or not an API uses concurrency internally.

Compatibility

secret.Do has not yet been released, so there is no compatibility concern to changing it.

crypto/subtle.WithDataIndependentTiming was released in Go 1.25. This would be a change in its semantics. However, it's unlikely to affect existing code, and if it does, the worst case is that additional code would now run with data independent timing, causing it to run slower.

Metadata

Metadata

Assignees

No one assigned

    Labels

    LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolProposalProposal-Accepted

    Type

    No type

    Projects

    Status

    Accepted

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions