fix(rootfs): only pivot_root when a mount namespace actually exists - #863
Conversation
✅ Deploy Preview for urunc canceled.
|
✅ Deploy Preview for urunc canceled.
|
99c7be6 to
18c0109
Compare
|
Hello @Anand-240 , thank you for the PR, as mentioned in the contribution guide:
Also testing a PR against the tests that the same PR introduces is not a valid test. You should perform all the tests. This is a small change and it is ok, but as an author you are responsible to ensure your changes work and show that to us. |
Exec() decided whether to pivot_root or chroot by checking if findNS() returned an error for the mount namespace. findNS() returns a non-nil error both when the namespace type is missing from the spec entirely and when it's present but not created yet, so the old check (withPivot := err != nil) ended up pivoting even when the spec had no mount namespace at all. In that case FormatNsenterInfo() never sets CLONE_NEWNS, so no new mount namespace gets created and the process stays in whatever mount namespace the caller (normally urunc create) is running in, usually the host's. pivot_root then runs against that namespace instead of an isolated one. Use the same ErrNotExistingNS check joinSandboxNetNs already relies on for the network namespace: only pivot when findNS returns nil (joining an existing namespace) or ErrNotExistingNS (entry present, about to be created). Fall back to chroot when the namespace type is missing from the spec entirely. Fixes urunc-dev#861 Signed-off-by: Anand-240 <anandprakashsrivastava68@gmail.com>
Signed-off-by: Anand-240 <anandprakashsrivastava68@gmail.com>
18c0109 to
2385fe8
Compare
cmainas
left a comment
There was a problem hiding this comment.
Hello @Anand-240 ,
thank you for the changes, there some things we need to fix for the unit test.
| t.Run("namespace type missing from spec", func(t *testing.T) { | ||
| t.Parallel() | ||
| namespaces := []specs.LinuxNamespace{ | ||
| {Type: specs.NetworkNamespace, Path: "/proc/1/ns/net"}, |
There was a problem hiding this comment.
We should use a dummy path here
There was a problem hiding this comment.
Fixed, using /dummy/path now instead of a real proc path.
| assert.False(t, errors.Is(err, ErrNotExistingNS), | ||
| "a namespace type absent from the spec must not be reported as ErrNotExistingNS") |
There was a problem hiding this comment.
Merged into one line.
| nsPath := filepath.Join(t.TempDir(), "mnt") | ||
| require.NoError(t, os.WriteFile(nsPath, []byte{}, 0644)) |
There was a problem hiding this comment.
Why do we need to create a file for this?
There was a problem hiding this comment.
Good catch, didn't need it. t.TempDir() already gives a real existing directory so I just use that as the path directly now, no file needed.
|
Please also include the LLM usage section in the PR description. |
Use a dummy path instead of a real proc path in the "namespace type missing from spec" case, merge the assert.False call and its message onto one line, and drop the unnecessary file creation in the "existing path" case since t.TempDir() already returns a real directory. Also fixes the gosec G306 warning on the removed WriteFile call. Signed-off-by: Anand-240 <anandprakashsrivastava68@gmail.com>
Done, it's back in the description now. |
Exec() decided whether to pivot_root or chroot by checking if findNS() returned an error for the mount namespace. findNS() returns a non-nil error both when the namespace type is missing from the spec entirely and when it's present but not created yet, so the old check (withPivot := err != nil) ended up pivoting even when the spec had no mount namespace at all. In that case FormatNsenterInfo() never sets CLONE_NEWNS, so no new mount namespace gets created and the process stays in whatever mount namespace the caller (normally urunc create) is running in, usually the host's. pivot_root then runs against that namespace instead of an isolated one. Use the same ErrNotExistingNS check joinSandboxNetNs already relies on for the network namespace: only pivot when findNS returns nil (joining an existing namespace) or ErrNotExistingNS (entry present, about to be created). Fall back to chroot when the namespace type is missing from the spec entirely. Fixes #861 PR: #863 Signed-off-by: Anand-240 <anandprakashsrivastava68@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
PR: #863 Signed-off-by: Anand-240 <anandprakashsrivastava68@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
Use a dummy path instead of a real proc path in the "namespace type missing from spec" case, merge the assert.False call and its message onto one line, and drop the unnecessary file creation in the "existing path" case since t.TempDir() already returns a real directory. Also fixes the gosec G306 warning on the removed WriteFile call. PR: #863 Signed-off-by: Anand-240 <anandprakashsrivastava68@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
Exec() decided whether to pivot_root or chroot by checking if findNS() returned an error for the mount namespace. findNS() returns a non-nil error both when the namespace type is missing from the spec entirely and when it's present but not created yet, so the old check (withPivot := err != nil) ended up pivoting even when the spec had no mount namespace at all. In that case FormatNsenterInfo() never sets CLONE_NEWNS, so no new mount namespace gets created and the process stays in whatever mount namespace the caller (normally urunc create) is running in, usually the host's. pivot_root then runs against that namespace instead of an isolated one. Use the same ErrNotExistingNS check joinSandboxNetNs already relies on for the network namespace: only pivot when findNS returns nil (joining an existing namespace) or ErrNotExistingNS (entry present, about to be created). Fall back to chroot when the namespace type is missing from the spec entirely. Fixes #861 PR: #863 Signed-off-by: Anand-240 <anandprakashsrivastava68@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
PR: #863 Signed-off-by: Anand-240 <anandprakashsrivastava68@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
Use a dummy path instead of a real proc path in the "namespace type missing from spec" case, merge the assert.False call and its message onto one line, and drop the unnecessary file creation in the "existing path" case since t.TempDir() already returns a real directory. Also fixes the gosec G306 warning on the removed WriteFile call. PR: #863 Signed-off-by: Anand-240 <anandprakashsrivastava68@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
Description
Exec() decides whether to pivot_root or chroot into the monitor's rootfs by checking whether findNS() returned an error for the mount namespace. findNS() returns a non-nil error in two different situations though: the namespace type is entirely absent from Linux.Namespaces, or it's present but not created yet (empty Path). The old code treated both the same way (
withPivot := err != nil), so it ended up pivoting even when the mount namespace type was missing from the spec altogether.When that happens, FormatNsenterInfo() never sets unix.CLONE_NEWNS, so no new mount namespace gets created. The process just stays in whatever mount namespace the caller (normally urunc create itself) is running in, which is usually the host's. pivot_root then runs against that namespace instead of an isolated one.
This mirrors the distinction joinSandboxNetNs already makes using ErrNotExistingNS: only pivot when findNS returns nil (joining an existing namespace) or ErrNotExistingNS (namespace entry present, about to be created). If the namespace type is missing from the spec entirely, fall back to chroot.
Also fixed the comment above this line, since it described the intended behavior but didn't match what the code actually did.
Related issues
How was this tested?
LLM usage
Used Claude to help investigate the bug (reported as #861) and put together this fix. Reviewed, tested, and pushed the follow-up fixes myself.
Checklist
make lint).make test_ctr,make test_nerdctl,make test_docker,make test_crictl).