Skip to content

pkg/cdi: do not write host device info back into cached specs - #368

Open
DeRuina wants to merge 2 commits into
cncf-tags:mainfrom
DeRuina:fix-cache-mutation
Open

DeRuina wants to merge 2 commits into
cncf-tags:mainfrom
DeRuina:fix-cache-mutation

Conversation

@DeRuina

@DeRuina DeRuina commented Oct 6, 2026

Copy link
Copy Markdown

ContainerEdits.Apply() calls fillMissingInfo() on the *DeviceNode pointers it is given. Cache.InjectDevices() passes the device nodes of the cached Spec, so the host path, type, major/minor and file mode read from the host end up stored in the cache.

Effects:

  • after the first injection the cached node counts as minimally specified, so later injections skip the host check
    and reuse the stored values until the next spec refresh;
  • concurrent injections of the same device race on the shared fields;
  • FileMode pointers are shared between the cache and every generated OCI spec.

This change fills in the missing information on a copy of the device node and leaves the cached Spec untouched. The new TestInjectDevicesDoesNotMutateCache injects a FIFO-backed device, checks the cache is unchanged, replaces the host node with a regular file and expects the next injection to fail. The test fails on main and passes with this change; go test ./..., -race on the cache tests, go vet and gofmt are clean.

Related: #322 already clones the collected edits before resolving device nodes for the new ResolveEdits() API, for the same reason. This change covers the existing InjectDevices() path and any direct Apply() caller, and should rebase cleanly on either #322 or #367.

ContainerEdits.Apply() calls fillMissingInfo() on the *DeviceNode
pointers it was given. Cache.InjectDevices() hands it the device nodes
of the cached Spec, so the host path, type, major/minor and file mode
read from the host were written into the cache itself.

After the first injection the cached node therefore counted as
minimally specified, later injections skipped the host check and
reused the stored values until the next spec refresh, and concurrent
injections of the same device raced on the shared fields.

Fill in the missing information on a copy of the device node and leave
the cached Spec untouched. Add a regression test that injects a device
backed by a FIFO, checks the cache is unchanged, replaces the host node
with a regular file and expects the next injection to notice.

Signed-off-by: Dean Ruina <dean.ruina@csc.fi>
Signed-off-by: Dean Ruina <ruinadd@gmail.com>
@DeRuina
DeRuina force-pushed the fix-cache-mutation branch from c417688 to b4533bb Compare October 6, 2026 10:50
elezar
elezar previously approved these changes Oct 12, 2026

@elezar elezar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Comment thread pkg/cdi/container-edits.go Outdated
@elezar
elezar dismissed their stale review October 12, 2026 13:43

pointer member question.

Address review: the copy made before fillMissingInfo() duplicated only
FileMode. UID and GID are pointers too, and toOCI() hands them to the
generated OCI device, so the OCI spec still shared them with the cached
Spec. Add deepCopyDeviceNode(), which copies FileMode, UID and GID, and
use it in Apply(). There is no existing deep-copy helper for
specs.DeviceNode to reuse.

Signed-off-by: Dean Ruina <dean.ruina@csc.fi>
@DeRuina

DeRuina commented Oct 23, 2026

Copy link
Copy Markdown
Author

@elezar The lint failure is unrelated to this change. go-version: stable now gives Go 1.27, and golangci-lint v2.13 can't read Go 1.27 export data (the errors are all inside stdlib packages). devel/device-cgroup-rules has been failing the same way since Oct 9. Bumping golangci-lint to v2.15 in golangci-lint.yml (or pinning Go to 1.26) should fix it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants