From d41b9d2d9ceb60ceb601af991441e70929801c42 Mon Sep 17 00:00:00 2001 From: Ophestra Umiker Date: Wed, 9 Oct 2024 23:51:15 +0900 Subject: [PATCH] ldd: separate Parse from Exec and trim space Signed-off-by: Ophestra Umiker --- dbus/run.go | 3 -- ldd/error.go | 26 +++++++++++--- ldd/exec.go | 18 ++++++++++ ldd/ldd.go | 28 +++------------ ldd/ldd_test.go | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 31 deletions(-) create mode 100644 ldd/exec.go create mode 100644 ldd/ldd_test.go diff --git a/dbus/run.go b/dbus/run.go index 345f12f..2aad777 100644 --- a/dbus/run.go +++ b/dbus/run.go @@ -90,9 +90,6 @@ func (p *Proxy) Start(ready chan error, output io.Writer, sandbox bool) error { // xdb-dbus-proxy bin and dependencies roBindTarget[path.Dir(toolPath)] = struct{}{} for _, ent := range proxyDeps { - if ent == nil { - continue - } if path.IsAbs(ent.Path) { roBindTarget[path.Dir(ent.Path)] = struct{}{} } diff --git a/ldd/error.go b/ldd/error.go index 51aef94..39b63ef 100644 --- a/ldd/error.go +++ b/ldd/error.go @@ -1,11 +1,27 @@ package ldd -import "fmt" +import ( + "errors" + "fmt" +) -type EntryUnexpectedSegmentsError struct { - Entry string +var ( + ErrUnexpectedSeparator = errors.New("unexpected separator") + ErrPathNotAbsolute = errors.New("path not absolute") + ErrBadLocationFormat = errors.New("bad location format") + ErrUnexpectedNewline = errors.New("unexpected newline") +) + +type EntryUnexpectedSegmentsError string + +func (e EntryUnexpectedSegmentsError) Is(err error) bool { + var eq EntryUnexpectedSegmentsError + if !errors.As(err, &eq) { + return false + } + return e == eq } -func (e *EntryUnexpectedSegmentsError) Error() string { - return fmt.Sprintf("unexpected segments in entry %q", e.Entry) +func (e EntryUnexpectedSegmentsError) Error() string { + return fmt.Sprintf("unexpected segments in entry %q", string(e)) } diff --git a/ldd/exec.go b/ldd/exec.go new file mode 100644 index 0000000..d6a2af5 --- /dev/null +++ b/ldd/exec.go @@ -0,0 +1,18 @@ +package ldd + +import ( + "fmt" + "os" + "os/exec" + "strings" +) + +func Exec(p string) ([]*Entry, error) { + t := exec.Command("ldd", p) + t.Stdout, t.Stderr = new(strings.Builder), os.Stderr + if err := t.Run(); err != nil { + return nil, err + } + + return Parse(t.Stdout.(fmt.Stringer)) +} diff --git a/ldd/ldd.go b/ldd/ldd.go index 989aeca..6c2aec3 100644 --- a/ldd/ldd.go +++ b/ldd/ldd.go @@ -1,49 +1,31 @@ package ldd import ( - "errors" "fmt" "math" - "os" - "os/exec" "path" "strconv" "strings" ) -var ( - ErrUnexpectedSeparator = errors.New("unexpected separator") - ErrPathNotAbsolute = errors.New("path not absolute") - ErrBadLocationFormat = errors.New("bad location format") -) - type Entry struct { Name string `json:"name,omitempty"` Path string `json:"path,omitempty"` Location uint64 `json:"location"` } -func Exec(p string) ([]*Entry, error) { - t := exec.Command("ldd", p) - t.Stdout = new(strings.Builder) - t.Stderr = os.Stderr - - if err := t.Run(); err != nil { - return nil, err - } - - out := t.Stdout.(fmt.Stringer).String() - payload := strings.Split(out, "\n") - +func Parse(stdout fmt.Stringer) ([]*Entry, error) { + payload := strings.Split(strings.TrimSpace(stdout.String()), "\n") result := make([]*Entry, len(payload)) for i, ent := range payload { if len(ent) == 0 { - continue + return nil, ErrUnexpectedNewline } segment := strings.SplitN(ent, " ", 5) + // location index var iL int switch len(segment) { @@ -63,7 +45,7 @@ func Exec(p string) ([]*Entry, error) { Path: segment[2], } default: - return nil, &EntryUnexpectedSegmentsError{ent} + return nil, EntryUnexpectedSegmentsError(ent) } if loc, err := parseLocation(segment[iL]); err != nil { diff --git a/ldd/ldd_test.go b/ldd/ldd_test.go new file mode 100644 index 0000000..ec8a5d0 --- /dev/null +++ b/ldd/ldd_test.go @@ -0,0 +1,95 @@ +package ldd_test + +import ( + "errors" + "reflect" + "strings" + "testing" + + "git.ophivana.moe/cat/fortify/ldd" +) + +func TestParseError(t *testing.T) { + testCases := []struct { + name, out string + wantErr error + }{ + {"unexpected newline", ` +/lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000) + +libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000) +`, ldd.ErrUnexpectedNewline}, + {"unexpected separator", ` +libzstd.so.1 = /usr/lib/libzstd.so.1 (0x7ff71bfd2000) +`, ldd.ErrUnexpectedSeparator}, + {"path not absolute", ` +libzstd.so.1 => usr/lib/libzstd.so.1 (0x7ff71bfd2000) +`, ldd.ErrPathNotAbsolute}, + {"unexpected segments", ` +meow libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000) +`, ldd.EntryUnexpectedSegmentsError("meow libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)")}, + {"bad location format", ` +libzstd.so.1 => /usr/lib/libzstd.so.1 7ff71bfd2000 +`, ldd.ErrBadLocationFormat}, + } + 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) { + t.Errorf("Parse() error = %v, wantErr %v", err, tc.wantErr) + } + }) + } +} + +func TestParse(t *testing.T) { + testCases := []struct { + file, out string + want []*ldd.Entry + }{ + {"musl /bin/kmod", ` +/lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000) +libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000) +liblzma.so.5 => /usr/lib/liblzma.so.5 (0x7ff71bf9a000) +libz.so.1 => /lib/libz.so.1 (0x7ff71bf80000) +libcrypto.so.3 => /lib/libcrypto.so.3 (0x7ff71ba00000) +libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)`, + []*ldd.Entry{ + {"/lib/ld-musl-x86_64.so.1", "", 0x7ff71c0a4000}, + {"libzstd.so.1", "/usr/lib/libzstd.so.1", 0x7ff71bfd2000}, + {"liblzma.so.5", "/usr/lib/liblzma.so.5", 0x7ff71bf9a000}, + {"libz.so.1", "/lib/libz.so.1", 0x7ff71bf80000}, + {"libcrypto.so.3", "/lib/libcrypto.so.3", 0x7ff71ba00000}, + {"libc.musl-x86_64.so.1", "/lib/ld-musl-x86_64.so.1", 0x7ff71c0a4000}, + }}, + {"glibc /nix/store/rc3n2r3nffpib2gqpxlkjx36frw6n34z-kmod-31/bin/kmod", ` +linux-vdso.so.1 (0x00007ffed65be000) +libzstd.so.1 => /nix/store/80pxmvb9q43kh9rkjagc4h41vf6dh1y6-zstd-1.5.6/lib/libzstd.so.1 (0x00007f3199cd1000) +liblzma.so.5 => /nix/store/g78jna1i5qhh8gqs4mr64648f0szqgw4-xz-5.4.7/lib/liblzma.so.5 (0x00007f3199ca2000) +libc.so.6 => /nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6 (0x00007f3199ab5000) +libpthread.so.0 => /nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libpthread.so.0 (0x00007f3199ab0000) +/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/ld-linux-x86-64.so.2 => /nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib64/ld-linux-x86-64.so.2 (0x00007f3199da5000)`, + []*ldd.Entry{ + {"linux-vdso.so.1", "", 0x00007ffed65be000}, + {"libzstd.so.1", "/nix/store/80pxmvb9q43kh9rkjagc4h41vf6dh1y6-zstd-1.5.6/lib/libzstd.so.1", 0x00007f3199cd1000}, + {"liblzma.so.5", "/nix/store/g78jna1i5qhh8gqs4mr64648f0szqgw4-xz-5.4.7/lib/liblzma.so.5", 0x00007f3199ca2000}, + {"libc.so.6", "/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6", 0x00007f3199ab5000}, + {"libpthread.so.0", "/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libpthread.so.0", 0x00007f3199ab0000}, + {"/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/ld-linux-x86-64.so.2", "/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib64/ld-linux-x86-64.so.2", 0x00007f3199da5000}, + }}, + } + 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 { + t.Errorf("Parse() error = %v", err) + } else if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Parse() got = %#v, want %#v", got, tc.want) + } + }) + } +}