From 4bb5d9780f3c46e942dc778d93c28391caee33c7 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Fri, 14 Mar 2025 17:51:29 +0900 Subject: [PATCH] ldd: run in native sandbox Signed-off-by: Ophestra --- flake.nix | 1 - internal/sandbox/container_test.go | 16 ++++++--- ldd/exec.go | 52 +++++++++++++++--------------- ldd/ldd.go | 5 ++- ldd/ldd_test.go | 11 ++----- package.nix | 1 - 6 files changed, 41 insertions(+), 45 deletions(-) diff --git a/flake.nix b/flake.nix index 55aa2e1..b1929d5 100644 --- a/flake.nix +++ b/flake.nix @@ -140,7 +140,6 @@ gcc pkg-config wayland-scanner - bubblewrap ] ++ ( with pkgs.pkgsStatic; diff --git a/internal/sandbox/container_test.go b/internal/sandbox/container_test.go index 082d0dc..6ef8cec 100644 --- a/internal/sandbox/container_test.go +++ b/internal/sandbox/container_test.go @@ -69,10 +69,7 @@ func TestContainer(t *testing.T) { container.Uid = 1000 container.Gid = 100 container.Hostname = tc.host - container.CommandContext = func(ctx context.Context) *exec.Cmd { - return exec.CommandContext(ctx, os.Args[0], "-test.v", - "-test.run=TestHelperInit", "--", "init") - } + container.CommandContext = commandContext container.Flags |= tc.flags container.Stdout, container.Stderr = os.Stdout, os.Stderr container.Ops = tc.ops @@ -89,7 +86,11 @@ func TestContainer(t *testing.T) { Bind(os.Args[0], os.Args[0], 0) // in case test has cgo enabled var libPaths []string - if entries, err := ldd.Exec(ctx, os.Args[0]); err != nil { + if entries, err := ldd.ExecFilter(ctx, + commandContext, + func(v []byte) []byte { + return bytes.SplitN(v, []byte("TestHelperInit\n"), 2)[1] + }, os.Args[0]); err != nil { log.Fatalf("ldd: %v", err) } else { libPathsM := make(map[string]struct{}, len(entries)) @@ -175,3 +176,8 @@ func TestHelperCheckContainer(t *testing.T) { t.Run("seccomp", func(t *testing.T) { check.MustAssertSeccomp() }) t.Run("mntent", func(t *testing.T) { check.MustAssertMounts("", "/proc/mounts", "/proc/self/fd/0") }) } + +func commandContext(ctx context.Context) *exec.Cmd { + return exec.CommandContext(ctx, os.Args[0], "-test.v", + "-test.run=TestHelperInit", "--", "init") +} diff --git a/ldd/exec.go b/ldd/exec.go index 5c51c6a..4f77ae3 100644 --- a/ldd/exec.go +++ b/ldd/exec.go @@ -7,8 +7,7 @@ import ( "os/exec" "time" - "git.gensokyo.uk/security/fortify/helper" - "git.gensokyo.uk/security/fortify/helper/bwrap" + "git.gensokyo.uk/security/fortify/internal/sandbox" ) const lddTimeout = 2 * time.Second @@ -18,34 +17,31 @@ var ( msgStaticGlibc = []byte("not a dynamic executable") ) -func Exec(ctx context.Context, p string) ([]*Entry, error) { - var h helper.Helper - - if toolPath, err := exec.LookPath("ldd"); err != nil { - return nil, err - } else if h, err = helper.NewBwrap( - (&bwrap.Config{ - Hostname: "fortify-ldd", - Chdir: "/", - Syscall: &bwrap.SyscallPolicy{DenyDevel: true, Multiarch: true}, - NewSession: true, - DieWithParent: true, - }).Bind("/", "/").DevTmpfs("/dev"), toolPath, false, - nil, func(_, _ int) []string { return []string{p} }, - nil, nil, - ); err != nil { - return nil, err - } - - stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) - h.Stdout(stdout).Stderr(stderr) +func Exec(ctx context.Context, p string) ([]*Entry, error) { return ExecFilter(ctx, nil, nil, p) } +func ExecFilter(ctx context.Context, + commandContext func(context.Context) *exec.Cmd, + f func([]byte) []byte, + p string) ([]*Entry, error) { c, cancel := context.WithTimeout(ctx, lddTimeout) defer cancel() - if err := h.Start(c, false); err != nil { + container := sandbox.New(c, "ldd", p) + container.Hostname = "fortify-ldd" + stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) + container.Stdout = stdout + container.Stderr = stderr + container.Bind("/", "/", 0).Dev("/dev") + + if commandContext != nil { + container.CommandContext = commandContext + } + + if err := container.Start(); err != nil { + return nil, err + } else if err = container.Serve(); err != nil { return nil, err } - if err := h.Wait(); err != nil { + if err := container.Wait(); err != nil { m := stderr.Bytes() if bytes.Contains(m, append([]byte(p+": "), msgStatic...)) || bytes.Contains(m, msgStaticGlibc) { @@ -56,5 +52,9 @@ func Exec(ctx context.Context, p string) ([]*Entry, error) { return nil, err } - return Parse(stdout) + v := stdout.Bytes() + if f != nil { + v = f(v) + } + return Parse(v) } diff --git a/ldd/ldd.go b/ldd/ldd.go index 434fbab..ca4156c 100644 --- a/ldd/ldd.go +++ b/ldd/ldd.go @@ -2,7 +2,6 @@ package ldd import ( - "fmt" "math" "path" "strconv" @@ -15,8 +14,8 @@ type Entry struct { Location uint64 `json:"location"` } -func Parse(stdout fmt.Stringer) ([]*Entry, error) { - payload := strings.Split(strings.TrimSpace(stdout.String()), "\n") +func Parse(p []byte) ([]*Entry, error) { + payload := strings.Split(strings.TrimSpace(string(p)), "\n") result := make([]*Entry, len(payload)) for i, ent := range payload { diff --git a/ldd/ldd_test.go b/ldd/ldd_test.go index 2e5d010..9c348c3 100644 --- a/ldd/ldd_test.go +++ b/ldd/ldd_test.go @@ -3,7 +3,6 @@ package ldd_test import ( "errors" "reflect" - "strings" "testing" "git.gensokyo.uk/security/fortify/ldd" @@ -34,10 +33,7 @@ libzstd.so.1 => /usr/lib/libzstd.so.1 7ff71bfd2000 } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - stdout := new(strings.Builder) - stdout.WriteString(tc.out) - - if _, err := ldd.Parse(stdout); !errors.Is(err, tc.wantErr) { + if _, err := ldd.Parse([]byte(tc.out)); !errors.Is(err, tc.wantErr) { t.Errorf("Parse() error = %v, wantErr %v", err, tc.wantErr) } }) @@ -111,10 +107,7 @@ libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)`, } for _, tc := range testCases { t.Run(tc.file, func(t *testing.T) { - stdout := new(strings.Builder) - stdout.WriteString(tc.out) - - if got, err := ldd.Parse(stdout); err != nil { + if got, err := ldd.Parse([]byte(tc.out)); err != nil { t.Errorf("Parse() error = %v", err) } else if !reflect.DeepEqual(got, tc.want) { t.Errorf("Parse() got = %#v, want %#v", got, tc.want) diff --git a/package.nix b/package.nix index bdf95ca..3088ec3 100644 --- a/package.nix +++ b/package.nix @@ -73,7 +73,6 @@ buildGoModule rec { pkg-config wayland-scanner makeBinaryWrapper - bubblewrap ]; preBuild = ''