Skip to content

Commit 6d68eb7

Browse files
Ensure that modified params file mount does not leak to host
This change ensures that the tmpfs mount created for the modified NVIDIA params file does not leak to the host. Signed-off-by: Carlos Eduardo Arango Gutierrez <[email protected]> Co-authored-by: Evan Lezar <[email protected]>
1 parent 4294100 commit 6d68eb7

File tree

2 files changed

+66
-17
lines changed

2 files changed

+66
-17
lines changed

cmd/nvidia-cdi-hook/disable-device-node-modification/params_linux.go

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,39 +20,57 @@
2020
package disabledevicenodemodification
2121

2222
import (
23+
"errors"
2324
"fmt"
2425
"os"
2526
"path/filepath"
2627

28+
securejoin "github.com/cyphar/filepath-securejoin"
2729
"github.com/opencontainers/runc/libcontainer/utils"
2830
"golang.org/x/sys/unix"
2931
)
3032

3133
func createParamsFileInContainer(containerRootDirPath string, contents []byte) error {
32-
tmpRoot, err := os.MkdirTemp("", "nvct-empty-dir*")
33-
if err != nil {
34-
return fmt.Errorf("failed to create temp root: %w", err)
34+
hookScratchDirPath := "/var/run/nvidia-ctk-hook"
35+
if err := utils.MkdirAllInRoot(containerRootDirPath, hookScratchDirPath, 0755); err != nil {
36+
return fmt.Errorf("error creating hook scratch folder: %w", err)
3537
}
3638

37-
if err := createTmpFs(tmpRoot, len(contents)); err != nil {
38-
return fmt.Errorf("failed to create tmpfs mount for params file: %w", err)
39-
}
39+
err := utils.WithProcfd(containerRootDirPath, hookScratchDirPath, func(hookScratchDirFdPath string) error {
40+
return createTmpFs(hookScratchDirFdPath, len(contents))
4041

41-
modifiedParamsFile, err := os.OpenFile(filepath.Join(tmpRoot, "nvct-params"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444)
42+
})
4243
if err != nil {
43-
return fmt.Errorf("failed to open modified params file: %w", err)
44+
return fmt.Errorf("failed to create tmpfs mount for params file: %w", err)
4445
}
45-
defer modifiedParamsFile.Close()
4646

47-
if _, err := modifiedParamsFile.Write(contents); err != nil {
48-
return fmt.Errorf("failed to write temporary params file: %w", err)
47+
modifiedParamsFilePath := filepath.Join(hookScratchDirPath, "nvct-params")
48+
if _, err := createFileInRoot(containerRootDirPath, modifiedParamsFilePath, 0444); err != nil {
49+
return fmt.Errorf("error creating modified params file: %w", err)
4950
}
5051

51-
err = utils.WithProcfd(containerRootDirPath, nvidiaDriverParamsPath, func(nvidiaDriverParamsFdPath string) error {
52-
return unix.Mount(modifiedParamsFile.Name(), nvidiaDriverParamsFdPath, "", unix.MS_BIND|unix.MS_RDONLY|unix.MS_NODEV|unix.MS_PRIVATE|unix.MS_NOSYMFOLLOW, "")
52+
err = utils.WithProcfd(containerRootDirPath, modifiedParamsFilePath, func(modifiedParamsFileFdPath string) error {
53+
modifiedParamsFile, err := os.OpenFile(modifiedParamsFileFdPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444)
54+
if err != nil {
55+
return fmt.Errorf("failed to open modified params file: %w", err)
56+
}
57+
defer modifiedParamsFile.Close()
58+
59+
if _, err := modifiedParamsFile.Write(contents); err != nil {
60+
return fmt.Errorf("failed to write temporary params file: %w", err)
61+
}
62+
63+
err = utils.WithProcfd(containerRootDirPath, nvidiaDriverParamsPath, func(nvidiaDriverParamsFdPath string) error {
64+
return unix.Mount(modifiedParamsFileFdPath, nvidiaDriverParamsFdPath, "", unix.MS_BIND|unix.MS_RDONLY|unix.MS_NODEV|unix.MS_PRIVATE|unix.MS_NOSYMFOLLOW, "")
65+
})
66+
if err != nil {
67+
return fmt.Errorf("failed to mount modified params file: %w", err)
68+
}
69+
70+
return nil
5371
})
5472
if err != nil {
55-
return fmt.Errorf("failed to mount modified params file: %w", err)
73+
return err
5674
}
5775

5876
return nil
@@ -61,3 +79,32 @@ func createParamsFileInContainer(containerRootDirPath string, contents []byte) e
6179
func createTmpFs(target string, size int) error {
6280
return unix.Mount("tmpfs", target, "tmpfs", 0, fmt.Sprintf("size=%d", size))
6381
}
82+
83+
// TODO(ArangoGutierrez): This function also exists in internal/ldconfig we should move this to a separate package.
84+
func createFileInRoot(containerRootDirPath string, destinationPath string, mode os.FileMode) (string, error) {
85+
dest, err := securejoin.SecureJoin(containerRootDirPath, destinationPath)
86+
if err != nil {
87+
return "", err
88+
}
89+
// Make the parent directory.
90+
destDir, destBase := filepath.Split(dest)
91+
destDirFd, err := utils.MkdirAllInRootOpen(containerRootDirPath, destDir, 0755)
92+
if err != nil {
93+
return "", fmt.Errorf("error creating parent dir: %w", err)
94+
}
95+
defer destDirFd.Close()
96+
// Make the target file. We want to avoid opening any file that is
97+
// already there because it could be a "bad" file like an invalid
98+
// device or hung tty that might cause a DoS, so we use mknodat.
99+
// destBase does not contain any "/" components, and mknodat does
100+
// not follow trailing symlinks, so we can safely just call mknodat
101+
// here.
102+
if err := unix.Mknodat(int(destDirFd.Fd()), destBase, unix.S_IFREG|uint32(mode), 0); err != nil {
103+
// If we get EEXIST, there was already an inode there and
104+
// we can consider that a success.
105+
if !errors.Is(err, unix.EEXIST) {
106+
return "", fmt.Errorf("error creating empty file: %w", err)
107+
}
108+
}
109+
return dest, nil
110+
}

tests/e2e/nvidia-container-toolkit_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ var _ = Describe("docker", Ordered, ContinueOnFailure, func() {
267267
Expect(err).ToNot(HaveOccurred())
268268

269269
tmpDirPath = GinkgoT().TempDir()
270+
_, _, err = runner.Run("mkdir -p " + tmpDirPath)
271+
Expect(err).ToNot(HaveOccurred())
270272

271273
output, _, err := runner.Run("mount | sort")
272274
Expect(err).ToNot(HaveOccurred())
@@ -280,17 +282,17 @@ var _ = Describe("docker", Ordered, ContinueOnFailure, func() {
280282
Expect(mountsAfter).To(Equal(mountsBefore))
281283
})
282284

283-
It("the nvidia-container-runtime-hook should not leak mounts", Label("legacy"), func(ctx context.Context) {
285+
It("should not leak mounts when using the nvidia-container-runtime-hook", Label("legacy"), func(ctx context.Context) {
284286
_, _, err := runner.Run("docker run --rm -i --runtime=runc -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=all --mount type=bind,source=" + tmpDirPath + ",target=/empty,bind-propagation=shared ubuntu true")
285287
Expect(err).ToNot(HaveOccurred())
286288
})
287289

288-
It("the nvidia-container-runtime should not leak mounts", func(ctx context.Context) {
290+
It("should not leak mounts when using the nvidia-container-runtime", func(ctx context.Context) {
289291
_, _, err := runner.Run("docker run --rm -i --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=all --mount type=bind,source=" + tmpDirPath + ",target=/empty,bind-propagation=shared ubuntu true")
290292
Expect(err).ToNot(HaveOccurred())
291293
})
292294

293-
It("CDI mode should not leak mounts", func(ctx context.Context) {
295+
It("should not leak mounts when using CDI mode", func(ctx context.Context) {
294296
_, _, err := runner.Run("docker run --rm -i --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=runtime.nvidia.com/gpu=all --mount type=bind,source=" + tmpDirPath + ",target=/empty,bind-propagation=shared ubuntu true")
295297
Expect(err).ToNot(HaveOccurred())
296298
})

0 commit comments

Comments
 (0)