Skip to content

Commit b86f9f2

Browse files
fix: provision shared libraries recursively to stop cross-distro lib mixing
provisionTool copied only the direct ldd deps of each host tool. Host binaries on Fedora (ls, mkdir) link libselinux, which itself links libpcre2 — that transitive dep was never copied, so the embedded Arch libpcre2 stayed in the chroot and host binaries loading it printed 'no version information available' (required by libselinux.so.1). ldd is now run on every provisioned library and deps are copied recursively, so the chroot's libraries all come from the host.
1 parent ca9e949 commit b86f9f2

1 file changed

Lines changed: 32 additions & 5 deletions

File tree

pkg/sandbox/rootfs.go

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -587,18 +587,42 @@ func provisionTool(name string) error {
587587
return err
588588
}
589589

590-
// Find all shared library dependencies using ldd
590+
// Provision all shared library dependencies, transitively (ldd on the tool,
591+
// then on each library), so the chroot never mixes libraries from the build
592+
// distro's embedded rootfs with the host distro's.
593+
return provisionDeps(path)
594+
}
595+
596+
// provisionDeps copies every shared library the given ELF file depends on from
597+
// the host into the sandbox, recursing through transitive dependencies. Direct
598+
// deps alone are not enough: host ls/mkdir link libselinux, which itself needs
599+
// libpcre2 — if only ls's direct deps are copied, the embedded Arch libpcre2
600+
// stays behind and produces "no version information available" warnings (or
601+
// worse) when a Fedora/Ubuntu host binary loads it.
602+
func provisionDeps(path string) error {
603+
visited := map[string]bool{}
604+
return provisionDepsRec(path, visited)
605+
}
606+
607+
func provisionDepsRec(path string, visited map[string]bool) error {
608+
if visited[path] {
609+
return nil
610+
}
611+
visited[path] = true
612+
591613
out, err := exec.Command("ldd", path).Output()
592614
if err != nil {
593-
return err
615+
// Not a dynamic executable (static binary), or ldd missing — nothing
616+
// more to do for this file.
617+
return nil
594618
}
595619

596620
for _, line := range strings.Split(string(out), "\n") {
597621
line = strings.TrimSpace(line)
598622
var libSrc string
599623

600624
if strings.Contains(line, "=>") {
601-
// Standard lib line: "libfoo.so.X => /real/path/libfoo.so.X (0x...)"
625+
// "libfoo.so.X => /real/path/libfoo.so.X (0x...)"
602626
parts := strings.Fields(line)
603627
if len(parts) >= 3 && strings.HasPrefix(parts[2], "/") {
604628
libSrc = parts[2]
@@ -611,12 +635,15 @@ func provisionTool(name string) error {
611635
}
612636
}
613637

614-
if libSrc == "" {
638+
if libSrc == "" || visited[libSrc] {
615639
continue
616640
}
617641

618642
// Provision the library preserving host paths (cross-distro compatible)
619-
_ = provisionLib(libSrc)
643+
if err := provisionLib(libSrc); err != nil {
644+
continue
645+
}
646+
_ = provisionDepsRec(libSrc, visited)
620647
}
621648
return nil
622649
}

0 commit comments

Comments
 (0)