diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-03-15 23:57:44 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-03-16 00:01:25 +0900 |
| commit | 6e7ddb2d2ec6b163dc2c178bc4d73b5943f51206 (patch) | |
| tree | 37ffe1564782377c1983909362ed6109c3f06b28 /helper | |
| parent | bac4e67867faa7d8274b684575140a65827d3a4c (diff) | |
helper: eliminate commandContext replacement
This is done more cleanly by modifying Args in cmdF.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'helper')
| -rw-r--r-- | helper/bwrap_test.go | 41 | ||||
| -rw-r--r-- | helper/cmd.go | 2 | ||||
| -rw-r--r-- | helper/cmd_test.go | 15 | ||||
| -rw-r--r-- | helper/helper.go | 7 | ||||
| -rw-r--r-- | helper/helper_test.go | 18 | ||||
| -rw-r--r-- | helper/stub.go | 72 | ||||
| -rw-r--r-- | helper/stub_test.go | 4 |
7 files changed, 87 insertions, 72 deletions
diff --git a/helper/bwrap_test.go b/helper/bwrap_test.go index 550ff34d..be0a465d 100644 --- a/helper/bwrap_test.go +++ b/helper/bwrap_test.go @@ -3,6 +3,8 @@ package helper_test import ( "context" "errors" + "fmt" + "io" "os" "os/exec" "strings" @@ -17,7 +19,7 @@ func TestBwrap(t *testing.T) { sc := &bwrap.Config{ Net: true, Hostname: "localhost", - Chdir: "/nonexistent", + Chdir: "/proc/nonexistent", Clearenv: true, NewSession: true, DieWithParent: true, @@ -26,14 +28,12 @@ func TestBwrap(t *testing.T) { t.Run("nonexistent bwrap name", func(t *testing.T) { bubblewrapName := helper.BubblewrapName - helper.BubblewrapName = "/nonexistent" - t.Cleanup(func() { - helper.BubblewrapName = bubblewrapName - }) + helper.BubblewrapName = "/proc/nonexistent" + t.Cleanup(func() { helper.BubblewrapName = bubblewrapName }) h := helper.MustNewBwrap( context.Background(), - "fortify", + "false", argsWt, false, argF, nil, nil, @@ -49,14 +49,14 @@ func TestBwrap(t *testing.T) { t.Run("valid new helper nil check", func(t *testing.T) { if got := helper.MustNewBwrap( context.TODO(), - "fortify", + "false", argsWt, false, argF, nil, nil, sc, nil, ); got == nil { t.Errorf("MustNewBwrap(%#v, %#v, %#v) got nil", - sc, argsWt, "fortify") + sc, argsWt, "false") return } }) @@ -72,7 +72,7 @@ func TestBwrap(t *testing.T) { helper.MustNewBwrap( context.TODO(), - "fortify", + "false", argsWt, false, argF, nil, nil, @@ -81,15 +81,13 @@ func TestBwrap(t *testing.T) { }) t.Run("start without pipes", func(t *testing.T) { - helper.InternalReplaceExecCommand(t) - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() stdout, stderr := new(strings.Builder), new(strings.Builder) h := helper.MustNewBwrap( - ctx, "crash-test-dummy", + ctx, os.Args[0], nil, false, - argFChecked, func(cmd *exec.Cmd) { cmd.Stdout, cmd.Stderr = stdout, stderr }, + argFChecked, func(cmd *exec.Cmd) { cmd.Stdout, cmd.Stderr = stdout, stderr; hijackBwrap(cmd) }, nil, sc, nil, ) @@ -107,14 +105,23 @@ func TestBwrap(t *testing.T) { }) t.Run("implementation compliance", func(t *testing.T) { - testHelper(t, func(ctx context.Context, cmdF func(cmd *exec.Cmd), stat bool) helper.Helper { + testHelper(t, func(ctx context.Context, setOutput func(stdoutP, stderrP *io.Writer), stat bool) helper.Helper { return helper.MustNewBwrap( - ctx, "crash-test-dummy", + ctx, os.Args[0], argsWt, stat, - argF, cmdF, + argF, func(cmd *exec.Cmd) { setOutput(&cmd.Stdout, &cmd.Stderr); hijackBwrap(cmd) }, nil, sc, nil, ) - }) + }, "exec") }) } + +func hijackBwrap(cmd *exec.Cmd) { + if cmd.Args[0] != "bwrap" { + panic(fmt.Sprintf("unexpected argv0 %q", cmd.Args[0])) + } + cmd.Err = nil + cmd.Path = os.Args[0] + cmd.Args = append([]string{os.Args[0], "-test.run=TestHelperStub", "--"}, cmd.Args...) +} diff --git a/helper/cmd.go b/helper/cmd.go index 5b66afe7..4fe978a2 100644 --- a/helper/cmd.go +++ b/helper/cmd.go @@ -42,7 +42,7 @@ func newHelperCmd( ) (cmd *helperCmd, args []string) { cmd = new(helperCmd) cmd.helperFiles, args = newHelperFiles(ctx, wt, stat, argF, extraFiles) - cmd.Cmd = commandContext(ctx, name) + cmd.Cmd = exec.CommandContext(ctx, name) cmd.Cmd.Cancel = func() error { return cmd.Process.Signal(syscall.SIGTERM) } cmd.WaitDelay = WaitDelay return diff --git a/helper/cmd_test.go b/helper/cmd_test.go index 60c0caff..9c640983 100644 --- a/helper/cmd_test.go +++ b/helper/cmd_test.go @@ -3,6 +3,7 @@ package helper_test import ( "context" "errors" + "io" "os" "os/exec" "testing" @@ -10,9 +11,9 @@ import ( "git.gensokyo.uk/security/fortify/helper" ) -func TestDirect(t *testing.T) { +func TestCmd(t *testing.T) { t.Run("start non-existent helper path", func(t *testing.T) { - h := helper.NewDirect(context.Background(), "/nonexistent", argsWt, false, argF, nil, nil) + h := helper.NewDirect(context.Background(), "/proc/nonexistent", argsWt, false, argF, nil, nil) if err := h.Start(); !errors.Is(err, os.ErrNotExist) { t.Errorf("Start: error = %v, wantErr %v", @@ -22,15 +23,17 @@ func TestDirect(t *testing.T) { t.Run("valid new helper nil check", func(t *testing.T) { if got := helper.NewDirect(context.TODO(), "fortify", argsWt, false, argF, nil, nil); got == nil { - t.Errorf("New(%q, %q) got nil", + t.Errorf("NewDirect(%q, %q) got nil", argsWt, "fortify") return } }) t.Run("implementation compliance", func(t *testing.T) { - testHelper(t, func(ctx context.Context, cmdF func(cmd *exec.Cmd), stat bool) helper.Helper { - return helper.NewDirect(ctx, "crash-test-dummy", argsWt, stat, argF, cmdF, nil) - }) + testHelper(t, func(ctx context.Context, setOutput func(stdoutP, stderrP *io.Writer), stat bool) helper.Helper { + return helper.NewDirect(ctx, os.Args[0], argsWt, stat, argF, func(cmd *exec.Cmd) { + setOutput(&cmd.Stdout, &cmd.Stderr) + }, nil) + }, "exec") }) } diff --git a/helper/helper.go b/helper/helper.go index ac45ac79..8a25fee9 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -6,17 +6,12 @@ import ( "fmt" "io" "os" - "os/exec" "time" "git.gensokyo.uk/security/fortify/helper/proc" ) -var ( - WaitDelay = 2 * time.Second - - commandContext = exec.CommandContext -) +var WaitDelay = 2 * time.Second const ( // FortifyHelper is set to 1 when args fd is enabled and 0 otherwise. diff --git a/helper/helper_test.go b/helper/helper_test.go index 3274fe5c..4bc98b5e 100644 --- a/helper/helper_test.go +++ b/helper/helper_test.go @@ -4,7 +4,7 @@ import ( "context" "errors" "fmt" - "os/exec" + "io" "strconv" "strings" "testing" @@ -36,7 +36,8 @@ func argF(argsFd, statFd int) []string { } func argFChecked(argsFd, statFd int) (args []string) { - args = make([]string, 0, 4) + args = make([]string, 0, 6) + args = append(args, "-test.run=TestHelperStub", "--") if argsFd > -1 { args = append(args, "--args", strconv.Itoa(argsFd)) } @@ -47,13 +48,14 @@ func argFChecked(argsFd, statFd int) (args []string) { } // this function tests an implementation of the helper.Helper interface -func testHelper(t *testing.T, createHelper func(ctx context.Context, cmdF func(cmd *exec.Cmd), stat bool) helper.Helper) { - helper.InternalReplaceExecCommand(t) - +func testHelper(t *testing.T, + createHelper func(ctx context.Context, setOutput func(stdoutP, stderrP *io.Writer), stat bool) helper.Helper, + prefix string, +) { t.Run("start helper with status channel and wait", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) stdout, stderr := new(strings.Builder), new(strings.Builder) - h := createHelper(ctx, func(cmd *exec.Cmd) { cmd.Stdout, cmd.Stderr = stdout, stderr }, true) + h := createHelper(ctx, func(stdoutP, stderrP *io.Writer) { *stdoutP, *stderrP = stdout, stderr }, true) t.Run("wait not yet started helper", func(t *testing.T) { defer func() { @@ -75,7 +77,7 @@ func testHelper(t *testing.T, createHelper func(ctx context.Context, cmdF func(c cancel() t.Run("start already started helper", func(t *testing.T) { - wantErr := "exec: already started" + wantErr := prefix + ": already started" if err := h.Start(); err != nil && err.Error() != wantErr { t.Errorf("Start: error = %v, wantErr %v", err, wantErr) @@ -108,7 +110,7 @@ func testHelper(t *testing.T, createHelper func(ctx context.Context, cmdF func(c ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() stdout, stderr := new(strings.Builder), new(strings.Builder) - h := createHelper(ctx, func(cmd *exec.Cmd) { cmd.Stdout, cmd.Stderr = stdout, stderr }, false) + h := createHelper(ctx, func(stdoutP, stderrP *io.Writer) { *stdoutP, *stderrP = stdout, stderr }, false) if err := h.Start(); err != nil { t.Errorf("Start() error = %v", diff --git a/helper/stub.go b/helper/stub.go index 12daa9fd..f64263cb 100644 --- a/helper/stub.go +++ b/helper/stub.go @@ -1,25 +1,24 @@ package helper import ( - "context" "flag" "fmt" "io" "os" - "os/exec" + "path" + "slices" "strconv" "strings" "syscall" - "testing" "git.gensokyo.uk/security/fortify/helper/bwrap" "git.gensokyo.uk/security/fortify/helper/proc" "git.gensokyo.uk/security/fortify/internal" ) -// InternalChildStub is an internal function but exported because it is cross-package; +// InternalHelperStub is an internal function but exported because it is cross-package; // it is part of the implementation of the helper stub. -func InternalChildStub() { +func InternalHelperStub() { // this test mocks the helper process var ap, sp string if v, ok := os.LookupEnv(FortifyHelper); !ok { @@ -33,32 +32,15 @@ func InternalChildStub() { sp = v } - switch os.Args[3] { - case "bwrap": + if len(os.Args) > 3 && os.Args[3] == "bwrap" { bwrapStub() - default: - genericStub(flagRestoreFiles(4, ap, sp)) + } else { + genericStub(flagRestoreFiles(3, ap, sp)) } internal.Exit(0) } -// InternalReplaceExecCommand is an internal function but exported because it is cross-package; -// it is part of the implementation of the helper stub. -func InternalReplaceExecCommand(t *testing.T) { - t.Cleanup(func() { commandContext = exec.CommandContext }) - - // replace execCommand to have the resulting *exec.Cmd launch TestHelperChildStub - commandContext = func(ctx context.Context, name string, arg ...string) *exec.Cmd { - // pass through nonexistent path - if name == "/nonexistent" && len(arg) == 0 { - return exec.CommandContext(ctx, name) - } - - return exec.CommandContext(ctx, os.Args[0], append([]string{"-test.run=TestHelperChildStub", "--", name}, arg...)...) - } -} - func newFile(fd int, name, p string) *os.File { present := false switch p { @@ -149,26 +131,54 @@ func bwrapStub() { sc := &bwrap.Config{ Net: true, Hostname: "localhost", - Chdir: "/nonexistent", + Chdir: "/proc/nonexistent", Clearenv: true, NewSession: true, DieWithParent: true, AsInit: true, } - if _, err := MustNewCheckedArgs(sc.Args(nil, new(proc.ExtraFilesPre), new([]proc.File))). + + efp := new(proc.ExtraFilesPre) + if t, ok := os.LookupEnv("GO_TEST_FORTIFY_BWRAP_STUB_TYPE"); ok { + switch t { + case "dbus": + sc.Net = false + sc.Hostname = "fortify-dbus" + sc.Chdir = "/" + sc.Syscall = &bwrap.SyscallPolicy{DenyDevel: true, Multiarch: true} + sc.AsInit = false + + bindTarget := []string{"/tmp/fortify.1971/12622d846cc3fe7b4c10359d01f0eb47"} + slices.Sort(bindTarget) + for _, name := range bindTarget { + sc.Bind(name, name, false, true) + } + roBindTarget := []string{"/run/user/1971", path.Dir(os.Args[0])} + slices.Sort(roBindTarget) + for _, name := range roBindTarget { + sc.Bind(name, name) + } + + // manipulate extra files list so fd ends up as 5 + efp.Append() + efp.Append() + } + } + + if _, err := MustNewCheckedArgs(sc.Args(nil, efp, new([]proc.File))). WriteTo(want); err != nil { panic("cannot read want: " + err.Error()) } - if len(flag.CommandLine.Args()) > 0 && flag.CommandLine.Args()[0] == "crash-test-dummy" && got.String() != want.String() { + if got.String() != want.String() { panic("bad bwrap args\ngot: " + got.String() + "\nwant: " + want.String()) } }() if err := syscall.Exec( - os.Args[0], - append([]string{os.Args[0], "-test.run=TestHelperChildStub", "--"}, flag.CommandLine.Args()...), + flag.CommandLine.Args()[0], + flag.CommandLine.Args(), os.Environ()); err != nil { - panic("cannot start general stub: " + err.Error()) + panic("cannot start helper stub: " + err.Error()) } } diff --git a/helper/stub_test.go b/helper/stub_test.go index 7720c0ea..1de830e3 100644 --- a/helper/stub_test.go +++ b/helper/stub_test.go @@ -6,6 +6,4 @@ import ( "git.gensokyo.uk/security/fortify/helper" ) -func TestHelperChildStub(t *testing.T) { - helper.InternalChildStub() -} +func TestHelperStub(t *testing.T) { helper.InternalHelperStub() } |
