Skip to content

fix(network): close tap device fd after creation#831

Open
Anamika1608 wants to merge 1 commit into
urunc-dev:mainfrom
Anamika1608:fix-tap-device-fd-leak
Open

fix(network): close tap device fd after creation#831
Anamika1608 wants to merge 1 commit into
urunc-dev:mainfrom
Anamika1608:fix-tap-device-fd-leak

Conversation

@Anamika1608

@Anamika1608 Anamika1608 commented Jul 17, 2026

Copy link
Copy Markdown

Description

createTapDevice relies on a later syscall.Exec into the monitor to close the tap device fd that netlink.LinkAdd opened (O_CLOEXEC). In any flow where urunc keeps running instead of exec-ing away, the fd stays open for as long as urunc runs, and since the tap device is single-queue, it blocks the monitor from attaching to the device ("Resource busy").

Close the fd explicitly right after the owner/group ioctls. The device is created with TUNSETPERSIST, so it survives the close.

Related issues

How was this tested?

  • go build ./... and go test ./internal/... ./pkg/... pass (Ubuntu 24.04 aarch64 VM with KVM, Firecracker v1.7.0).
  • make lint reports no findings in the changed file.
  • The failure this fixes was reproduced and verified fixed in a prototype where urunc spawns Firecracker as a child process instead of exec-ing into it (the API-based boot work from LFx Mentorship (CNCF - urunc: Improve lifecycle management of sandbox monitors) #756): with the fd left open, Firecracker's /network-interfaces call fails with EBUSY; with this change, the guest boots and serves traffic.
  • Live end-to-end check on this branch: the chttp-firecracker image boots through real containerd + nerdctl and the guest's HTTP server answers 200. Verified identical behavior on unchanged main.
  • make test_nerdctl is not runnable in my environment (aarch64 VM with only Firecracker installed; the suite needs docker, all five monitors, devmapper, and amd64 test images), so the e2e checkbox is left unticked.

LLM usage

claude code (opus 4.8) for the understanding of codebase, approach decisions and reviews

Checklist

  • I have read the contribution guide.
  • The linter passes locally (make lint).
  • The e2e tests of at least one tool pass locally (make test_ctr, make test_nerdctl, make test_docker, make test_crictl).
  • If LLMs were used: I have read the llm policy.

@netlify

netlify Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploy Preview for urunc canceled.

Name Link
🔨 Latest commit 185f2a1
🔍 Latest deploy log https://app.netlify.com/projects/urunc/deploys/6a609ce4f0829c0008c8552d

@Anamika1608
Anamika1608 force-pushed the fix-tap-device-fd-leak branch from bf9b5f5 to 1e77783 Compare July 17, 2026 06:46
@Anamika1608

Anamika1608 commented Jul 17, 2026

Copy link
Copy Markdown
Author

hi @cmainas ,

as discussed, i opened a separate issue and PR for the tap device fd leak:

one thing i want to flag honestly about the PR: i could not get the e2e tests to pass locally, but i verified this is not caused by my change. i ran make test_nerdctl twice in my test VM (ubuntu 24.04 aarch64, KVM, firecracker v1.7.0) - once on my branch and once on plain, unchanged main (609e07e). both runs fail identically: 26 of 26 executed specs, and every failure happens at container create, before any of the network code my PR touches even runs.

the actual failure is:

failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "/urunit": stat /urunit: no such file or directory

this also reproduces outside the test suite with a plain nerdctl run of harbor.nbfc.io/nubificus/urunc/chttp-firecracker-linux-aarch64:latest on unchanged main. the same image boots fine on the older main i was previously based on (8928c4c), so something between 8928c4c and 609e07e changed the behavior.

so on the PR: build, unit tests and lint all pass, and the fix itself was verified in the API-based boot prototype where it actually manifests (EBUSY on the tap device, fixed after the change). i left the e2e checkbox unticked and explained this in the PR description.

@cmainas cmainas 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.

Hello @Anamika1608 ,

thank you for the PR, I have added one comment. Also, please add yourself in https://github.com/urunc-dev/urunc/blob/main/.github/contributors.yaml

Comment thread pkg/network/network.go Outdated
@cmainas

cmainas commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

hi @cmainas ,

as discussed, i opened a separate issue and PR for the tap device fd leak:

* issue: [Close the tap device fd explicitly instead of relying on O_CLOEXEC #830](https://github.com/urunc-dev/urunc/issues/830)

* pr: [fix(network): close tap device fd after creation #831](https://github.com/urunc-dev/urunc/pull/831)

one thing i want to flag honestly about the PR: i could not get the e2e tests to pass locally, but i verified this is not caused by my change. i ran make test_nerdctl twice in my test VM (ubuntu 24.04 aarch64, KVM, firecracker v1.7.0) - once on my branch and once on plain, unchanged main (609e07e). both runs fail identically: 26 of 26 executed specs, and every failure happens at container create, before any of the network code my PR touches even runs.

the actual failure is:

failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "/urunit": stat /urunit: no such file or directory

this also reproduces outside the test suite with a plain nerdctl run of harbor.nbfc.io/nubificus/urunc/chttp-firecracker-linux-aarch64:latest on unchanged main. the same image boots fine on the older main i was previously based on (8928c4c), so something between 8928c4c and 609e07e changed the behavior.

so on the PR: build, unit tests and lint all pass, and the fix itself was verified in the API-based boot prototype where it actually manifests (EBUSY on the tap device, fixed after the change). i left the e2e checkbox unticked and explained this in the PR description.

From the logs it seems that runc is used instead of urunc. Can you verify the version of urunc with urunc --version?

@Anamika1608
Anamika1608 force-pushed the fix-tap-device-fd-leak branch from 1e77783 to bda6460 Compare July 18, 2026 15:33
@Anamika1608

Anamika1608 commented Jul 20, 2026

Copy link
Copy Markdown
Author

hi @cmainas ,
as discussed, i opened a separate issue and PR for the tap device fd leak:

* issue: [Close the tap device fd explicitly instead of relying on O_CLOEXEC #830](https://github.com/urunc-dev/urunc/issues/830)

* pr: [fix(network): close tap device fd after creation #831](https://github.com/urunc-dev/urunc/pull/831)

one thing i want to flag honestly about the PR: i could not get the e2e tests to pass locally, but i verified this is not caused by my change. i ran make test_nerdctl twice in my test VM (ubuntu 24.04 aarch64, KVM, firecracker v1.7.0) - once on my branch and once on plain, unchanged main (609e07e). both runs fail identically: 26 of 26 executed specs, and every failure happens at container create, before any of the network code my PR touches even runs.
the actual failure is:
failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "/urunit": stat /urunit: no such file or directory
this also reproduces outside the test suite with a plain nerdctl run of harbor.nbfc.io/nubificus/urunc/chttp-firecracker-linux-aarch64:latest on unchanged main. the same image boots fine on the older main i was previously based on (8928c4c), so something between 8928c4c and 609e07e changed the behavior.
so on the PR: build, unit tests and lint all pass, and the fix itself was verified in the API-based boot prototype where it actually manifests (EBUSY on the tap device, fixed after the change). i left the e2e checkbox unticked and explained this in the PR description.

From the logs it seems that runc is used instead of urunc. Can you verify the version of urunc with urunc --version?

yeah, you were right it was because of the stale vendor file i had, i deleted that one and generated the new one, and it passes completely now.

@cmainas cmainas 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.

Thank you @Anamika1608 for the changes,

we still need to check for errors even in the failure path and update the error message we return or at least log the closing fd error.

Comment thread pkg/network/network.go Outdated
Comment thread pkg/network/network.go Outdated
@cmainas

cmainas commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Hello @Anamika1608 ,

the changes look good, but please follow the correct structure of the PR template.

@Anamika1608

Copy link
Copy Markdown
Author

Hello @Anamika1608 ,

the changes look good, but please follow the correct structure of the PR template.

yes, i have updated the pr description.

@cmainas

cmainas commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Hello @Anamika1608 ,
the changes look good, but please follow the correct structure of the PR template.

yes, i have updated the pr description.

Great, let's squash the commits too and as soon as all checks complete we can merge.

createTapDevice relies on a later syscall.Exec into the monitor to
close the tap device fd that netlink.LinkAdd opened (O_CLOEXEC). In any
flow where urunc keeps running instead of exec-ing away, the fd stays
open and, since the tap device is single-queue, blocks the monitor from
attaching to it ("Resource busy").

Close the fd explicitly after the owner/group ioctls (handling the
close error), and add the author to contributors.

Signed-off-by: Anamika Aggarwal <anamikaagg18@gmail.com>
@Anamika1608
Anamika1608 force-pushed the fix-tap-device-fd-leak branch from 61fa441 to 185f2a1 Compare July 22, 2026 10:35
@Anamika1608

Copy link
Copy Markdown
Author

Hello @Anamika1608 ,
the changes look good, but please follow the correct structure of the PR template.

yes, i have updated the pr description.

Great, let's squash the commits too and as soon as all checks complete we can merge.

done, i have merged all in one commit

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Close the tap device fd explicitly instead of relying on O_CLOEXEC

2 participants