Repository: moby/buildkit

Issue:
push auth fails when 5 to 10 Minutes after pull auth (with Workload Identity in GCP)
### Contributing guidelines and issue reporting guide

- [x] I've read the [contributing guidelines](https://github.com/moby/buildkit/blob/master/.github/CONTRIBUTING.md) and wholeheartedly agree. I've also read the [issue reporting guide](https://github.com/moby/buildkit/blob/master/.github/issue_reporting_guide.md).

### Well-formed report checklist

- [x] I have found a bug that the documentation does not mention anything about my problem
- [x] I have found a bug that there are no open or closed issues that are related to my problem
- [x] I have provided version/information about my environment and done my best to provide a reproducer

### Description of bug

## Bug description

Using Google Artifact Registry and Workload Identity for authentication:
Image pushes fail due to auth fail if the push is exactly 5 Minutes to 10 Minutes after the cache pull. With following Error:
```
Error: buildx failed with: ERROR: failed to solve: error writing layer blob: failed to authorize: failed to fetch oauth token: unexpected status from GET request to https://europe-west3-docker.pkg.dev/v2/token?scope=repository%3A__our-project__%2F__our-registry__%2Ftest-nginx-image%3Apull%2Cpush&service=europe-west3-docker.pkg.dev: 401 Unauthorized
```

Error seems from [authprovider.go#L140](https://github.com/moby/buildkit/blob/master/session/auth/authprovider/authprovider.go#L140) and the issue could result from [authprovider.go#L62](https://github.com/moby/buildkit/blob/master/session/auth/authprovider/authprovider.go#L62).
I tried to change the code to:
```go
// Tokens for Google Artifact Registry via Workload Identity expire after 5 minutes
return time.Since(created) > 5*time.Minute-10*time.Second
```
But these changes (also tried to change the log) are not reflected after I build the buildkit image and use it in buildx.

## Reproduction

1. Docker Registry in Google Artifact Registry
2. Run the build via Github Actions
3. Use Workload Identity Federation for authentication against Google Services from GH workflow run
4. use buildx
5. The build needs to have two `[auth]`
   1. first tiggered due to `--cache-from`: `[auth] .../test-nginx-image:pull token for europe-west3-docker.pkg.dev`
   2. second trigered due to `--cache-to` or `--push`: `[auth] .../test-nginx-image:pull,push token for europe-west3-docker.pkg.dev`
6. The second [auth] needs to be more than 5 Minutes, but less than 10 Minutes after the first [auth].
   1. At the beginning it was a complicate Dockerfile, but a simple build with `sleep 270` works, `sleep 300` fails and `sleep 600` (and much more) works fine again.

- Using other Authentication mechanism works fine. 
- I tried several sleeps, also before the build, seems really only related to a single `docker buildx build` call.
- As workaround we can do a build without push, followed by a build with push. The `--cache-from` can stay in the second call, as everything is cached there is no [auth] for the remote cache needed (or in the log) during the second run.

## Version information

```bash
/usr/bin/docker buildx version
  github.com/docker/buildx v0.21.3 7b5fecbd7a62d73843f7a73a6d4ec353c0555ef5
/usr/bin/docker buildx inspect --bootstrap --builder builder-db441f8f-6bde-49ee-b10d-ccac2e79b5c6
  #1 [internal] booting buildkit
  #1 pulling image moby/buildkit:buildx-stable-1
  #1 pulling image moby/buildkit:buildx-stable-1 4.9s done
  #1 creating container buildx_buildkit_builder-db441f8f-6bde-49ee-b10d-ccac2e79b5c60
  #1 creating container buildx_buildkit_builder-db441f8f-6bde-49ee-b10d-ccac2e79b5c60 13.0s done
  #1 DONE 18.0s
  Name:          builder-db441f8f-6bde-49ee-b10d-ccac2e79b5c6
  Driver:        docker-container
  Last Activity: 2025-03-18 18:15:21 +0000 UTC
  
  Nodes:
  Name:                  builder-db441f8f-6bde-49ee-b10d-ccac2e79b5c60
  Endpoint:              unix:///run/docker/docker.sock
  Status:                running
  BuildKit daemon flags: --debug --allow-insecure-entitlement=network.host
  BuildKit version:      v0.20.1
  Platforms:             linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
  Labels:
   org.mobyproject.buildkit.worker.executor:         oci
   org.mobyproject.buildkit.worker.hostname:         56024517c3e3
   org.mobyproject.buildkit.worker.network:          host
   org.mobyproject.buildkit.worker.oci.process-mode: sandbox
   org.mobyproject.buildkit.worker.selinux.enabled:  false
   org.mobyproject.buildkit.worker.snapshotter:      overlayfs
  GC Policy rule#0:
   All:            false
   Filters:        type==source.local,type==exec.cachemount,type==source.git.checkout
   Keep Duration:  48h0m0s
   Max Used Space: 488.3MiB
  GC Policy rule#1:
   All:            false
   Keep Duration:  [144](..../actions/runs/1644262/job/10093592#step:5:149)0h0m0s
   Reserved Space: 9.313GiB
   Max Used Space: 93.13GiB
   Min Free Space: 36.32GiB
  GC Policy rule#2:
   All:            false
   Reserved Space: 9.313GiB
   Max Used Space: 93.13GiB
   Min Free Space: 36.32GiB
  GC Policy rule#3:
   All:            true
   Reserved Space: 9.313GiB
   Max Used Space: 93.13GiB
   Min Free Space: 36.32GiB
/usr/bin/docker version
  Client:
   Version:           28.0.1
   API version:       1.48
   Go version:        go1.23.6
   Git commit:        068a01e
   Built:             Wed Feb 26 10:40:04 2025
   OS/Arch:           linux/amd64
   Context:           default
  
  Server: Docker Engine - Community
   Engine:
    Version:          28.0.1
    API version:      1.48 (minimum version 1.[24](......./actions/runs/1644262/job/10093592#step:5:25))
    Go version:       go1.23.6
    Git commit:       bbd0a17
    Built:            Wed Feb 26 10:41:19 20[25](......./actions/runs/1644262/job/10093592#step:5:26)
    OS/Arch:          linux/amd64
    Experimental:     false
   containerd:
    Version:          v1.7.25
    GitCommit:        bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
   runc:
    Version:          1.2.5
    GitCommit:        v1.2.5-0-g59923ef
   docker-init:
    Version:          0.19.0
    GitCommit:        de40ad0
  /usr/bin/docker info
  Client:
   Version:    28.0.1
   Context:    default
   Debug Mode: false
   Plugins:
    buildx: Docker Buildx (Docker Inc.)
      Version:  v0.21.3
      Path:     /usr/local/lib/docker/cli-plugins/docker-buildx
    compose: Docker Compose (Docker Inc.)
      Version:  v2.34.0
      Path:     /usr/local/lib/docker/cli-plugins/docker-compose
  
  Server:
   Containers: 0
    Running: 0
    Paused: 0
    Stopped: 0
   Images: 0
   Server Version: 28.0.1
   Storage Driver: overlay2
    Backing Filesystem: extfs
    Supports d_type: true
    Using metacopy: false
    Native Overlay Diff: true
    userxattr: true
   Logging Driver: json-file
   Cgroup Driver: cgroupfs
   Cgroup Version: 2
   Plugins:
    Volume: local
    Network: bridge host ipvlan macvlan null overlay
    Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
   Swarm: inactive
   Runtimes: io.containerd.runc.v2 runc
   Default Runtime: runc
   Init Binary: docker-init
   containerd version: bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
   runc version: v1.2.5-0-g59923ef
   init version: de40ad0
   Security Options:
    seccomp
     Profile: builtin
    cgroupns
   Kernel Version: 5.15.0-1073-gke
   Operating System: Alpine Linux v3.21
   OSType: linux
   Architecture: x86_64
   CPUs: 16
   Total Memory: 125.8GiB
   Name: gcp-tiny-qfhqz-runner-wdnfq
   ID: efccffe4-c154-4c21-8d4c-1cdb57c2dceb
   Docker Root Dir: /var/lib/docker
   Debug Mode: false
   Experimental: false
   Insecure Registries:
    ::1/128
    1[27](....../actions/runs/1644262/job/10093592#step:5:28).0.0.0/8
   Registry Mirrors:
    https://mirror.gcr.io/
    https://...../
   Live Restore Enabled: false
   Product License: Community Engine
```


Hints from issue comments:
@tonistiigi: > and the issue could result from [authprovider.go#L62](https://github.com/moby/buildkit/blob/master/session/auth/authprovider/authprovider.go#L62).

> Tokens for Google Artifact Registry via Workload Identity expire after 5 minutes

Note that there are two timeouts here. When the registry asks for an authentication token, buildx will contact the auth service with your credentials to pull one. This token has an expiration time as a field, and buildkit/buildx will refresh it and get a new one if the previous token gets close to expiration. This is in https://github.com/moby/buildkit/blob/master/util/resolver/authorizer.go#L330 .

The other case is that for some credential helpers, the credentials themselves are not static and will expire (there are multiple levels of credentials). Because there is significant overhead in the credential helpers, buildx will cache them. When the token expires, build will try to generate a new one but may receive an error because cached credentials don't work anymore. This is the 10min cache expiration time you are pointing to.

You can try with a custom build that changes the timeout to 5min. If it works, we can consider making it configurable or lowering the default. If the error returned from the token endpoint is typed we could also consider a fix where we try again with uncached credentials. Note that this is used in client side so if you are using `buildx` you need to update the https://github.com/moby/buildkit/blob/master/session/auth/authprovider/authprovider.go#L62 in buildx repository.
@MichaelKorn: Thanks a lot @tonistiigi for the quick reply.
I had seen the code that processes the actual expiration of tokens. The problem here is that we are dealing with "id_tokens" and these are somewhat tricky. They are characterized by being very short-lived, confusion exists about how long they actually live (we have observed 5, 10, 15, and 60 minutes at different places), and that these tokens themselves are opaque (Google offers an endpoint where you get some information if you send the id_token there).
And if you use Workload Identity, then you have "id_tokens" everywhere.

In the meantime, my colleague @nobbs has manipulated buildx in a way that ensures that ultimately at [the relevant place](https://github.com/moby/buildkit/blob/master/session/auth/authprovider/authprovider.go#L61) we have a 5-minute timeout (a few seconds less would be even better, as I indicated above): https://github.com/nobbs/buildx/commit/444aa016f3588a51159d2cc3450d52c8830dc88e
We have now been able to confirm in several tests that with this change the issue is resolved for us.

Should I create a PR with the changed proposed in the issue description?@tonistiigi: > Should I create a PR with the changed proposed in the issue description?

Yes, lowering the default to ~5min seems ok. But make the change in buildkit instead of overriding in buildx so all clients have a better default.

FYI @cpuguy83 

Solution patch:
diff --git a/.github/workflows/.test.yml b/.github/workflows/.test.yml
index 05f216985..586fc9b9c 100644
--- a/.github/workflows/.test.yml
+++ b/.github/workflows/.test.yml
@@ -1,6 +1,10 @@
 # reusable workflow
 name: .test
 
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
+permissions:
+  contents: read
+
 on:
   workflow_call:
     inputs:
diff --git a/.github/workflows/buildkit.yml b/.github/workflows/buildkit.yml
index c17f80e8e..4af1fd0be 100644
--- a/.github/workflows/buildkit.yml
+++ b/.github/workflows/buildkit.yml
@@ -4,6 +4,10 @@ concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
+permissions:
+  contents: read
+
 on:
   schedule:
     - cron: '0 10 * * *'
@@ -150,10 +154,10 @@ jobs:
   govulncheck:
     runs-on: ubuntu-24.04
     permissions:
+      # same as global permission
+      contents: read
       # required to write sarif report
       security-events: write
-      # required to check out the repository
-      contents: read
     steps:
       -
         name: Set up Docker Buildx
@@ -226,6 +230,8 @@ jobs:
     runs-on: ubuntu-24.04
     if: ${{ github.ref == 'refs/heads/master' && github.repository == 'moby/buildkit' }}
     permissions:
+      # same as global permission
+      contents: read
       # required to write sarif report
       security-events: write
     needs:
@@ -266,6 +272,9 @@ jobs:
 
   release:
     runs-on: ubuntu-24.04
+    permissions:
+      # required to create GitHub release
+      contents: write
     needs:
       - prepare
       - test
diff --git a/.github/workflows/buildx-image.yml b/.github/workflows/buildx-image.yml
index f3f00e3b9..a6c316517 100644
--- a/.github/workflows/buildx-image.yml
+++ b/.github/workflows/buildx-image.yml
@@ -13,6 +13,10 @@ concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
+permissions:
+  contents: read
+
 on:
   workflow_dispatch:
     inputs:
diff --git a/.github/workflows/dockerd.yml b/.github/workflows/dockerd.yml
index b323ac404..6d76fb26f 100644
--- a/.github/workflows/dockerd.yml
+++ b/.github/workflows/dockerd.yml
@@ -1,5 +1,9 @@
 name: dockerd
 
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
+permissions:
+  contents: read
+
 on:
   # TODO: add event to build on command in PR (e.g., /test-dockerd)
   workflow_dispatch:
diff --git a/.github/workflows/docs-upstream.yml b/.github/workflows/docs-upstream.yml
index 0c9d18e7d..4762d64c8 100644
--- a/.github/workflows/docs-upstream.yml
+++ b/.github/workflows/docs-upstream.yml
@@ -7,6 +7,10 @@ concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
+permissions:
+  contents: read
+
 on:
   push:
     branches:
diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml
index ff5ffece8..95e619cdd 100644
--- a/.github/workflows/frontend.yml
+++ b/.github/workflows/frontend.yml
@@ -4,6 +4,10 @@ concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
+permissions:
+  contents: read
+
 on:
   workflow_dispatch:
   push:
@@ -130,6 +134,8 @@ jobs:
     runs-on: ubuntu-24.04
     if: ${{ github.ref == 'refs/heads/master' && github.repository == 'moby/buildkit' }}
     permissions:
+      # same as global permission
+      contents: read
       # required to write sarif report
       security-events: write
     needs:
@@ -171,6 +177,9 @@ jobs:
   release:
     runs-on: ubuntu-24.04
     if: startsWith(github.ref, 'refs/tags/dockerfile')
+    permissions:
+      # required to create GitHub release
+      contents: write
     needs:
       - prepare
       - test
diff --git a/.github/workflows/test-os.yml b/.github/workflows/test-os.yml
index 101313020..dbc2ea586 100644
--- a/.github/workflows/test-os.yml
+++ b/.github/workflows/test-os.yml
@@ -4,6 +4,10 @@ concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
+permissions:
+  contents: read
+
 on:
   schedule:
     - cron: '0 10 * * *'
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 4bc88855e..a99017106 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -4,6 +4,10 @@ concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
+# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
+permissions:
+  contents: read
+
 on:
   workflow_dispatch:
   push:
diff --git a/cache/refs.go b/cache/refs.go
index 5f5692b42..0d8f58c26 100644
--- a/cache/refs.go
+++ b/cache/refs.go
@@ -32,6 +32,7 @@ import (
 	"github.com/moby/buildkit/util/overlay"
 	"github.com/moby/buildkit/util/progress"
 	rootlessmountopts "github.com/moby/buildkit/util/rootless/mountopts"
+	"github.com/moby/buildkit/util/tracing"
 	"github.com/moby/buildkit/util/winlayers"
 	"github.com/moby/sys/mountinfo"
 	"github.com/moby/sys/userns"
@@ -39,6 +40,8 @@ import (
 	ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/trace"
 	"golang.org/x/sync/errgroup"
 )
 
@@ -1308,6 +1311,11 @@ func (sr *immutableRef) unlazyLayer(ctx context.Context, dhs DescHandlers, pg pr
 		statusDone := pg.Status("extracting "+desc.Digest.String(), "extracting")
 		defer statusDone()
 	}
+	sp, ctx := tracing.StartSpan(ctx, "extract", trace.WithAttributes(
+		attribute.String("digest", desc.Digest.String()),
+		attribute.Int("size", int(desc.Size)),
+	))
+	defer sp.End()
 
 	key := fmt.Sprintf("extract-%s %s", identity.NewID(), sr.getChainID())
 
diff --git a/client/solve.go b/client/solve.go
index 897a95ee6..557c4886f 100644
--- a/client/solve.go
+++ b/client/solve.go
@@ -359,7 +359,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
 		}
 		for _, storePath := range storesToUpdate {
 			names := []ociindex.NameOrTag{ociindex.Tag("latest")}
-			if t, ok := res.ExporterResponse["image.name"]; ok {
+			if t, ok := res.ExporterResponse[exptypes.ExporterImageNameKey]; ok {
 				inp := strings.Split(t, ",")
 				names = make([]ociindex.NameOrTag, len(inp))
 				for i, n := range inp {
diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go
index 7c4136b53..58d2623b4 100644
--- a/exporter/containerimage/export.go
+++ b/exporter/containerimage/export.go
@@ -149,6 +149,16 @@ func (e *imageExporter) Resolve(ctx context.Context, id int, opt map[string]stri
 			i.storeAllowIncomplete = b
 		case exptypes.OptKeyDanglingPrefix:
 			i.danglingPrefix = v
+		case exptypes.OptKeyDanglingEmptyOnly:
+			if v == "" {
+				i.danglingEmptyOnly = true
+				continue
+			}
+			b, err := strconv.ParseBool(v)
+			if err != nil {
+				return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
+			}
+			i.danglingEmptyOnly = b
 		case exptypes.OptKeyNameCanonical:
 			if v == "" {
 				i.nameCanonical = true
@@ -183,6 +193,7 @@ type imageExporterInstance struct {
 	insecure             bool
 	nameCanonical        bool
 	danglingPrefix       string
+	danglingEmptyOnly    bool
 	meta                 map[string][]byte
 }
 
@@ -247,9 +258,14 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
 	}
 
 	nameCanonical := e.nameCanonical
-	if e.opts.ImageName == "" && e.danglingPrefix != "" {
-		e.opts.ImageName = e.danglingPrefix + "@" + desc.Digest.String()
-		nameCanonical = false
+	if e.danglingPrefix != "" && (!e.danglingEmptyOnly || e.opts.ImageName == "") {
+		danglingImageName := e.danglingPrefix + "@" + desc.Digest.String()
+		if e.opts.ImageName != "" {
+			e.opts.ImageName += "," + danglingImageName
+		} else {
+			e.opts.ImageName = danglingImageName
+			nameCanonical = false
+		}
 	}
 
 	if e.opts.ImageName != "" {
@@ -274,7 +290,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
 				}
 
 				sfx := []string{""}
-				if nameCanonical {
+				if nameCanonical && !strings.ContainsRune(targetName, '@') {
 					sfx = append(sfx, "@"+desc.Digest.String())
 				}
 				for _, sfx := range sfx {
@@ -344,7 +360,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
 				}
 			}
 		}
-		resp["image.name"] = e.opts.ImageName
+		resp[exptypes.ExporterImageNameKey] = e.opts.ImageName
 	}
 
 	resp[exptypes.ExporterImageDigestKey] = desc.Digest.String()
diff --git a/exporter/containerimage/exptypes/keys.go b/exporter/containerimage/exptypes/keys.go
index 8eb2064e1..7cfa8dc02 100644
--- a/exporter/containerimage/exptypes/keys.go
+++ b/exporter/containerimage/exptypes/keys.go
@@ -26,11 +26,17 @@ var (
 	// Value: bool <true|false>
 	OptKeyUnpack ImageExporterOptKey = "unpack"
 
-	// Fallback image name prefix if image name isn't provided.
-	// If used, image will be named as <value>@<digest>
+	// Image name prefix to be used for tagging a dangling image.
+	// If used, image will be named as <value>@<digest> in addition
+	// to any other specified names.
 	// Value: string
 	OptKeyDanglingPrefix ImageExporterOptKey = "dangling-name-prefix"
 
+	// Only use the dangling image name as a fallback if image name isn't provided.
+	// Ignored if dangling-name-prefix is not set.
+	// Value: bool <true|false>
+	OptKeyDanglingEmptyOnly ImageExporterOptKey = "danging-name-empty-only"
+
 	// Creates additional image name with format <name>@<digest>
 	// Value: bool <true|false>
 	OptKeyNameCanonical ImageExporterOptKey = "name-canonical"
diff --git a/exporter/containerimage/exptypes/types.go b/exporter/containerimage/exptypes/types.go
index 056485b66..d7cc98b94 100644
--- a/exporter/containerimage/exptypes/types.go
+++ b/exporter/containerimage/exptypes/types.go
@@ -9,6 +9,7 @@ import (
 
 const (
 	ExporterConfigDigestKey      = "config.digest"
+	ExporterImageNameKey         = "image.name"
 	ExporterImageDigestKey       = "containerimage.digest"
 	ExporterImageConfigKey       = "containerimage.config"
 	ExporterImageConfigDigestKey = "containerimage.config.digest"
diff --git a/exporter/oci/export.go b/exporter/oci/export.go
index 69eabfebb..3db1278fb 100644
--- a/exporter/oci/export.go
+++ b/exporter/oci/export.go
@@ -204,7 +204,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
 	}
 
 	if len(names) != 0 {
-		resp["image.name"] = strings.Join(names, ",")
+		resp[exptypes.ExporterImageNameKey] = strings.Join(names, ",")
 	}
 
 	expOpts := []archiveexporter.ExportOpt{archiveexporter.WithManifest(*desc, names...)}
@@ -298,7 +298,7 @@ func normalizedNames(name string) ([]string, error) {
 		return nil, nil
 	}
 	names := strings.Split(name, ",")
-	var tagNames = make([]string, len(names))
+	tagNames := make([]string, len(names))
 	for i, name := range names {
 		parsed, err := reference.ParseNormalizedNamed(name)
 		if err != nil {
diff --git a/go.mod b/go.mod
index fd599cf4f..d9fa554f3 100644
--- a/go.mod
+++ b/go.mod
@@ -32,8 +32,8 @@ require (
 	github.com/containernetworking/plugins v1.5.1
 	github.com/coreos/go-systemd/v22 v22.5.0
 	github.com/distribution/reference v0.6.0
-	github.com/docker/cli v28.0.1+incompatible
-	github.com/docker/docker v28.0.1+incompatible
+	github.com/docker/cli v28.0.2+incompatible
+	github.com/docker/docker v28.0.2+incompatible
 	github.com/docker/go-connections v0.5.0
 	github.com/docker/go-units v0.5.0
 	github.com/gofrs/flock v0.12.1
@@ -108,7 +108,7 @@ require (
 	google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
 	google.golang.org/protobuf v1.35.2
 	kernel.org/pub/linux/libs/security/libcap/cap v1.2.73
-	tags.cncf.io/container-device-interface v0.8.1
+	tags.cncf.io/container-device-interface v1.0.1
 )
 
 require (
@@ -151,7 +151,7 @@ require (
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
+	github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
 	github.com/google/uuid v1.6.0 // indirect
@@ -183,7 +183,7 @@ require (
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 // indirect
 	sigs.k8s.io/yaml v1.4.0 // indirect
-	tags.cncf.io/container-device-interface/specs-go v0.8.0 // indirect
+	tags.cncf.io/container-device-interface/specs-go v1.0.0 // indirect
 )
 
 exclude (
diff --git a/go.sum b/go.sum
index d605d1a9d..6a3d3655f 100644
--- a/go.sum
+++ b/go.sum
@@ -144,10 +144,10 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi
 github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
 github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
 github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs=
-github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
-github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/cli v28.0.2+incompatible h1:cRPZ77FK3/IXTAIQQj1vmhlxiLS5m+MIUDwS6f57lrE=
+github.com/docker/cli v28.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8=
+github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
 github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@@ -185,8 +185,8 @@ github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeH
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
-github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
+github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
@@ -590,7 +590,7 @@ kernel.org/pub/linux/libs/security/libcap/psx v1.2.73/go.mod h1:+l6Ee2F59XiJ2I6W
 sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
 sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
 sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
-tags.cncf.io/container-device-interface v0.8.1 h1:c0jN4Mt6781jD67NdPajmZlD1qrqQyov/Xfoab37lj0=
-tags.cncf.io/container-device-interface v0.8.1/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
-tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
-tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=
+tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=
+tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0=
+tags.cncf.io/container-device-interface/specs-go v1.0.0 h1:8gLw29hH1ZQP9K1YtAzpvkHCjjyIxHZYzBAvlQ+0vD8=
+tags.cncf.io/container-device-interface/specs-go v1.0.0/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ=
diff --git a/session/auth/authprovider/authprovider.go b/session/auth/authprovider/authprovider.go
index 0280e96dc..54e458e53 100644
--- a/session/auth/authprovider/authprovider.go
+++ b/session/auth/authprovider/authprovider.go
@@ -45,7 +45,7 @@ type DockerAuthProviderConfig struct {
 	TLSConfigs map[string]*AuthTLSConfig
 	// ExpireCachedAuth is a function that returns true auth config should be refreshed
 	// instead of using a pre-cached result.
-	// If nil then the cached result will expire after 10 minutes.
+	// If nil then the cached result will expire after 4 minutes and 50 seconds.
 	// The function is called with the time the cached auth config was created
 	// and the server URL the auth config is for.
 	ExpireCachedAuth func(created time.Time, serverURL string) bool
@@ -59,7 +59,8 @@ type authConfigCacheEntry struct {
 func NewDockerAuthProvider(cfg DockerAuthProviderConfig) session.Attachable {
 	if cfg.ExpireCachedAuth == nil {
 		cfg.ExpireCachedAuth = func(created time.Time, _ string) bool {
-			return time.Since(created) > 10*time.Minute
+			// Tokens for Google Artifact Registry via Workload Identity expire after 5 minutes.
+			return time.Since(created) > 4*time.Minute+50*time.Second
 		}
 	}
 	return &authProvider{
diff --git a/solver/llbsolver/vertex.go b/solver/llbsolver/vertex.go
index d2973d9dd..c80345403 100644
--- a/solver/llbsolver/vertex.go
+++ b/solver/llbsolver/vertex.go
@@ -136,6 +136,11 @@ func ValidateEntitlements(ent entitlements.Set, cdiManager *cdidevices.Manager)
 
 				var allowedDevices []*pb.CDIDevice
 				var nonAliasedDevices []*pb.CDIDevice
+				for _, d := range cdiManager.ListDevices() {
+					if d.OnDemand && d.AutoAllow {
+						allowedDevices = append(allowedDevices, &pb.CDIDevice{Name: d.Name})
+					}
+				}
 				if cfg != nil {
 					for _, d := range op.Exec.CdiDevices {
 						if newName, ok := cfg.Devices[d.Name]; ok && newName != "" {
diff --git a/vendor/github.com/docker/cli/cli/config/config.go b/vendor/github.com/docker/cli/cli/config/config.go
index 910b3c006..daf504333 100644
--- a/vendor/github.com/docker/cli/cli/config/config.go
+++ b/vendor/github.com/docker/cli/cli/config/config.go
@@ -69,6 +69,11 @@ func getHomeDir() string {
 	return home
 }
 
+// Provider defines an interface for providing the CLI config.
+type Provider interface {
+	ConfigFile() *configfile.ConfigFile
+}
+
 // Dir returns the directory the configuration file is stored in
 func Dir() string {
 	initConfigDir.Do(func() {
diff --git a/vendor/github.com/docker/docker/libnetwork/resolvconf/resolvconf.go b/vendor/github.com/docker/docker/libnetwork/resolvconf/resolvconf.go
index 4805b0ead..3be2bff6c 100644
--- a/vendor/github.com/docker/docker/libnetwork/resolvconf/resolvconf.go
+++ b/vendor/github.com/docker/docker/libnetwork/resolvconf/resolvconf.go
@@ -5,7 +5,6 @@ import (
 	"bytes"
 	"net/netip"
 	"os"
-	"strings"
 
 	"github.com/docker/docker/libnetwork/internal/resolvconf"
 	"github.com/opencontainers/go-digest"
@@ -127,31 +126,33 @@ func GetOptions(resolvConf []byte) []string {
 //
 // Note that the resolv.conf file is written, but the hash file is not.
 func Build(path string, nameservers, dnsSearch, dnsOptions []string) (*File, error) {
-	content := bytes.NewBuffer(nil)
-	if len(dnsSearch) > 0 {
-		if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." {
-			if _, err := content.WriteString("search " + searchString + "\n"); err != nil {
-				return nil, err
-			}
-		}
-	}
-	for _, dns := range nameservers {
-		if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil {
+	var ns []netip.Addr
+	for _, addr := range nameservers {
+		ipAddr, err := netip.ParseAddr(addr)
+		if err != nil {
 			return nil, err
 		}
+		ns = append(ns, ipAddr)
 	}
-	if len(dnsOptions) > 0 {
-		if optsString := strings.Join(dnsOptions, " "); strings.Trim(optsString, " ") != "" {
-			if _, err := content.WriteString("options " + optsString + "\n"); err != nil {
-				return nil, err
-			}
-		}
+	rc := resolvconf.ResolvConf{}
+	rc.OverrideNameServers(ns)
+	rc.OverrideSearch(dnsSearch)
+	rc.OverrideOptions(dnsOptions)
+
+	content, err := rc.Generate(false)
+	if err != nil {
+		return nil, err
 	}
 
-	if err := os.WriteFile(path, content.Bytes(), 0o644); err != nil {
+	// Write the resolv.conf file - it's bind-mounted into the container, so can't
+	// move a temp file into place, just have to truncate and write it.
+	//
+	// TODO(thaJeztah): the Build function is currently only used by BuildKit, which only uses "File.Content", and doesn't require the file to be written.
+	if err := os.WriteFile(path, content, 0o644); err != nil {
 		return nil, err
 	}
 
-	hash := digest.FromBytes(content.Bytes())
-	return &File{Content: content.Bytes(), Hash: []byte(hash)}, nil
+	// TODO(thaJeztah): the Build function is currently only used by BuildKit, which does not use the Hash
+	hash := digest.FromBytes(content)
+	return &File{Content: content, Hash: []byte(hash)}, nil
 }
diff --git a/vendor/github.com/docker/docker/pkg/archive/archive.go b/vendor/github.com/docker/docker/pkg/archive/archive.go
index b05780406..9bbb11c19 100644
--- a/vendor/github.com/docker/docker/pkg/archive/archive.go
+++ b/vendor/github.com/docker/docker/pkg/archive/archive.go
@@ -236,11 +236,9 @@ func (r *readCloserWrapper) Close() error {
 	return nil
 }
 
-var (
-	bufioReader32KPool = &sync.Pool{
-		New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
-	}
-)
+var bufioReader32KPool = &sync.Pool{
+	New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
+}
 
 type bufferedReader struct {
 	buf *bufio.Reader
@@ -252,17 +250,17 @@ func newBufferedReader(r io.Reader) *bufferedReader {
 	return &bufferedReader{buf}
 }
 
-func (r *bufferedReader) Read(p []byte) (n int, err error) {
+func (r *bufferedReader) Read(p []byte) (int, error) {
 	if r.buf == nil {
 		return 0, io.EOF
 	}
-	n, err = r.buf.Read(p)
+	n, err := r.buf.Read(p)
 	if err == io.EOF {
 		r.buf.Reset(nil)
 		bufioReader32KPool.Put(r.buf)
 		r.buf = nil
 	}
-	return
+	return n, err
 }
 
 func (r *bufferedReader) Peek(n int) ([]byte, error) {
@@ -428,7 +426,7 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
 					pipeWriter.CloseWithError(err)
 					return
 				}
-				if _, err := copyWithBuffer(tarWriter, tarReader); err != nil {
+				if err := copyWithBuffer(tarWriter, tarReader); err != nil {
 					pipeWriter.CloseWithError(err)
 					return
 				}
@@ -731,7 +729,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
 			return err
 		}
 
-		_, err = copyWithBuffer(ta.TarWriter, file)
+		err = copyWithBuffer(ta.TarWriter, file)
 		file.Close()
 		if err != nil {
 			return err
@@ -778,11 +776,11 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
 		if err != nil {
 			return err
 		}
-		if _, err := copyWithBuffer(file, reader); err != nil {
-			file.Close()
+		if err := copyWithBuffer(file, reader); err != nil {
+			_ = file.Close()
 			return err
 		}
-		file.Close()
+		_ = file.Close()
 
 	case tar.TypeBlock, tar.TypeChar:
 		if inUserns { // cannot create devices in a userns
@@ -1438,7 +1436,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
 			if err := tw.WriteHeader(hdr); err != nil {
 				return err
 			}
-			if _, err := copyWithBuffer(tw, srcF); err != nil {
+			if err := copyWithBuffer(tw, srcF); err != nil {
 				return err
 			}
 			return nil
diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_linux.go b/vendor/github.com/docker/docker/pkg/archive/archive_linux.go
index 631d2e3c5..7b6c3e02b 100644
--- a/vendor/github.com/docker/docker/pkg/archive/archive_linux.go
+++ b/vendor/github.com/docker/docker/pkg/archive/archive_linux.go
@@ -20,7 +20,7 @@ func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter {
 
 type overlayWhiteoutConverter struct{}
 
-func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) {
+func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, _ error) {
 	// convert whiteouts to AUFS format
 	if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
 		// we just rename the file and make it normal
@@ -31,38 +31,41 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
 		hdr.Size = 0
 	}
 
-	if fi.Mode()&os.ModeDir != 0 {
-		opaqueXattrName := "trusted.overlay.opaque"
-		if userns.RunningInUserNS() {
-			opaqueXattrName = "user.overlay.opaque"
-		}
+	if fi.Mode()&os.ModeDir == 0 {
+		// FIXME(thaJeztah): return a sentinel error instead of nil, nil
+		return nil, nil
+	}
 
-		// convert opaque dirs to AUFS format by writing an empty file with the prefix
-		opaque, err := lgetxattr(path, opaqueXattrName)
-		if err != nil {
-			return nil, err
-		}
-		if len(opaque) == 1 && opaque[0] == 'y' {
-			delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
-
-			// create a header for the whiteout file
-			// it should inherit some properties from the parent, but be a regular file
-			wo = &tar.Header{
-				Typeflag:   tar.TypeReg,
-				Mode:       hdr.Mode & int64(os.ModePerm),
-				Name:       filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
-				Size:       0,
-				Uid:        hdr.Uid,
-				Uname:      hdr.Uname,
-				Gid:        hdr.Gid,
-				Gname:      hdr.Gname,
-				AccessTime: hdr.AccessTime,
-				ChangeTime: hdr.ChangeTime,
-			}
-		}
+	opaqueXattrName := "trusted.overlay.opaque"
+	if userns.RunningInUserNS() {
+		opaqueXattrName = "user.overlay.opaque"
 	}
 
-	return
+	// convert opaque dirs to AUFS format by writing an empty file with the prefix
+	opaque, err := lgetxattr(path, opaqueXattrName)
+	if err != nil {
+		return nil, err
+	}
+	if len(opaque) != 1 || opaque[0] != 'y' {
+		// FIXME(thaJeztah): return a sentinel error instead of nil, nil
+		return nil, nil
+	}
+	delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
+
+	// create a header for the whiteout file
+	// it should inherit some properties from the parent, but be a regular file
+	return &tar.Header{
+		Typeflag:   tar.TypeReg,
+		Mode:       hdr.Mode & int64(os.ModePerm),
+		Name:       filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
+		Size:       0,
+		Uid:        hdr.Uid,
+		Uname:      hdr.Uname,
+		Gid:        hdr.Gid,
+		Gname:      hdr.Gname,
+		AccessTime: hdr.AccessTime,
+		ChangeTime: hdr.ChangeTime,
+	}, nil
 }
 
 func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_unix.go b/vendor/github.com/docker/docker/pkg/archive/archive_unix.go
index 9c70d1789..bc6b25ae0 100644
--- a/vendor/github.com/docker/docker/pkg/archive/archive_unix.go
+++ b/vendor/github.com/docker/docker/pkg/archive/archive_unix.go
@@ -73,14 +73,13 @@ func statUnix(fi os.FileInfo, hdr *tar.Header) error {
 	return nil
 }
 
-func getInodeFromStat(stat interface{}) (inode uint64, err error) {
+func getInodeFromStat(stat interface{}) (uint64, error) {
 	s, ok := stat.(*syscall.Stat_t)
-
-	if ok {
-		inode = s.Ino
+	if !ok {
+		// FIXME(thaJeztah): this should likely return an error; see https://github.com/moby/moby/pull/49493#discussion_r1979152897
+		return 0, nil
 	}
-
-	return
+	return s.Ino, nil
 }
 
 func getFileUIDGID(stat interface{}) (idtools.Identity, error) {
diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_windows.go b/vendor/github.com/docker/docker/pkg/archive/archive_windows.go
index 031608162..fd2546eab 100644
--- a/vendor/github.com/docker/docker/pkg/archive/archive_windows.go
+++ b/vendor/github.com/docker/docker/pkg/archive/archive_windows.go
@@ -48,9 +48,9 @@ func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (
 	return
 }
 
-func getInodeFromStat(stat interface{}) (inode uint64, err error) {
+func getInodeFromStat(stat interface{}) (uint64, error) {
 	// do nothing. no notion of Inode in stat on Windows
-	return
+	return 0, nil
 }
 
 // handleTarTypeBlockCharFifo is an OS-specific helper function used by
diff --git a/vendor/github.com/docker/docker/pkg/archive/changes.go b/vendor/github.com/docker/docker/pkg/archive/changes.go
index 79c810a68..1c0509d03 100644
--- a/vendor/github.com/docker/docker/pkg/archive/changes.go
+++ b/vendor/github.com/docker/docker/pkg/archive/changes.go
@@ -83,7 +83,7 @@ func aufsMetadataSkip(path string) (skip bool, err error) {
 	if err != nil {
 		skip = true
 	}
-	return
+	return skip, err
 }
 
 func aufsDeletedFile(root, path string, fi os.FileInfo) (string, error) {
diff --git a/vendor/github.com/docker/docker/pkg/archive/copy.go b/vendor/github.com/docker/docker/pkg/archive/copy.go
index cddf18ecd..cae0173de 100644
--- a/vendor/github.com/docker/docker/pkg/archive/copy.go
+++ b/vendor/github.com/docker/docker/pkg/archive/copy.go
@@ -25,11 +25,11 @@ var copyPool = sync.Pool{
 	New: func() interface{} { s := make([]byte, 32*1024); return &s },
 }
 
-func copyWithBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
+func copyWithBuffer(dst io.Writer, src io.Reader) error {
 	buf := copyPool.Get().(*[]byte)
-	written, err = io.CopyBuffer(dst, src, *buf)
+	_, err := io.CopyBuffer(dst, src, *buf)
 	copyPool.Put(buf)
-	return
+	return err
 }
 
 // PreserveTrailingDotOrSeparator returns the given cleaned path (after
@@ -105,13 +105,13 @@ func TarResource(sourceInfo CopyInfo) (content io.ReadCloser, err error) {
 
 // TarResourceRebase is like TarResource but renames the first path element of
 // items in the resulting tar archive to match the given rebaseName if not "".
-func TarResourceRebase(sourcePath, rebaseName string) (content io.ReadCloser, err error) {
+func TarResourceRebase(sourcePath, rebaseName string) (content io.ReadCloser, _ error) {
 	sourcePath = normalizePath(sourcePath)
-	if _, err = os.Lstat(sourcePath); err != nil {
+	if _, err := os.Lstat(sourcePath); err != nil {
 		// Catches the case where the source does not exist or is not a
 		// directory if asserted to be a directory, as this also causes an
 		// error.
-		return
+		return nil, err
 	}
 
 	// Separate the source path between its directory and
@@ -442,11 +442,12 @@ func CopyTo(content io.Reader, srcInfo CopyInfo, dstPath string) error {
 // whether to follow symbol link or not, if followLink is true, resolvedPath will return
 // link target of any symbol link file, else it will only resolve symlink of directory
 // but return symbol link file itself without resolving.
-func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseName string, err error) {
+func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseName string, _ error) {
 	if followLink {
+		var err error
 		resolvedPath, err = filepath.EvalSymlinks(path)
 		if err != nil {
-			return
+			return "", "", err
 		}
 
 		resolvedPath, rebaseName = GetRebaseName(path, resolvedPath)
@@ -454,10 +455,9 @@ func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseNa
 		dirPath, basePath := filepath.Split(path)
 
 		// if not follow symbol link, then resolve symbol link of parent dir
-		var resolvedDirPath string
-		resolvedDirPath, err = filepath.EvalSymlinks(dirPath)
+		resolvedDirPath, err := filepath.EvalSymlinks(dirPath)
 		if err != nil {
-			return
+			return "", "", err
 		}
 		// resolvedDirPath will have been cleaned (no trailing path separators) so
 		// we can manually join it with the base path element.
diff --git a/vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go b/vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go
index 8ce83bd0b..5bfdfa2f1 100644
--- a/vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go
+++ b/vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go
@@ -17,12 +17,13 @@ func chtimes(name string, atime time.Time, mtime time.Time) error {
 	return os.Chtimes(name, atime, mtime)
 }
 
-func timeToTimespec(time time.Time) (ts unix.Timespec) {
+func timeToTimespec(time time.Time) unix.Timespec {
 	if time.IsZero() {
 		// Return UTIME_OMIT special value
-		ts.Sec = 0
-		ts.Nsec = (1 << 30) - 2
-		return
+		return unix.Timespec{
+			Sec:  0,
+			Nsec: (1 << 30) - 2,
+		}
 	}
 	return unix.NsecToTimespec(time.UnixNano())
 }
diff --git a/vendor/github.com/docker/docker/pkg/archive/wrap.go b/vendor/github.com/docker/docker/pkg/archive/wrap.go
index 903befd76..f8a97254e 100644
--- a/vendor/github.com/docker/docker/pkg/archive/wrap.go
+++ b/vendor/github.com/docker/docker/pkg/archive/wrap.go
@@ -45,8 +45,8 @@ func Generate(input ...string) (io.Reader, error) {
 	return buf, nil
 }
 
-func parseStringPairs(input ...string) (output [][2]string) {
-	output = make([][2]string, 0, len(input)/2+1)
+func parseStringPairs(input ...string) [][2]string {
+	output := make([][2]string, 0, len(input)/2+1)
 	for i := 0; i < len(input); i += 2 {
 		var pair [2]string
 		pair[0] = input[i]
@@ -55,5 +55,5 @@ func parseStringPairs(input ...string) (output [][2]string) {
 		}
 		output = append(output, pair)
 	}
-	return
+	return output
 }
diff --git a/vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go b/vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go
index cbbe835bb..abf462753 100644
--- a/vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go
+++ b/vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go
@@ -11,12 +11,12 @@ import (
 // destination path. Writing and closing concurrently is not allowed.
 // NOTE: umask is not considered for the file's permissions.
 func New(filename string, perm os.FileMode) (io.WriteCloser, error) {
-	f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
+	abspath, err := filepath.Abs(filename)
 	if err != nil {
 		return nil, err
 	}
 
-	abspath, err := filepath.Abs(filename)
+	f, err := os.CreateTemp(filepath.Dir(abspath), ".tmp-"+filepath.Base(filename))
 	if err != nil {
 		return nil, err
 	}
diff --git a/vendor/github.com/docker/docker/pkg/chrootarchive/diff_unix.go b/vendor/github.com/docker/docker/pkg/chrootarchive/diff_unix.go
index e214e9a3d..be1110dcc 100644
--- a/vendor/github.com/docker/docker/pkg/chrootarchive/diff_unix.go
+++ b/vendor/github.com/docker/docker/pkg/chrootarchive/diff_unix.go
@@ -13,7 +13,7 @@ import (
 // applyLayerHandler parses a diff in the standard layer format from `layer`, and
 // applies it to the directory `dest`. Returns the size in bytes of the
 // contents of the layer.
-func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions, decompress bool) (size int64, err error) {
+func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions, decompress bool) (size int64, _ error) {
 	if decompress {
 		decompressed, err := archive.DecompressStream(layer)
 		if err != nil {
diff --git a/vendor/github.com/golang-jwt/jwt/v5/README.md b/vendor/github.com/golang-jwt/jwt/v5/README.md
index 964598a31..0bb636f22 100644
--- a/vendor/github.com/golang-jwt/jwt/v5/README.md
+++ b/vendor/github.com/golang-jwt/jwt/v5/README.md
@@ -10,11 +10,11 @@ implementation of [JSON Web
 Tokens](https://datatracker.ietf.org/doc/html/rfc7519).
 
 Starting with [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0)
-this project adds Go module support, but maintains backwards compatibility with
+this project adds Go module support, but maintains backward compatibility with
 older `v3.x.y` tags and upstream `github.com/dgrijalva/jwt-go`. See the
 [`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information. Version
 v5.0.0 introduces major improvements to the validation of tokens, but is not
-entirely backwards compatible. 
+entirely backward compatible. 
 
 > After the original author of the library suggested migrating the maintenance
 > of `jwt-go`, a dedicated team of open source maintainers decided to clone the
@@ -24,7 +24,7 @@ entirely backwards compatible.
 
 
 **SECURITY NOTICE:** Some older versions of Go have a security issue in the
-crypto/elliptic. Recommendation is to upgrade to at least 1.15 See issue
+crypto/elliptic. The recommendation is to upgrade to at least 1.15 See issue
 [dgrijalva/jwt-go#216](https://github.com/dgrijalva/jwt-go/issues/216) for more
 detail.
 
@@ -32,7 +32,7 @@ detail.
 what you
 expect](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/).
 This library attempts to make it easy to do the right thing by requiring key
-types match the expected alg, but you should take the extra step to verify it in
+types to match the expected alg, but you should take the extra step to verify it in
 your usage.  See the examples provided.
 
 ### Supported Go versions
@@ -41,7 +41,7 @@ Our support of Go versions is aligned with Go's [version release
 policy](https://golang.org/doc/devel/release#policy). So we will support a major
 version of Go until there are two newer major releases. We no longer support
 building jwt-go with unsupported Go versions, as these contain security
-vulnerabilities which will not be fixed.
+vulnerabilities that will not be fixed.
 
 ## What the heck is a JWT?
 
@@ -117,7 +117,7 @@ notable differences:
 
 This library is considered production ready.  Feedback and feature requests are
 appreciated.  The API should be considered stable.  There should be very few
-backwards-incompatible changes outside of major version updates (and only with
+backward-incompatible changes outside of major version updates (and only with
 good reason).
 
 This project uses [Semantic Versioning 2.0.0](http://semver.org).  Accepted pull
@@ -125,8 +125,8 @@ requests will land on `main`.  Periodically, versions will be tagged from
 `main`.  You can find all the releases on [the project releases
 page](https://github.com/golang-jwt/jwt/releases).
 
-**BREAKING CHANGES:*** A full list of breaking changes is available in
-`VERSION_HISTORY.md`.  See `MIGRATION_GUIDE.md` for more information on updating
+**BREAKING CHANGES:** A full list of breaking changes is available in
+`VERSION_HISTORY.md`.  See [`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information on updating
 your code.
 
 ## Extensions
diff --git a/vendor/github.com/golang-jwt/jwt/v5/SECURITY.md b/vendor/github.com/golang-jwt/jwt/v5/SECURITY.md
index b08402c34..2740597f1 100644
--- a/vendor/github.com/golang-jwt/jwt/v5/SECURITY.md
+++ b/vendor/github.com/golang-jwt/jwt/v5/SECURITY.md
@@ -2,11 +2,11 @@
 
 ## Supported Versions
 
-As of February 2022 (and until this document is updated), the latest version `v4` is supported.
+As of November 2024 (and until this document is updated), the latest version `v5` is supported. In critical cases, we might supply back-ported patches for `v4`.
 
 ## Reporting a Vulnerability
 
-If you think you found a vulnerability, and even if you are not sure, please report it to jwt-go-security@googlegroups.com or one of the other [golang-jwt maintainers](https://github.com/orgs/golang-jwt/people). Please try be explicit, describe steps to reproduce the security issue with code example(s).
+If you think you found a vulnerability, and even if you are not sure, please report it a [GitHub Security Advisory](https://github.com/golang-jwt/jwt/security/advisories/new). Please try be explicit, describe steps to reproduce the security issue with code example(s).
 
 You will receive a response within a timely manner. If the issue is confirmed, we will do our best to release a patch as soon as possible given the complexity of the problem.
 
diff --git a/vendor/github.com/golang-jwt/jwt/v5/parser.go b/vendor/github.com/golang-jwt/jwt/v5/parser.go
index ecf99af78..054c7eb6f 100644
--- a/vendor/github.com/golang-jwt/jwt/v5/parser.go
+++ b/vendor/github.com/golang-jwt/jwt/v5/parser.go
@@ -8,6 +8,8 @@ import (
 	"strings"
 )
 
+const tokenDelimiter = "."
+
 type Parser struct {
 	// If populated, only these methods will be considered valid.
 	validMethods []string
@@ -136,9 +138,10 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
 // It's only ever useful in cases where you know the signature is valid (since it has already
 // been or will be checked elsewhere in the stack) and you want to extract values from it.
 func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) {
-	parts = strings.Split(tokenString, ".")
-	if len(parts) != 3 {
-		return nil, parts, newError("token contains an invalid number of segments", ErrTokenMalformed)
+	var ok bool
+	parts, ok = splitToken(tokenString)
+	if !ok {
+		return nil, nil, newError("token contains an invalid number of segments", ErrTokenMalformed)
 	}
 
 	token = &Token{Raw: tokenString}
@@ -196,6 +199,33 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
 	return token, parts, nil
 }
 
+// splitToken splits a token string into three parts: header, claims, and signature. It will only
+// return true if the token contains exactly two delimiters and three parts. In all other cases, it
+// will return nil parts and false.
+func splitToken(token string) ([]string, bool) {
+	parts := make([]string, 3)
+	header, remain, ok := strings.Cut(token, tokenDelimiter)
+	if !ok {
+		return nil, false
+	}
+	parts[0] = header
+	claims, remain, ok := strings.Cut(remain, tokenDelimiter)
+	if !ok {
+		return nil, false
+	}
+	parts[1] = claims
+	// One more cut to ensure the signature is the last part of the token and there are no more
+	// delimiters. This avoids an issue where malicious input could contain additional delimiters
+	// causing unecessary overhead parsing tokens.
+	signature, _, unexpected := strings.Cut(remain, tokenDelimiter)
+	if unexpected {
+		return nil, false
+	}
+	parts[2] = signature
+
+	return parts, true
+}
+
 // DecodeSegment decodes a JWT specific base64url encoding. This function will
 // take into account whether the [Parser] is configured with additional options,
 // such as [WithStrictDecoding] or [WithPaddingAllowed].
diff --git a/vendor/github.com/golang-jwt/jwt/v5/token.go b/vendor/github.com/golang-jwt/jwt/v5/token.go
index 352873a2d..9c7f4ab01 100644
--- a/vendor/github.com/golang-jwt/jwt/v5/token.go
+++ b/vendor/github.com/golang-jwt/jwt/v5/token.go
@@ -75,7 +75,7 @@ func (t *Token) SignedString(key interface{}) (string, error) {
 }
 
 // SigningString generates the signing string.  This is the most expensive part
-// of the whole deal.  Unless you need this for something special, just go
+// of the whole deal. Unless you need this for something special, just go
 // straight for the SignedString.
 func (t *Token) SigningString() (string, error) {
 	h, err := json.Marshal(t.Header)
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 6c204246c..2616c59ef 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -486,14 +486,14 @@ github.com/dimchansky/utfbom
 # github.com/distribution/reference v0.6.0
 ## explicit; go 1.20
 github.com/distribution/reference
-# github.com/docker/cli v28.0.1+incompatible
+# github.com/docker/cli v28.0.2+incompatible
 ## explicit
 github.com/docker/cli/cli/config
 github.com/docker/cli/cli/config/configfile
 github.com/docker/cli/cli/config/credentials
 github.com/docker/cli/cli/config/types
 github.com/docker/cli/cli/connhelper/commandconn
-# github.com/docker/docker v28.0.1+incompatible
+# github.com/docker/docker v28.0.2+incompatible
 ## explicit
 github.com/docker/docker/internal/mounttree
 github.com/docker/docker/internal/unshare
@@ -539,7 +539,7 @@ github.com/gofrs/flock
 # github.com/gogo/protobuf v1.3.2
 ## explicit; go 1.15
 github.com/gogo/protobuf/proto
-# github.com/golang-jwt/jwt/v5 v5.2.1
+# github.com/golang-jwt/jwt/v5 v5.2.2
 ## explicit; go 1.18
 github.com/golang-jwt/jwt/v5
 # github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
@@ -1121,12 +1121,12 @@ kernel.org/pub/linux/libs/security/libcap/psx
 ## explicit; go 1.12
 sigs.k8s.io/yaml
 sigs.k8s.io/yaml/goyaml.v2
-# tags.cncf.io/container-device-interface v0.8.1
+# tags.cncf.io/container-device-interface v1.0.1
 ## explicit; go 1.20
 tags.cncf.io/container-device-interface/internal/validation
 tags.cncf.io/container-device-interface/internal/validation/k8s
 tags.cncf.io/container-device-interface/pkg/cdi
 tags.cncf.io/container-device-interface/pkg/parser
-# tags.cncf.io/container-device-interface/specs-go v0.8.0
+# tags.cncf.io/container-device-interface/specs-go v1.0.0
 ## explicit; go 1.19
 tags.cncf.io/container-device-interface/specs-go
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go
index a38b0f1bc..a596c610b 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/annotations.go
@@ -71,7 +71,7 @@ func ParseAnnotations(annotations map[string]string) ([]string, []string, error)
 			continue
 		}
 		for _, d := range strings.Split(value, ",") {
-			if !IsQualifiedName(d) {
+			if !parser.IsQualifiedName(d) {
 				return nil, nil, fmt.Errorf("invalid CDI device name %q", d)
 			}
 			devices = append(devices, d)
@@ -130,7 +130,7 @@ func AnnotationKey(pluginName, deviceID string) (string, error) {
 func AnnotationValue(devices []string) (string, error) {
 	value, sep := "", ""
 	for _, d := range devices {
-		if _, _, _, err := ParseQualifiedName(d); err != nil {
+		if _, _, _, err := parser.ParseQualifiedName(d); err != nil {
 			return "", err
 		}
 		value += sep + d
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go
index 9afa4b182..7095f27da 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache.go
@@ -22,6 +22,7 @@ import (
 	"io/fs"
 	"os"
 	"path/filepath"
+	"runtime"
 	"sort"
 	"strings"
 	"sync"
@@ -116,7 +117,7 @@ func (c *Cache) configure(options ...Option) {
 		c.watch.setup(c.specDirs, c.dirErrors)
 		c.watch.start(&c.Mutex, c.refresh, c.dirErrors)
 	}
-	c.refresh()
+	_ = c.refresh() // we record but ignore errors
 }
 
 // Refresh rescans the CDI Spec directories and refreshes the Cache.
@@ -222,7 +223,8 @@ func (c *Cache) refreshIfRequired(force bool) (bool, error) {
 
 // InjectDevices injects the given qualified devices to an OCI Spec. It
 // returns any unresolvable devices and an error if injection fails for
-// any of the devices.
+// any of the devices. Might trigger a cache refresh, in which case any
+// errors encountered can be obtained using GetErrors().
 func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, error) {
 	var unresolved []string
 
@@ -233,7 +235,7 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	edits := &ContainerEdits{}
 	specs := map[*Spec]struct{}{}
@@ -335,24 +337,27 @@ func (c *Cache) RemoveSpec(name string) error {
 	return err
 }
 
-// GetDevice returns the cached device for the given qualified name.
+// GetDevice returns the cached device for the given qualified name. Might trigger
+// a cache refresh, in which case any errors encountered can be obtained using
+// GetErrors().
 func (c *Cache) GetDevice(device string) *Device {
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	return c.devices[device]
 }
 
-// ListDevices lists all cached devices by qualified name.
+// ListDevices lists all cached devices by qualified name. Might trigger a cache
+// refresh, in which case any errors encountered can be obtained using GetErrors().
 func (c *Cache) ListDevices() []string {
 	var devices []string
 
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	for name := range c.devices {
 		devices = append(devices, name)
@@ -362,14 +367,15 @@ func (c *Cache) ListDevices() []string {
 	return devices
 }
 
-// ListVendors lists all vendors known to the cache.
+// ListVendors lists all vendors known to the cache. Might trigger a cache refresh,
+// in which case any errors encountered can be obtained using GetErrors().
 func (c *Cache) ListVendors() []string {
 	var vendors []string
 
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	for vendor := range c.specs {
 		vendors = append(vendors, vendor)
@@ -379,7 +385,8 @@ func (c *Cache) ListVendors() []string {
 	return vendors
 }
 
-// ListClasses lists all device classes known to the cache.
+// ListClasses lists all device classes known to the cache. Might trigger a cache
+// refresh, in which case any errors encountered can be obtained using GetErrors().
 func (c *Cache) ListClasses() []string {
 	var (
 		cmap    = map[string]struct{}{}
@@ -389,7 +396,7 @@ func (c *Cache) ListClasses() []string {
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	for _, specs := range c.specs {
 		for _, spec := range specs {
@@ -404,12 +411,13 @@ func (c *Cache) ListClasses() []string {
 	return classes
 }
 
-// GetVendorSpecs returns all specs for the given vendor.
+// GetVendorSpecs returns all specs for the given vendor. Might trigger a cache
+// refresh, in which case any errors encountered can be obtained using GetErrors().
 func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
 	c.Lock()
 	defer c.Unlock()
 
-	c.refreshIfRequired(false)
+	_, _ = c.refreshIfRequired(false) // we record but ignore errors
 
 	return c.specs[vendor]
 }
@@ -522,6 +530,13 @@ func (w *watch) watch(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error
 	if watch == nil {
 		return
 	}
+
+	eventMask := fsnotify.Rename | fsnotify.Remove | fsnotify.Write
+	// On macOS, we also need to watch for Create events.
+	if runtime.GOOS == "darwin" {
+		eventMask |= fsnotify.Create
+	}
+
 	for {
 		select {
 		case event, ok := <-watch.Events:
@@ -529,10 +544,10 @@ func (w *watch) watch(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error
 				return
 			}
 
-			if (event.Op & (fsnotify.Rename | fsnotify.Remove | fsnotify.Write)) == 0 {
+			if (event.Op & eventMask) == 0 {
 				continue
 			}
-			if event.Op == fsnotify.Write {
+			if event.Op == fsnotify.Write || event.Op == fsnotify.Create {
 				if ext := filepath.Ext(event.Name); ext != ".json" && ext != ".yaml" {
 					continue
 				}
@@ -544,7 +559,7 @@ func (w *watch) watch(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error
 			} else {
 				w.update(dirErrors)
 			}
-			refresh()
+			_ = refresh()
 			m.Unlock()
 
 		case _, ok := <-watch.Errors:
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go
index 70791666c..4744eff8f 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/container-edits.go
@@ -26,7 +26,7 @@ import (
 
 	oci "github.com/opencontainers/runtime-spec/specs-go"
 	ocigen "github.com/opencontainers/runtime-tools/generate"
-	"tags.cncf.io/container-device-interface/specs-go"
+	cdi "tags.cncf.io/container-device-interface/specs-go"
 )
 
 const (
@@ -64,7 +64,7 @@ var (
 // to all OCI Specs where at least one devices from the CDI Spec
 // is injected.
 type ContainerEdits struct {
-	*specs.ContainerEdits
+	*cdi.ContainerEdits
 }
 
 // Apply edits to the given OCI Spec. Updates the OCI Spec in place.
@@ -205,7 +205,7 @@ func (e *ContainerEdits) Append(o *ContainerEdits) *ContainerEdits {
 		e = &ContainerEdits{}
 	}
 	if e.ContainerEdits == nil {
-		e.ContainerEdits = &specs.ContainerEdits{}
+		e.ContainerEdits = &cdi.ContainerEdits{}
 	}
 
 	e.Env = append(e.Env, o.Env...)
@@ -259,7 +259,7 @@ func ValidateEnv(env []string) error {
 
 // DeviceNode is a CDI Spec DeviceNode wrapper, used for validating DeviceNodes.
 type DeviceNode struct {
-	*specs.DeviceNode
+	*cdi.DeviceNode
 }
 
 // Validate a CDI Spec DeviceNode.
@@ -289,7 +289,7 @@ func (d *DeviceNode) Validate() error {
 
 // Hook is a CDI Spec Hook wrapper, used for validating hooks.
 type Hook struct {
-	*specs.Hook
+	*cdi.Hook
 }
 
 // Validate a hook.
@@ -308,7 +308,7 @@ func (h *Hook) Validate() error {
 
 // Mount is a CDI Mount wrapper, used for validating mounts.
 type Mount struct {
-	*specs.Mount
+	*cdi.Mount
 }
 
 // Validate a mount.
@@ -325,13 +325,13 @@ func (m *Mount) Validate() error {
 // IntelRdt is a CDI IntelRdt wrapper.
 // This is used for validation and conversion to OCI specifications.
 type IntelRdt struct {
-	*specs.IntelRdt
+	*cdi.IntelRdt
 }
 
 // ValidateIntelRdt validates the IntelRdt configuration.
 //
 // Deprecated: ValidateIntelRdt is deprecated use IntelRdt.Validate() instead.
-func ValidateIntelRdt(i *specs.IntelRdt) error {
+func ValidateIntelRdt(i *cdi.IntelRdt) error {
 	return (&IntelRdt{i}).Validate()
 }
 
@@ -355,7 +355,7 @@ func ensureOCIHooks(spec *oci.Spec) {
 func sortMounts(specgen *ocigen.Generator) {
 	mounts := specgen.Mounts()
 	specgen.ClearMounts()
-	sort.Sort(orderedMounts(mounts))
+	sort.Stable(orderedMounts(mounts))
 	specgen.Config.Mounts = mounts
 }
 
@@ -375,14 +375,7 @@ func (m orderedMounts) Len() int {
 // mount indexed by parameter 1 is less than that of the mount indexed by
 // parameter 2. Used in sorting.
 func (m orderedMounts) Less(i, j int) bool {
-	ip, jp := m.parts(i), m.parts(j)
-	if ip < jp {
-		return true
-	}
-	if jp < ip {
-		return false
-	}
-	return m[i].Destination < m[j].Destination
+	return m.parts(i) < m.parts(j)
 }
 
 // Swap swaps two items in an array of mounts. Used in sorting
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go
index 00be48dd5..2e5fa57f4 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/device.go
@@ -67,7 +67,7 @@ func (d *Device) edits() *ContainerEdits {
 
 // Validate the device.
 func (d *Device) validate() error {
-	if err := ValidateDeviceName(d.Name); err != nil {
+	if err := parser.ValidateDeviceName(d.Name); err != nil {
 		return err
 	}
 	name := d.Name
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go
index 0ea071455..d00e0d339 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/doc.go
@@ -35,25 +35,11 @@
 // available and instantiated the first time it is referenced directly
 // or indirectly. The most frequently used cache functions are available
 // as identically named package level functions which operate on the
-// default cache instance. Moreover, the registry also operates on the
-// same default cache. We plan to deprecate the registry and eventually
-// remove it in a future release.
-//
-// # CDI Registry
-//
-// Note: the Registry and its related interfaces are deprecated and will
-// be removed in a future version. Please use the default cache and its
-// related package-level function instead.
-//
-// The primary interface to interact with CDI devices is the Registry. It
-// is essentially a cache of all Specs and devices discovered in standard
-// CDI directories on the host. The registry has two main functionality,
-// injecting devices into an OCI Spec and refreshing the cache of CDI
-// Specs and devices.
+// default cache instance.
 //
 // # Device Injection
 //
-// Using the Registry one can inject CDI devices into a container with code
+// Using the Cache one can inject CDI devices into a container with code
 // similar to the following snippet:
 //
 //	import (
@@ -63,13 +49,14 @@
 //	    log "github.com/sirupsen/logrus"
 //
 //	    "tags.cncf.io/container-device-interface/pkg/cdi"
-//	    oci "github.com/opencontainers/runtime-spec/specs-go"
+//	    "github.com/opencontainers/runtime-spec/specs-go"
 //	)
 //
-//	func injectCDIDevices(spec *oci.Spec, devices []string) error {
+//	func injectCDIDevices(spec *specs.Spec, devices []string) error {
 //	    log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
 //
-//	    unresolved, err := cdi.GetRegistry().InjectDevices(spec, devices)
+//	    cache := cdi.GetDefaultCache()
+//	    unresolved, err := cache.InjectDevices(spec, devices)
 //	    if err != nil {
 //	        return fmt.Errorf("CDI device injection failed: %w", err)
 //	    }
@@ -106,17 +93,17 @@
 //	    log "github.com/sirupsen/logrus"
 //
 //	    "tags.cncf.io/container-device-interface/pkg/cdi"
-//	    oci "github.com/opencontainers/runtime-spec/specs-go"
+//	    "github.com/opencontainers/runtime-spec/specs-go"
 //	)
 //
-//	func injectCDIDevices(spec *oci.Spec, devices []string) error {
-//	    registry := cdi.GetRegistry()
+//	func injectCDIDevices(spec *specs.Spec, devices []string) error {
+//	    cache := cdi.GetDefaultCache()
 //
-//	    if err := registry.Refresh(); err != nil {
+//	    if err := cache.Refresh(); err != nil {
 //	        // Note:
 //	        //   It is up to the implementation to decide whether
 //	        //   to abort injection on errors. A failed Refresh()
-//	        //   does not necessarily render the registry unusable.
+//	        //   does not necessarily render the cache unusable.
 //	        //   For instance, a parse error in a Spec file for
 //	        //   vendor A does not have any effect on devices of
 //	        //   vendor B...
@@ -125,7 +112,7 @@
 //
 //	    log.Debug("pristine OCI Spec: %s", dumpSpec(spec))
 //
-//	    unresolved, err := registry.InjectDevices(spec, devices)
+//	    unresolved, err := cache.InjectDevices(spec, devices)
 //	    if err != nil {
 //	        return fmt.Errorf("CDI device injection failed: %w", err)
 //	    }
@@ -192,7 +179,7 @@
 // )
 //
 //	func generateDeviceSpecs() error {
-//	    registry := cdi.GetRegistry()
+//	    cache := specs.GetDefaultCache()
 //	    spec := &specs.Spec{
 //	        Version: specs.CurrentVersion,
 //	        Kind:    vendor+"/"+class,
@@ -210,7 +197,7 @@
 //	        return fmt.Errorf("failed to generate Spec name: %w", err)
 //	    }
 //
-//	    return registry.SpecDB().WriteSpec(spec, specName)
+//	    return cache.WriteSpec(spec, specName)
 //	}
 //
 // Similarly, generating and later cleaning up transient Spec files can be
@@ -229,7 +216,7 @@
 // )
 //
 //	func generateTransientSpec(ctr Container) error {
-//	    registry := cdi.GetRegistry()
+//	    cache := specs.GetDefaultCache()
 //	    devices := getContainerDevs(ctr, vendor, class)
 //	    spec := &specs.Spec{
 //	        Version: specs.CurrentVersion,
@@ -257,21 +244,21 @@
 //	        return fmt.Errorf("failed to generate Spec name: %w", err)
 //	    }
 //
-//	    return registry.SpecDB().WriteSpec(spec, specName)
+//	    return cache.WriteSpec(spec, specName)
 //	}
 //
 //	func removeTransientSpec(ctr Container) error {
-//	    registry := cdi.GetRegistry()
+//	    cache := specs.GetDefaultCache()
 //	    transientID := getSomeSufficientlyUniqueIDForContainer(ctr)
 //	    specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID)
 //
-//	    return registry.SpecDB().RemoveSpec(specName)
+//	    return cache.RemoveSpec(specName)
 //	}
 //
 // # CDI Spec Validation
 //
 // This package performs both syntactic and semantic validation of CDI
-// Spec file data when a Spec file is loaded via the registry or using
+// Spec file data when a Spec file is loaded via the cache or using
 // the ReadSpec API function. As part of the semantic verification, the
 // Spec file is verified against the CDI Spec JSON validation schema.
 //
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/qualified-device.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/qualified-device.go
deleted file mode 100644
index 0bdfdc166..000000000
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/qualified-device.go
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-   Copyright © 2021 The CDI Authors
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package cdi
-
-import (
-	"tags.cncf.io/container-device-interface/pkg/parser"
-)
-
-// QualifiedName returns the qualified name for a device.
-// The syntax for a qualified device names is
-//
-//	"<vendor>/<class>=<name>".
-//
-// A valid vendor and class name may contain the following runes:
-//
-//	'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'.
-//
-// A valid device name may contain the following runes:
-//
-//	'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':'
-//
-// Deprecated: use parser.QualifiedName instead
-func QualifiedName(vendor, class, name string) string {
-	return parser.QualifiedName(vendor, class, name)
-}
-
-// IsQualifiedName tests if a device name is qualified.
-//
-// Deprecated: use parser.IsQualifiedName instead
-func IsQualifiedName(device string) bool {
-	return parser.IsQualifiedName(device)
-}
-
-// ParseQualifiedName splits a qualified name into device vendor, class,
-// and name. If the device fails to parse as a qualified name, or if any
-// of the split components fail to pass syntax validation, vendor and
-// class are returned as empty, together with the verbatim input as the
-// name and an error describing the reason for failure.
-//
-// Deprecated: use parser.ParseQualifiedName instead
-func ParseQualifiedName(device string) (string, string, string, error) {
-	return parser.ParseQualifiedName(device)
-}
-
-// ParseDevice tries to split a device name into vendor, class, and name.
-// If this fails, for instance in the case of unqualified device names,
-// ParseDevice returns an empty vendor and class together with name set
-// to the verbatim input.
-//
-// Deprecated: use parser.ParseDevice instead
-func ParseDevice(device string) (string, string, string) {
-	return parser.ParseDevice(device)
-}
-
-// ParseQualifier splits a device qualifier into vendor and class.
-// The syntax for a device qualifier is
-//
-//	"<vendor>/<class>"
-//
-// If parsing fails, an empty vendor and the class set to the
-// verbatim input is returned.
-//
-// Deprecated: use parser.ParseQualifier instead
-func ParseQualifier(kind string) (string, string) {
-	return parser.ParseQualifier(kind)
-}
-
-// ValidateVendorName checks the validity of a vendor name.
-// A vendor name may contain the following ASCII characters:
-//   - upper- and lowercase letters ('A'-'Z', 'a'-'z')
-//   - digits ('0'-'9')
-//   - underscore, dash, and dot ('_', '-', and '.')
-//
-// Deprecated: use parser.ValidateVendorName instead
-func ValidateVendorName(vendor string) error {
-	return parser.ValidateVendorName(vendor)
-}
-
-// ValidateClassName checks the validity of class name.
-// A class name may contain the following ASCII characters:
-//   - upper- and lowercase letters ('A'-'Z', 'a'-'z')
-//   - digits ('0'-'9')
-//   - underscore, dash, and dot ('_', '-', and '.')
-//
-// Deprecated: use parser.ValidateClassName instead
-func ValidateClassName(class string) error {
-	return parser.ValidateClassName(class)
-}
-
-// ValidateDeviceName checks the validity of a device name.
-// A device name may contain the following ASCII characters:
-//   - upper- and lowercase letters ('A'-'Z', 'a'-'z')
-//   - digits ('0'-'9')
-//   - underscore, dash, dot, colon ('_', '-', '.', ':')
-//
-// Deprecated: use parser.ValidateDeviceName instead
-func ValidateDeviceName(name string) error {
-	return parser.ValidateDeviceName(name)
-}
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/registry.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/registry.go
deleted file mode 100644
index 3113a05a2..000000000
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/registry.go
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
-   Copyright © 2021 The CDI Authors
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package cdi
-
-import (
-	"sync"
-
-	oci "github.com/opencontainers/runtime-spec/specs-go"
-	cdi "tags.cncf.io/container-device-interface/specs-go"
-)
-
-// Registry keeps a cache of all CDI Specs installed or generated on
-// the host. Registry is the primary interface clients should use to
-// interact with CDI.
-//
-// The most commonly used Registry functions are for refreshing the
-// registry and injecting CDI devices into an OCI Spec.
-//
-// Deprecated: Registry is deprecated and will be removed in a future
-// version. Please update your code to use the corresponding package-
-// level functions Configure(), Refresh(), InjectDevices(), GetErrors(),
-// and GetDefaultCache().
-type Registry interface {
-	RegistryResolver
-	RegistryRefresher
-	DeviceDB() RegistryDeviceDB
-	SpecDB() RegistrySpecDB
-}
-
-// RegistryRefresher is the registry interface for refreshing the
-// cache of CDI Specs and devices.
-//
-// Configure reconfigures the registry with the given options.
-//
-// Refresh rescans all CDI Spec directories and updates the
-// state of the cache to reflect any changes. It returns any
-// errors encountered during the refresh.
-//
-// GetErrors returns all errors encountered for any of the scanned
-// Spec files during the last cache refresh.
-//
-// GetSpecDirectories returns the set up CDI Spec directories
-// currently in use. The directories are returned in the scan
-// order of Refresh().
-//
-// GetSpecDirErrors returns any errors related to the configured
-// Spec directories.
-//
-// Deprecated: RegistryRefresher is deprecated and will be removed
-// in a future version. Please use the default cache and its related
-// package-level functions instead.
-type RegistryRefresher interface {
-	Configure(...Option) error
-	Refresh() error
-	GetErrors() map[string][]error
-	GetSpecDirectories() []string
-	GetSpecDirErrors() map[string]error
-}
-
-// RegistryResolver is the registry interface for injecting CDI
-// devices into an OCI Spec.
-//
-// InjectDevices takes an OCI Spec and injects into it a set of
-// CDI devices given by qualified name. It returns the names of
-// any unresolved devices and an error if injection fails.
-//
-// Deprecated: RegistryRefresher is deprecated and will be removed
-// in a future version. Please use the default cache and its related
-// package-level functions instead.
-type RegistryResolver interface {
-	InjectDevices(spec *oci.Spec, device ...string) (unresolved []string, err error)
-}
-
-// RegistryDeviceDB is the registry interface for querying devices.
-//
-// GetDevice returns the CDI device for the given qualified name. If
-// the device is not GetDevice returns nil.
-//
-// ListDevices returns a slice with the names of qualified device
-// known. The returned slice is sorted.
-//
-// Deprecated: RegistryDeviceDB is deprecated and will be removed
-// in a future version. Please use the default cache and its related
-// package-level functions instead.
-// and will be removed in a future version. Please use the default
-// cache and its related package-level functions instead.
-type RegistryDeviceDB interface {
-	GetDevice(device string) *Device
-	ListDevices() []string
-}
-
-// RegistrySpecDB is the registry interface for querying CDI Specs.
-//
-// ListVendors returns a slice with all vendors known. The returned
-// slice is sorted.
-//
-// ListClasses returns a slice with all classes known. The returned
-// slice is sorted.
-//
-// GetVendorSpecs returns a slice of all Specs for the vendor.
-//
-// GetSpecErrors returns any errors for the Spec encountered during
-// the last cache refresh.
-//
-// WriteSpec writes the Spec with the given content and name to the
-// last Spec directory.
-//
-// Deprecated: RegistrySpecDB is deprecated and will be removed
-// in a future version. Please use the default cache and its related
-// package-level functions instead.
-type RegistrySpecDB interface {
-	ListVendors() []string
-	ListClasses() []string
-	GetVendorSpecs(vendor string) []*Spec
-	GetSpecErrors(*Spec) []error
-	WriteSpec(raw *cdi.Spec, name string) error
-	RemoveSpec(name string) error
-}
-
-type registry struct {
-	*Cache
-}
-
-var _ Registry = &registry{}
-
-var (
-	reg      *registry
-	initOnce sync.Once
-)
-
-// GetRegistry returns the CDI registry. If any options are given, those
-// are applied to the registry.
-//
-// Deprecated: GetRegistry is deprecated and will be removed in a future
-// version. Please use the default cache and its related package-level
-// functions instead.
-func GetRegistry(options ...Option) Registry {
-	initOnce.Do(func() {
-		reg = &registry{GetDefaultCache()}
-	})
-	if len(options) > 0 {
-		// We don't care about errors here
-		_ = reg.Configure(options...)
-	}
-	return reg
-}
-
-// DeviceDB returns the registry interface for querying devices.
-//
-// Deprecated: DeviceDB is deprecated and will be removed in a future
-// version. Please use the default cache and its related package-level
-// functions instead.
-func (r *registry) DeviceDB() RegistryDeviceDB {
-	return r
-}
-
-// SpecDB returns the registry interface for querying Specs.
-//
-// Deprecated: SpecDB is deprecated and will be removed in a future
-// version. Please use the default cache and its related package-level
-// functions instead.
-func (r *registry) SpecDB() RegistrySpecDB {
-	return r
-}
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go
index 09005d690..b192f962c 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec-dirs.go
@@ -35,8 +35,7 @@ var (
 	// While altering this variable changes the package defaults,
 	// the preferred way of overriding the default directories is
 	// to use a WithSpecDirs options. Otherwise the change is only
-	// effective if it takes place before creating the Registry or
-	// other Cache instances.
+	// effective if it takes place before creating the cache instance.
 	DefaultSpecDirs = []string{DefaultStaticDir, DefaultDynamicDir}
 	// ErrStopScan can be returned from a ScanSpecFunc to stop the scan.
 	ErrStopScan = errors.New("stop Spec scan")
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go
index 1a0a662b8..8d295a83f 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/spec.go
@@ -25,9 +25,11 @@ import (
 	"sync"
 
 	oci "github.com/opencontainers/runtime-spec/specs-go"
+	orderedyaml "gopkg.in/yaml.v3"
 	"sigs.k8s.io/yaml"
 
 	"tags.cncf.io/container-device-interface/internal/validation"
+	"tags.cncf.io/container-device-interface/pkg/parser"
 	cdi "tags.cncf.io/container-device-interface/specs-go"
 )
 
@@ -36,9 +38,13 @@ const (
 	defaultSpecExt = ".yaml"
 )
 
+type validator interface {
+	Validate(*cdi.Spec) error
+}
+
 var (
 	// Externally set CDI Spec validation function.
-	specValidator func(*cdi.Spec) error
+	specValidator validator
 	validatorLock sync.RWMutex
 )
 
@@ -105,7 +111,7 @@ func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
 		spec.path += defaultSpecExt
 	}
 
-	spec.vendor, spec.class = ParseQualifier(spec.Kind)
+	spec.vendor, spec.class = parser.ParseQualifier(spec.Kind)
 
 	if spec.devices, err = spec.validate(); err != nil {
 		return nil, fmt.Errorf("invalid CDI Spec: %w", err)
@@ -130,7 +136,7 @@ func (s *Spec) write(overwrite bool) error {
 	}
 
 	if filepath.Ext(s.path) == ".yaml" {
-		data, err = yaml.Marshal(s.Spec)
+		data, err = orderedyaml.Marshal(s.Spec)
 		data = append([]byte("---\n"), data...)
 	} else {
 		data, err = json.Marshal(s.Spec)
@@ -200,24 +206,21 @@ func (s *Spec) edits() *ContainerEdits {
 	return &ContainerEdits{&s.ContainerEdits}
 }
 
+// MinimumRequiredVersion determines the minimum spec version for the input spec.
+// Deprecated: use cdi.MinimumRequiredVersion instead
+func MinimumRequiredVersion(spec *cdi.Spec) (string, error) {
+	return cdi.MinimumRequiredVersion(spec)
+}
+
 // Validate the Spec.
 func (s *Spec) validate() (map[string]*Device, error) {
-	if err := validateVersion(s.Version); err != nil {
+	if err := cdi.ValidateVersion(s.Spec); err != nil {
 		return nil, err
 	}
-
-	minVersion, err := MinimumRequiredVersion(s.Spec)
-	if err != nil {
-		return nil, fmt.Errorf("could not determine minimum required version: %v", err)
-	}
-	if newVersion(minVersion).IsGreaterThan(newVersion(s.Version)) {
-		return nil, fmt.Errorf("the spec version must be at least v%v", minVersion)
-	}
-
-	if err := ValidateVendorName(s.vendor); err != nil {
+	if err := parser.ValidateVendorName(s.vendor); err != nil {
 		return nil, err
 	}
-	if err := ValidateClassName(s.class); err != nil {
+	if err := parser.ValidateClassName(s.class); err != nil {
 		return nil, err
 	}
 	if err := validation.ValidateSpecAnnotations(s.Kind, s.Annotations); err != nil {
@@ -238,17 +241,11 @@ func (s *Spec) validate() (map[string]*Device, error) {
 		}
 		devices[d.Name] = dev
 	}
-
-	return devices, nil
-}
-
-// validateVersion checks whether the specified spec version is supported.
-func validateVersion(version string) error {
-	if !validSpecVersions.isValidVersion(version) {
-		return fmt.Errorf("invalid version %q", version)
+	if len(devices) == 0 {
+		return nil, fmt.Errorf("invalid spec, no devices")
 	}
 
-	return nil
+	return devices, nil
 }
 
 // ParseSpec parses CDI Spec data into a raw CDI Spec.
@@ -264,13 +261,13 @@ func ParseSpec(data []byte) (*cdi.Spec, error) {
 // SetSpecValidator sets a CDI Spec validator function. This function
 // is used for extra CDI Spec content validation whenever a Spec file
 // loaded (using ReadSpec() or written (using WriteSpec()).
-func SetSpecValidator(fn func(*cdi.Spec) error) {
+func SetSpecValidator(v validator) {
 	validatorLock.Lock()
 	defer validatorLock.Unlock()
-	specValidator = fn
+	specValidator = v
 }
 
-// validateSpec validates the Spec using the extneral validator.
+// validateSpec validates the Spec using the external validator.
 func validateSpec(raw *cdi.Spec) error {
 	validatorLock.RLock()
 	defer validatorLock.RUnlock()
@@ -278,7 +275,7 @@ func validateSpec(raw *cdi.Spec) error {
 	if specValidator == nil {
 		return nil
 	}
-	err := specValidator(raw)
+	err := specValidator.Validate(raw)
 	if err != nil {
 		return fmt.Errorf("Spec validation failed: %w", err)
 	}
@@ -328,7 +325,7 @@ func GenerateTransientSpecName(vendor, class, transientID string) string {
 // the Spec does not contain a valid vendor or class, it returns
 // an empty name and a non-nil error.
 func GenerateNameForSpec(raw *cdi.Spec) (string, error) {
-	vendor, class := ParseQualifier(raw.Kind)
+	vendor, class := parser.ParseQualifier(raw.Kind)
 	if vendor == "" {
 		return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
 	}
@@ -342,7 +339,7 @@ func GenerateNameForSpec(raw *cdi.Spec) (string, error) {
 // If the Spec does not contain a valid vendor or class, it returns an
 // an empty name and a non-nil error.
 func GenerateNameForTransientSpec(raw *cdi.Spec, transientID string) (string, error) {
-	vendor, class := ParseQualifier(raw.Kind)
+	vendor, class := parser.ParseQualifier(raw.Kind)
 	if vendor == "" {
 		return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind)
 	}
diff --git a/vendor/tags.cncf.io/container-device-interface/specs-go/config.go b/vendor/tags.cncf.io/container-device-interface/specs-go/config.go
index d6d6302ff..f28657b86 100644
--- a/vendor/tags.cncf.io/container-device-interface/specs-go/config.go
+++ b/vendor/tags.cncf.io/container-device-interface/specs-go/config.go
@@ -2,72 +2,71 @@ package specs
 
 import "os"
 
-// CurrentVersion is the current version of the Spec.
-const CurrentVersion = "0.8.0"
-
 // Spec is the base configuration for CDI
 type Spec struct {
-	Version string `json:"cdiVersion"`
-	Kind    string `json:"kind"`
+	Version string `json:"cdiVersion" yaml:"cdiVersion"`
+	Kind    string `json:"kind"       yaml:"kind"`
 	// Annotations add meta information per CDI spec. Note these are CDI-specific and do not affect container metadata.
-	Annotations    map[string]string `json:"annotations,omitempty"`
-	Devices        []Device          `json:"devices"`
-	ContainerEdits ContainerEdits    `json:"containerEdits,omitempty"`
+	// Added in v0.6.0.
+	Annotations    map[string]string `json:"annotations,omitempty"    yaml:"annotations,omitempty"`
+	Devices        []Device          `json:"devices"                  yaml:"devices"`
+	ContainerEdits ContainerEdits    `json:"containerEdits,omitempty" yaml:"containerEdits,omitempty"`
 }
 
 // Device is a "Device" a container runtime can add to a container
 type Device struct {
-	Name string `json:"name"`
+	Name string `json:"name" yaml:"name"`
 	// Annotations add meta information per device. Note these are CDI-specific and do not affect container metadata.
-	Annotations    map[string]string `json:"annotations,omitempty"`
-	ContainerEdits ContainerEdits    `json:"containerEdits"`
+	// Added in v0.6.0.
+	Annotations    map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
+	ContainerEdits ContainerEdits    `json:"containerEdits"        yaml:"containerEdits"`
 }
 
 // ContainerEdits are edits a container runtime must make to the OCI spec to expose the device.
 type ContainerEdits struct {
-	Env            []string      `json:"env,omitempty"`
-	DeviceNodes    []*DeviceNode `json:"deviceNodes,omitempty"`
-	Hooks          []*Hook       `json:"hooks,omitempty"`
-	Mounts         []*Mount      `json:"mounts,omitempty"`
-	IntelRdt       *IntelRdt     `json:"intelRdt,omitempty"`
-	AdditionalGIDs []uint32      `json:"additionalGids,omitempty"`
+	Env            []string      `json:"env,omitempty"            yaml:"env,omitempty"`
+	DeviceNodes    []*DeviceNode `json:"deviceNodes,omitempty"    yaml:"deviceNodes,omitempty"`
+	Hooks          []*Hook       `json:"hooks,omitempty"          yaml:"hooks,omitempty"`
+	Mounts         []*Mount      `json:"mounts,omitempty"         yaml:"mounts,omitempty"`
+	IntelRdt       *IntelRdt     `json:"intelRdt,omitempty"       yaml:"intelRdt,omitempty"`       // Added in v0.7.0
+	AdditionalGIDs []uint32      `json:"additionalGids,omitempty" yaml:"additionalGids,omitempty"` // Added in v0.7.0
 }
 
 // DeviceNode represents a device node that needs to be added to the OCI spec.
 type DeviceNode struct {
-	Path        string       `json:"path"`
-	HostPath    string       `json:"hostPath,omitempty"`
-	Type        string       `json:"type,omitempty"`
-	Major       int64        `json:"major,omitempty"`
-	Minor       int64        `json:"minor,omitempty"`
-	FileMode    *os.FileMode `json:"fileMode,omitempty"`
-	Permissions string       `json:"permissions,omitempty"`
-	UID         *uint32      `json:"uid,omitempty"`
-	GID         *uint32      `json:"gid,omitempty"`
+	Path        string       `json:"path"                  yaml:"path"`
+	HostPath    string       `json:"hostPath,omitempty"    yaml:"hostPath,omitempty"` // Added in v0.5.0
+	Type        string       `json:"type,omitempty"        yaml:"type,omitempty"`
+	Major       int64        `json:"major,omitempty"       yaml:"major,omitempty"`
+	Minor       int64        `json:"minor,omitempty"       yaml:"minor,omitempty"`
+	FileMode    *os.FileMode `json:"fileMode,omitempty"    yaml:"fileMode,omitempty"`
+	Permissions string       `json:"permissions,omitempty" yaml:"permissions,omitempty"`
+	UID         *uint32      `json:"uid,omitempty"         yaml:"uid,omitempty"`
+	GID         *uint32      `json:"gid,omitempty"         yaml:"gid,omitempty"`
 }
 
 // Mount represents a mount that needs to be added to the OCI spec.
 type Mount struct {
-	HostPath      string   `json:"hostPath"`
-	ContainerPath string   `json:"containerPath"`
-	Options       []string `json:"options,omitempty"`
-	Type          string   `json:"type,omitempty"`
+	HostPath      string   `json:"hostPath"          yaml:"hostPath"`
+	ContainerPath string   `json:"containerPath"     yaml:"containerPath"`
+	Options       []string `json:"options,omitempty" yaml:"options,omitempty"`
+	Type          string   `json:"type,omitempty"    yaml:"type,omitempty"` // Added in v0.4.0
 }
 
 // Hook represents a hook that needs to be added to the OCI spec.
 type Hook struct {
-	HookName string   `json:"hookName"`
-	Path     string   `json:"path"`
-	Args     []string `json:"args,omitempty"`
-	Env      []string `json:"env,omitempty"`
-	Timeout  *int     `json:"timeout,omitempty"`
+	HookName string   `json:"hookName"          yaml:"hookName"`
+	Path     string   `json:"path"              yaml:"path"`
+	Args     []string `json:"args,omitempty"    yaml:"args,omitempty"`
+	Env      []string `json:"env,omitempty"     yaml:"env,omitempty"`
+	Timeout  *int     `json:"timeout,omitempty" yaml:"timeout,omitempty"`
 }
 
 // IntelRdt describes the Linux IntelRdt parameters to set in the OCI spec.
 type IntelRdt struct {
-	ClosID        string `json:"closID,omitempty"`
-	L3CacheSchema string `json:"l3CacheSchema,omitempty"`
-	MemBwSchema   string `json:"memBwSchema,omitempty"`
-	EnableCMT     bool   `json:"enableCMT,omitempty"`
-	EnableMBM     bool   `json:"enableMBM,omitempty"`
+	ClosID        string `json:"closID,omitempty"        yaml:"closID,omitempty"`
+	L3CacheSchema string `json:"l3CacheSchema,omitempty" yaml:"l3CacheSchema,omitempty"`
+	MemBwSchema   string `json:"memBwSchema,omitempty"   yaml:"memBwSchema,omitempty"`
+	EnableCMT     bool   `json:"enableCMT,omitempty"     yaml:"enableCMT,omitempty"`
+	EnableMBM     bool   `json:"enableMBM,omitempty"     yaml:"enableMBM,omitempty"`
 }
diff --git a/vendor/tags.cncf.io/container-device-interface/specs-go/oci.go b/vendor/tags.cncf.io/container-device-interface/specs-go/oci.go
deleted file mode 100644
index ce485cb23..000000000
--- a/vendor/tags.cncf.io/container-device-interface/specs-go/oci.go
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-Copyright © 2021 The CDI Authors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package specs
-
-import "errors"
-
-// errDeprecated is returned for the ToOCI functions below.
-// This should provide better guidance for user when migrating from the API
-// below to the APIs provided in the cdi package.
-var errDeprecated = errors.New("deprecated; Use cdi package functions instead")
-
-// ToOCI returns the opencontainers runtime Spec Hook for this Hook.
-//
-// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.Hook.toOCI
-// and made private.
-func (h *Hook) ToOCI() error {
-	return errDeprecated
-}
-
-// ToOCI returns the opencontainers runtime Spec Mount for this Mount.
-//
-// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.Mount.toOCI
-// and made private.
-func (m *Mount) ToOCI() error {
-	return errDeprecated
-}
-
-// ToOCI returns the opencontainers runtime Spec LinuxDevice for this DeviceNode.
-//
-// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.DeviceNode.toOCI
-// and made private.
-func (d *DeviceNode) ToOCI() error {
-	return errDeprecated
-}
-
-// ToOCI returns the opencontainers runtime Spec LinuxIntelRdt for this IntelRdt config.
-//
-// Deprecated: This function has been moved to tags.cncf.io/container-device-interface/pkg/cdi.IntelRdt.toOCI
-// and made private.
-func (i *IntelRdt) ToOCI() error {
-	return errDeprecated
-}
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/version.go b/vendor/tags.cncf.io/container-device-interface/specs-go/version.go
similarity index 69%
rename from vendor/tags.cncf.io/container-device-interface/pkg/cdi/version.go
rename to vendor/tags.cncf.io/container-device-interface/specs-go/version.go
index 9ca912671..002e03505 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/version.go
+++ b/vendor/tags.cncf.io/container-device-interface/specs-go/version.go
@@ -14,20 +14,18 @@
    limitations under the License.
 */
 
-package cdi
+package specs
 
 import (
+	"fmt"
 	"strings"
 
 	"golang.org/x/mod/semver"
-
-	"tags.cncf.io/container-device-interface/pkg/parser"
-	cdi "tags.cncf.io/container-device-interface/specs-go"
 )
 
 const (
-	// CurrentVersion is the current version of the CDI Spec.
-	CurrentVersion = cdi.CurrentVersion
+	// CurrentVersion is the current version of the Spec.
+	CurrentVersion = "1.0.0"
 
 	// vCurrent is the current version as a semver-comparable type
 	vCurrent version = "v" + CurrentVersion
@@ -41,6 +39,7 @@ const (
 	v060 version = "v0.6.0"
 	v070 version = "v0.7.0"
 	v080 version = "v0.8.0"
+	v100 version = "v1.0.0"
 
 	// vEarliest is the earliest supported version of the CDI specification
 	vEarliest version = v030
@@ -58,10 +57,29 @@ var validSpecVersions = requiredVersionMap{
 	v060: requiresV060,
 	v070: requiresV070,
 	v080: requiresV080,
+	v100: requiresV100,
+}
+
+// ValidateVersion checks whether the specified spec version is valid.
+// In addition to checking whether the spec version is in the set of known versions,
+// the spec is inspected to determine whether the features used are available in specified
+// version.
+func ValidateVersion(spec *Spec) error {
+	if !validSpecVersions.isValidVersion(spec.Version) {
+		return fmt.Errorf("invalid version %q", spec.Version)
+	}
+	minVersion, err := MinimumRequiredVersion(spec)
+	if err != nil {
+		return fmt.Errorf("could not determine minimum required version: %w", err)
+	}
+	if newVersion(minVersion).isGreaterThan(newVersion(spec.Version)) {
+		return fmt.Errorf("the spec version must be at least v%v", minVersion)
+	}
+	return nil
 }
 
 // MinimumRequiredVersion determines the minimum spec version for the input spec.
-func MinimumRequiredVersion(spec *cdi.Spec) (string, error) {
+func MinimumRequiredVersion(spec *Spec) (string, error) {
 	minVersion := validSpecVersions.requiredVersion(spec)
 	return minVersion.String(), nil
 }
@@ -80,17 +98,17 @@ func (v version) String() string {
 	return strings.TrimPrefix(string(v), "v")
 }
 
-// IsGreaterThan checks with a version is greater than the specified version.
-func (v version) IsGreaterThan(o version) bool {
+// isGreaterThan checks with a version is greater than the specified version.
+func (v version) isGreaterThan(o version) bool {
 	return semver.Compare(string(v), string(o)) > 0
 }
 
-// IsLatest checks whether the version is the latest supported version
-func (v version) IsLatest() bool {
+// isLatest checks whether the version is the latest supported version
+func (v version) isLatest() bool {
 	return v == vCurrent
 }
 
-type requiredFunc func(*cdi.Spec) bool
+type requiredFunc func(*Spec) bool
 
 type requiredVersionMap map[version]requiredFunc
 
@@ -103,18 +121,18 @@ func (r requiredVersionMap) isValidVersion(specVersion string) bool {
 }
 
 // requiredVersion returns the minimum version required for the given spec
-func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version {
+func (r requiredVersionMap) requiredVersion(spec *Spec) version {
 	minVersion := vEarliest
 
 	for v, isRequired := range validSpecVersions {
 		if isRequired == nil {
 			continue
 		}
-		if isRequired(spec) && v.IsGreaterThan(minVersion) {
+		if isRequired(spec) && v.isGreaterThan(minVersion) {
 			minVersion = v
 		}
 		// If we have already detected the latest version then no later version could be detected
-		if minVersion.IsLatest() {
+		if minVersion.isLatest() {
 			break
 		}
 	}
@@ -122,15 +140,22 @@ func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version {
 	return minVersion
 }
 
+// requiresV100 returns true if the spec uses v1.0.0 features.
+// Since the v1.0.0 spec bump was due to moving the minimum version checks to
+// the spec package, there are no explicit spec changes.
+func requiresV100(_ *Spec) bool {
+	return false
+}
+
 // requiresV080 returns true if the spec uses v0.8.0 features.
 // Since the v0.8.0 spec bump was due to the removed .ToOCI functions on the
-// spec types, there are explicit spec changes.
-func requiresV080(_ *cdi.Spec) bool {
+// spec types, there are no explicit spec changes.
+func requiresV080(_ *Spec) bool {
 	return false
 }
 
 // requiresV070 returns true if the spec uses v0.7.0 features
-func requiresV070(spec *cdi.Spec) bool {
+func requiresV070(spec *Spec) bool {
 	if spec.ContainerEdits.IntelRdt != nil {
 		return true
 	}
@@ -153,7 +178,7 @@ func requiresV070(spec *cdi.Spec) bool {
 }
 
 // requiresV060 returns true if the spec uses v0.6.0 features
-func requiresV060(spec *cdi.Spec) bool {
+func requiresV060(spec *Spec) bool {
 	// The v0.6.0 spec allows annotations to be specified at a spec level
 	for range spec.Annotations {
 		return true
@@ -167,23 +192,20 @@ func requiresV060(spec *cdi.Spec) bool {
 	}
 
 	// The v0.6.0 spec allows dots "." in Kind name label (class)
-	vendor, class := parser.ParseQualifier(spec.Kind)
-	if vendor != "" {
-		if strings.ContainsRune(class, '.') {
-			return true
-		}
+	if !strings.Contains(spec.Kind, "/") {
+		return false
 	}
-
-	return false
+	class := strings.SplitN(spec.Kind, "/", 2)[1]
+	return strings.Contains(class, ".")
 }
 
 // requiresV050 returns true if the spec uses v0.5.0 features
-func requiresV050(spec *cdi.Spec) bool {
-	var edits []*cdi.ContainerEdits
+func requiresV050(spec *Spec) bool {
+	var edits []*ContainerEdits
 
 	for _, d := range spec.Devices {
-		// The v0.5.0 spec allowed device names to start with a digit instead of requiring a letter
-		if len(d.Name) > 0 && !parser.IsLetter(rune(d.Name[0])) {
+		// The v0.5.0 spec allowed device name to start with a digit
+		if len(d.Name) > 0 && '0' <= d.Name[0] && d.Name[0] <= '9' {
 			return true
 		}
 		edits = append(edits, &d.ContainerEdits)
@@ -202,8 +224,8 @@ func requiresV050(spec *cdi.Spec) bool {
 }
 
 // requiresV040 returns true if the spec uses v0.4.0 features
-func requiresV040(spec *cdi.Spec) bool {
-	var edits []*cdi.ContainerEdits
+func requiresV040(spec *Spec) bool {
+	var edits []*ContainerEdits
 
 	for _, d := range spec.Devices {
 		edits = append(edits, &d.ContainerEdits)



Test patch:
diff --git a/client/client_test.go b/client/client_test.go
index badac0b0a..dfc387159 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -1229,8 +1229,8 @@ func testFrontendImageNaming(t *testing.T, sb integration.Sandbox) {
 			// Nothing to check
 		},
 		ExporterDocker: func(out, imageName string, exporterResponse map[string]string) {
-			require.Contains(t, exporterResponse, "image.name")
-			require.Equal(t, exporterResponse["image.name"], "docker.io/library/"+imageName)
+			require.Contains(t, exporterResponse, exptypes.ExporterImageNameKey)
+			require.Equal(t, exporterResponse[exptypes.ExporterImageNameKey], "docker.io/library/"+imageName)
 
 			dt, err := os.ReadFile(out)
 			require.NoError(t, err)
@@ -1257,8 +1257,8 @@ func testFrontendImageNaming(t *testing.T, sb integration.Sandbox) {
 			require.Equal(t, imageName, dockerMfst[0].RepoTags[0])
 		},
 		ExporterImage: func(_, imageName string, exporterResponse map[string]string) {
-			require.Contains(t, exporterResponse, "image.name")
-			require.Equal(t, exporterResponse["image.name"], imageName)
+			require.Contains(t, exporterResponse, exptypes.ExporterImageNameKey)
+			require.Equal(t, exporterResponse[exptypes.ExporterImageNameKey], imageName)
 
 			// check if we can pull (requires containerd)
 			cdAddress := sb.ContainerdAddress()
@@ -3291,9 +3291,9 @@ func testMultipleExporters(t *testing.T, sb integration.Sandbox) {
 	require.NoError(t, err)
 
 	if workers.IsTestDockerd() {
-		require.Equal(t, resp.ExporterResponse["image.name"], target1+","+target2)
+		require.Equal(t, resp.ExporterResponse[exptypes.ExporterImageNameKey], target1+","+target2)
 	} else {
-		require.Equal(t, resp.ExporterResponse["image.name"], target2)
+		require.Equal(t, resp.ExporterResponse[exptypes.ExporterImageNameKey], target2)
 	}
 	require.FileExists(t, filepath.Join(destDir, "out.tar"))
 	require.FileExists(t, filepath.Join(destDir, "out2.tar"))
diff --git a/cmd/buildctl/build_test.go b/cmd/buildctl/build_test.go
index 6bb791077..76d072de4 100644
--- a/cmd/buildctl/build_test.go
+++ b/cmd/buildctl/build_test.go
@@ -144,8 +144,8 @@ func testBuildMetadataFile(t *testing.T, sb integration.Sandbox) {
 	err = json.Unmarshal(metadataBytes, &metadata)
 	require.NoError(t, err)
 
-	require.Contains(t, metadata, "image.name")
-	require.Equal(t, imageName, metadata["image.name"])
+	require.Contains(t, metadata, exptypes.ExporterImageNameKey)
+	require.Equal(t, imageName, metadata[exptypes.ExporterImageNameKey])
 
 	require.Contains(t, metadata, exptypes.ExporterImageDigestKey)
 	digest := metadata[exptypes.ExporterImageDigestKey]
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_darwin.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_darwin.go
new file mode 100644
index 000000000..b09ea6ff9
--- /dev/null
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_darwin.go
@@ -0,0 +1,26 @@
+//go:build darwin
+// +build darwin
+
+/*
+   Copyright © 2021 The CDI Authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package cdi
+
+import "syscall"
+
+func osSync() {
+	_ = syscall.Sync()
+}
diff --git a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_unix.go b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_unix.go
index 0ee5fb86f..b7c44129f 100644
--- a/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_unix.go
+++ b/vendor/tags.cncf.io/container-device-interface/pkg/cdi/cache_test_unix.go
@@ -1,5 +1,5 @@
-//go:build !windows
-// +build !windows
+//go:build !windows && !darwin
+// +build !windows,!darwin
 
 /*
    Copyright © 2021 The CDI Authors