-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Incorrect use of atomic.Value
in the syncutil.Go
utility function causes panic
#916
Comments
I encountered the same panic again a little while later and so I added some print statements to get a better idea of what was going on. It turns out that it occurs when I try to push a manifest using the standard empty JSON config descriptor into a registry that doesn't have that blob already -- this time using the
The second store is causing the panic because I captured all three of these using a version of this function modified as I suggested in my original writeup, which fixed the problem and so also allowed the ORAS CLI to return one of the errors without crashing: // Go concurrently invokes fn on items.
func Go[T any](ctx context.Context, limiter *semaphore.Weighted, fn GoFunc[T], items ...T) error {
type WrapErr struct {
err error
}
eg, egCtx := errgroup.WithContext(ctx)
var egErr atomic.Value
for _, item := range items {
region := LimitRegion(egCtx, limiter)
if err := region.Start(); err != nil {
if egErrWrap, ok := egErr.Load().(WrapErr); ok && egErrWrap.err != nil {
return egErrWrap.err
}
return err
}
eg.Go(func(t T) func() error {
return func() error {
defer region.End()
err := fn(egCtx, region, t)
if err != nil {
fmt.Fprintf(os.Stderr, "%T: %s\n", err, err)
egErr.CompareAndSwap(nil, WrapErr{err})
return err
}
return nil
}
}(item))
}
return eg.Wait()
} In case it's important for reproduction, my local OCI layout directory also lacks the blob for the empty JSON object. I suspect that's what the first of the three errors was complaining about, but I've not confirmed that. |
Hi @apparentlymart , thanks for opening the issue. We will look into it once we have some bandwidth. |
While using ORAS-Go to push a layout with some slightly-incorrect manifest contents I encountered this panic:
Unfortunately I can't share with you the incorrect content since it was just throwaway stuff in a job where I didn't preserve the input artifacts, but I found the relevant code here:
oras-go/internal/syncutil/limit.go
Lines 73 to 92 in 853e012
I think the
egErr.CompareAndSwap
call on line 86 is incorrect, becauseerr
here has an interface type (error
) and so whichever concrete error type is stored there first freezes the type expected for all future calls, per theValue.CompareAndSwap
documentation:If
egErr.CompareAndSwap
gets subsequently called again with a different concrete error type from one of the other goroutines then I expect that this would generate the panic I observed.I think one potential way to make this more robust would be for this function to declare a local struct type to act as the consistent concrete type for
egErr
, with the dynamically-typederror
inside it:Then when loading the error again:
The concrete type in
egErr
would therefore always be exactlyWrappedError
, and so this panic would then be impossible.Again I'm sorry I can't offer a specific reproduction case for this, but I hope the above is useful. I'm not blocked by this because I've now corrected my manifest content so that it doesn't cause these errors.
The text was updated successfully, but these errors were encountered: