ldd: require absolute pathname
All checks were successful
Test / Create distribution (push) Successful in 36s
Test / Sandbox (push) Successful in 2m22s
Test / Hakurei (push) Successful in 3m18s
Test / Hpkg (push) Successful in 4m1s
Test / Sandbox (race detector) (push) Successful in 4m12s
Test / Hakurei (race detector) (push) Successful in 5m7s
Test / Flake checks (push) Successful in 1m23s

The sandbox which ldd(1) runs in does not inherit parent work directory, so relative pathnames will not work correctly. While it is trivial to support such a use case, the use of relative pathnames is highly error-prone and generally frowned against in this project. The Exec function remains available under the same signature until v0.4.0 where it will be removed.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-11-14 21:53:10 +09:00
parent 45953b3d9c
commit 46fa104419
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
2 changed files with 29 additions and 10 deletions

View File

@ -22,15 +22,21 @@ const (
msgStaticSuffix = ": Not a valid dynamic program"
// msgStaticGlibc is a substring of the message printed to stderr by glibc on a statically linked program.
msgStaticGlibc = "not a dynamic executable"
// lddName is the file name of ldd(1) passed to exec.LookPath.
lddName = "ldd"
// lddTimeout is the maximum duration ldd(1) is allowed to ran for before it is terminated.
lddTimeout = 4 * time.Second
)
// Exec runs ldd(1) in a restrictive [container] and connects it to a [Decoder], returning resulting entries.
func Exec(ctx context.Context, msg message.Msg, p string) ([]*Entry, error) {
const (
lddName = "ldd"
lddTimeout = 4 * time.Second
)
// Resolve runs ldd(1) in a strict sandbox and connects its stdout to a [Decoder].
//
// The returned error has concrete type
// [exec.Error] or [check.AbsoluteError] for fault during lookup of ldd(1),
// [os.SyscallError] for fault creating the stdout pipe,
// [container.StartError] for fault during either stage of container setup.
// Otherwise, it passes through the return values of [Decoder.Decode].
func Resolve(ctx context.Context, msg message.Msg, pathname *check.Absolute) ([]*Entry, error) {
c, cancel := context.WithTimeout(ctx, lddTimeout)
defer cancel()
@ -41,7 +47,7 @@ func Exec(ctx context.Context, msg message.Msg, p string) ([]*Entry, error) {
return nil, err
}
z := container.NewCommand(c, msg, toolPath, lddName, p)
z := container.NewCommand(c, msg, toolPath, lddName, pathname.String())
z.Hostname = "hakurei-" + lddName
z.SeccompFlags |= seccomp.AllowMultiarch
z.SeccompPresets |= std.PresetStrict
@ -86,3 +92,15 @@ func Exec(ctx context.Context, msg message.Msg, p string) ([]*Entry, error) {
}
return entries, decodeErr
}
// Exec runs ldd(1) in a restrictive [container] and connects it to a [Decoder], returning resulting entries.
//
// Deprecated: this function takes an unchecked pathname string.
// Relative pathnames do not work in the container as working directory information is not sent.
func Exec(ctx context.Context, msg message.Msg, pathname string) ([]*Entry, error) {
if a, err := check.NewAbs(pathname); err != nil {
return nil, err
} else {
return Resolve(ctx, msg, a)
}
}

View File

@ -7,6 +7,7 @@ import (
"testing"
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/ldd"
"hakurei.app/message"
)
@ -17,7 +18,7 @@ func TestExec(t *testing.T) {
t.Run("failure", func(t *testing.T) {
t.Parallel()
_, err := ldd.Exec(t.Context(), nil, "/proc/nonexistent")
_, err := ldd.Resolve(t.Context(), nil, check.MustAbs("/proc/nonexistent"))
var exitError *exec.ExitError
if !errors.As(err, &exitError) {
@ -33,7 +34,7 @@ func TestExec(t *testing.T) {
t.Run("success", func(t *testing.T) {
msg := message.New(nil)
msg.GetLogger().SetPrefix("check: ")
if entries, err := ldd.Exec(t.Context(), nil, container.MustExecutable(msg)); err != nil {
if entries, err := ldd.Resolve(t.Context(), nil, check.MustAbs(container.MustExecutable(msg))); err != nil {
t.Fatalf("Exec: error = %v", err)
} else if testing.Verbose() {
// result cannot be measured here as build information is not known