]> Cypherpunks repositories - gostls13.git/commitdiff
context: don't return the wrong error when Cause races cancellation
authorDamien Neil <dneil@google.com>
Fri, 6 Jun 2025 22:38:28 +0000 (15:38 -0700)
committerDamien Neil <dneil@google.com>
Tue, 25 Nov 2025 01:02:35 +0000 (17:02 -0800)
Check to see if a context is canceled at all
before checking for the cancellaion cause.
If we can't find a cause, use the original error.

Avoids a data race where we look for a cause,
find none (because the context is not canceled),
the context is canceled,
and we then return ctx.Err() (even though there is now a cause).

Fixes #73390

Change-Id: I97f44aef25c6b02871d987970abfb4c215c5c80e
Reviewed-on: https://go-review.googlesource.com/c/go/+/679835
Reviewed-by: Sean Liao <sean@liao.dev>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
src/context/context.go

index 232fc55d875e37f39e9f60b4a53861f28361ca98..d00ac67e38296573d20520dbd7aceb2fd1a52906 100644 (file)
@@ -286,6 +286,10 @@ func withCancel(parent Context) *cancelCtx {
 // Otherwise Cause(c) returns the same value as c.Err().
 // Cause returns nil if c has not been canceled yet.
 func Cause(c Context) error {
+       err := c.Err()
+       if err == nil {
+               return nil
+       }
        if cc, ok := c.Value(&cancelCtxKey).(*cancelCtx); ok {
                cc.mu.Lock()
                cause := cc.cause
@@ -293,16 +297,12 @@ func Cause(c Context) error {
                if cause != nil {
                        return cause
                }
-               // Either this context is not canceled,
-               // or it is canceled and the cancellation happened in a
-               // custom context implementation rather than a *cancelCtx.
-       }
-       // There is no cancelCtxKey value with a cause, so we know that c is
-       // not a descendant of some canceled Context created by WithCancelCause.
-       // Therefore, there is no specific cause to return.
-       // If this is not one of the standard Context types,
-       // it might still have an error even though it won't have a cause.
-       return c.Err()
+               // The parent cancelCtx doesn't have a cause,
+               // so c must have been canceled in some custom context implementation.
+       }
+       // We don't have a cause to return from a parent cancelCtx,
+       // so return the context's error.
+       return err
 }
 
 // AfterFunc arranges to call f in its own goroutine after ctx is canceled.