From 0abd9ed3cd713254d66aae7621846a98de8d912f Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Tue, 11 Nov 2025 11:18:38 +0100 Subject: [PATCH 1/3] Fix update of ldcache on non-debian containers This change ensures that the ldcache in a non-debian container includes libraries at /lib64 and /usr/lib64 when running on debian host. This is required because the system search paths do not include these folders by default resulting in a non-debian container missing system libraries from the ldcache. Signed-off-by: Evan Lezar --- internal/ldconfig/ldconfig.go | 37 ++++++++++++++++------ internal/ldconfig/ldconfig_test.go | 2 +- tests/e2e/nvidia-container-toolkit_test.go | 19 +++++++++++ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/internal/ldconfig/ldconfig.go b/internal/ldconfig/ldconfig.go index 4c9939f1e..eae1f4d1f 100644 --- a/internal/ldconfig/ldconfig.go +++ b/internal/ldconfig/ldconfig.go @@ -67,7 +67,8 @@ func NewRunner(id string, ldconfigPath string, containerRoot string, additionala // This struct is used to perform operations on the ldcache and libraries in a // particular root (e.g. a container). // -// args[0] is the reexec initializer function name +// args[0] is the reexec initializer function name and is required. +// // The following flags are required: // // --ldconfig-path=LDCONFIG_PATH the path to ldconfig on the host @@ -76,16 +77,20 @@ func NewRunner(id string, ldconfigPath string, containerRoot string, additionala // The following flags are optional: // // --is-debian-like-host Indicates that the host system is debian-based. +// See https://github.com/NVIDIA/nvidia-container-toolkit/pull/1444 // // The remaining args are folders where soname symlinks need to be created. func NewFromArgs(args ...string) (*Ldconfig, error) { if len(args) < 1 { return nil, fmt.Errorf("incorrect arguments: %v", args) } - fs := flag.NewFlagSet(args[1], flag.ExitOnError) + fs := flag.NewFlagSet("ldconfig-options", flag.ExitOnError) ldconfigPath := fs.String("ldconfig-path", "", "the path to ldconfig on the host") containerRoot := fs.String("container-root", "", "the path in which ldconfig must be run") - isDebianLikeHost := fs.Bool("is-debian-like-host", false, "the hook is running from a Debian-like host") + isDebianLikeHost := fs.Bool("is-debian-like-host", false, `indicates that the host system is debian-based. +This allows us to handle the case where there are differences in behavior +between the ldconfig from the host (as executed from an update-ldcache hook) and +ldconfig in the container. Such differences include system search paths.`) if err := fs.Parse(args[1:]); err != nil { return nil, err } @@ -116,7 +121,7 @@ func (l *Ldconfig) UpdateLDCache() error { // Explicitly specify using /etc/ld.so.conf since the host's ldconfig may // be configured to use a different config file by default. const topLevelLdsoconfFilePath = "/etc/ld.so.conf" - filteredDirectories, err := l.filterDirectories(topLevelLdsoconfFilePath, l.directories...) + filteredDirectories, ldconfigDirs, err := l.filterDirectories(topLevelLdsoconfFilePath, l.directories...) if err != nil { return err } @@ -126,6 +131,19 @@ func (l *Ldconfig) UpdateLDCache() error { "-f", topLevelLdsoconfFilePath, "-C", "/etc/ld.so.cache", } + // If we are running in a non-debian container on a debian host we also + // need to add the system directories for non-debian hosts to the list of + // folders processed by ldconfig. + // We only do this if they are not already tracked, since the folders on + // on the command line have a higher priority than folders in ld.so.conf. + if l.isDebianLikeHost && !l.isDebianLikeContainer { + for _, systemSearchPath := range l.getSystemSearchPaths() { + if _, ok := ldconfigDirs[systemSearchPath]; ok { + continue + } + args = append(args, systemSearchPath) + } + } if err := createLdsoconfdFile(ldsoconfdFilenamePattern, filteredDirectories...); err != nil { return fmt.Errorf("failed to update ld.so.conf.d: %w", err) @@ -163,10 +181,10 @@ func (l *Ldconfig) prepareRoot() (string, error) { return ldconfigPath, nil } -func (l *Ldconfig) filterDirectories(configFilePath string, directories ...string) ([]string, error) { +func (l *Ldconfig) filterDirectories(configFilePath string, directories ...string) ([]string, map[string]struct{}, error) { ldconfigDirs, err := l.getLdsoconfDirectories(configFilePath) if err != nil { - return nil, err + return nil, nil, err } var filtered []string @@ -175,8 +193,9 @@ func (l *Ldconfig) filterDirectories(configFilePath string, directories ...strin continue } filtered = append(filtered, d) + ldconfigDirs[d] = struct{}{} } - return filtered, nil + return filtered, ldconfigDirs, nil } // createLdsoconfdFile creates a file at /etc/ld.so.conf.d/. @@ -224,7 +243,7 @@ func createLdsoconfdFile(pattern string, dirs ...string) error { // files that refer to the directory. func (l *Ldconfig) getLdsoconfDirectories(configFilePath string) (map[string]struct{}, error) { ldconfigDirs := make(map[string]struct{}) - for _, d := range l.getSystemSerachPaths() { + for _, d := range l.getSystemSearchPaths() { ldconfigDirs[d] = struct{}{} } @@ -253,7 +272,7 @@ func (l *Ldconfig) getLdsoconfDirectories(configFilePath string) (map[string]str return ldconfigDirs, nil } -func (l *Ldconfig) getSystemSerachPaths() []string { +func (l *Ldconfig) getSystemSearchPaths() []string { if l.isDebianLikeContainer { debianSystemSearchPaths() } diff --git a/internal/ldconfig/ldconfig_test.go b/internal/ldconfig/ldconfig_test.go index a22876719..308faa0ec 100644 --- a/internal/ldconfig/ldconfig_test.go +++ b/internal/ldconfig/ldconfig_test.go @@ -117,7 +117,7 @@ include INCLUDED_PATTERN* l := &Ldconfig{ isDebianLikeContainer: true, } - filtered, err := l.filterDirectories(topLevelConfPath, tc.input...) + filtered, _, err := l.filterDirectories(topLevelConfPath, tc.input...) require.NoError(t, err) require.Equal(t, tc.expected, filtered) diff --git a/tests/e2e/nvidia-container-toolkit_test.go b/tests/e2e/nvidia-container-toolkit_test.go index 69086301b..33181345e 100644 --- a/tests/e2e/nvidia-container-toolkit_test.go +++ b/tests/e2e/nvidia-container-toolkit_test.go @@ -570,4 +570,23 @@ EOF`) Expect(output).To(Equal(expectedOutput)) }) }) + + When("running a ubi9 container", Ordered, func() { + var ( + expectedOutput string + ) + BeforeAll(func(ctx context.Context) { + _, _, err := runner.Run(`docker pull redhat/ubi9`) + Expect(err).ToNot(HaveOccurred()) + + expectedOutput, _, err = runner.Run(`docker run --rm --runtime=runc redhat/ubi9 bash -c "ldconfig -p | grep libc.so."`) + Expect(err).ToNot(HaveOccurred()) + }) + + It("should include the system libraries when using the nvidia-container-runtime", func(ctx context.Context) { + output, _, err := runner.Run(`docker run --rm --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=all redhat/ubi9 bash -c "ldconfig -p | grep libc.so."`) + Expect(err).ToNot(HaveOccurred()) + Expect(output).To(Equal(expectedOutput)) + }) + }) }) From bfe98f14e47d133cd5270005734d4b963d34623c Mon Sep 17 00:00:00 2001 From: Jean-Francois Roy Date: Fri, 14 Nov 2025 11:17:24 -0800 Subject: [PATCH 2/3] Add missing return in `getSystemSearchPaths` for debian like containers Signed-off-by: Evan Lezar --- internal/ldconfig/ldconfig.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ldconfig/ldconfig.go b/internal/ldconfig/ldconfig.go index eae1f4d1f..8df6245dd 100644 --- a/internal/ldconfig/ldconfig.go +++ b/internal/ldconfig/ldconfig.go @@ -274,7 +274,7 @@ func (l *Ldconfig) getLdsoconfDirectories(configFilePath string) (map[string]str func (l *Ldconfig) getSystemSearchPaths() []string { if l.isDebianLikeContainer { - debianSystemSearchPaths() + return debianSystemSearchPaths() } return nonDebianSystemSearchPaths() } From 70cca0349f039e616ed15c069a8eb3da24ce3f53 Mon Sep 17 00:00:00 2001 From: Jean-Francois Roy Date: Thu, 13 Nov 2025 16:56:21 -0800 Subject: [PATCH 3/3] ldconfig: Determine container "debian-ness" after root pivot Signed-off-by: Evan Lezar --- internal/ldconfig/ldconfig.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/ldconfig/ldconfig.go b/internal/ldconfig/ldconfig.go index 8df6245dd..49a21c860 100644 --- a/internal/ldconfig/ldconfig.go +++ b/internal/ldconfig/ldconfig.go @@ -103,11 +103,10 @@ ldconfig in the container. Such differences include system search paths.`) } l := &Ldconfig{ - ldconfigPath: *ldconfigPath, - inRoot: *containerRoot, - isDebianLikeHost: *isDebianLikeHost, - isDebianLikeContainer: isDebian(), - directories: fs.Args(), + ldconfigPath: *ldconfigPath, + inRoot: *containerRoot, + isDebianLikeHost: *isDebianLikeHost, + directories: fs.Args(), } return l, nil } @@ -118,6 +117,9 @@ func (l *Ldconfig) UpdateLDCache() error { return err } + // `prepareRoot` pivots to the container root, so can now set the container "debian-ness". + l.isDebianLikeContainer = isDebian() + // Explicitly specify using /etc/ld.so.conf since the host's ldconfig may // be configured to use a different config file by default. const topLevelLdsoconfFilePath = "/etc/ld.so.conf"