fortify/helper/stub.go
Ophestra Umiker c201c30c7f
helper/bwrap: check args only for internal tests
Tests internal to the helper package sets crash-test-dummy as the command whenever a launch is expected to go through, and the hardcoded args are only valid for internal tests, so this characteristic is used here to exclude external tests that pass real program names and custom bwrap configurations.

Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
2024-10-09 00:21:31 +09:00

175 lines
4.5 KiB
Go

package helper
import (
"flag"
"io"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
"testing"
"git.ophivana.moe/cat/fortify/helper/bwrap"
)
// InternalChildStub is an internal function but exported because it is cross-package;
// it is part of the implementation of the helper stub.
func InternalChildStub() {
// this test mocks the helper process
if os.Getenv(FortifyHelper) != "1" ||
os.Getenv(FortifyStatus) == "-1" { // this indicates the stub is being invoked as a bwrap child without pipes
return
}
argsFD := flag.Int("args", -1, "")
statFD := flag.Int("fd", -1, "")
_ = flag.CommandLine.Parse(os.Args[4:])
switch os.Args[3] {
case "bwrap":
bwrapStub(argsFD, statFD)
default:
genericStub(argsFD, statFD)
}
os.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() {
execCommand = exec.Command
})
// replace execCommand to have the resulting *exec.Cmd launch TestHelperChildStub
execCommand = func(name string, arg ...string) *exec.Cmd {
// pass through nonexistent path
if name == "/nonexistent" && len(arg) == 0 {
return exec.Command(name)
}
return exec.Command(os.Args[0], append([]string{"-test.run=TestHelperChildStub", "--", name}, arg...)...)
}
}
func genericStub(argsFD, statFD *int) {
// simulate args pipe behaviour
func() {
if *argsFD == -1 {
panic("attempted to start helper without passing args pipe fd")
}
f := os.NewFile(uintptr(*argsFD), "|0")
if f == nil {
panic("attempted to start helper without args pipe")
}
if _, err := io.Copy(os.Stdout, f); err != nil {
panic("cannot read args: " + err.Error())
}
}()
var wait chan struct{}
// simulate status pipe behaviour
if os.Getenv(FortifyStatus) == "1" {
if *statFD == -1 {
panic("attempted to start helper with status reporting without passing status pipe fd")
}
wait = make(chan struct{})
go func() {
f := os.NewFile(uintptr(*statFD), "|1")
if f == nil {
panic("attempted to start with status reporting without status pipe")
}
if _, err := f.Write([]byte{'x'}); err != nil {
panic("cannot write to status pipe: " + err.Error())
}
// wait for status pipe close
var epoll int
if fd, err := syscall.EpollCreate1(0); err != nil {
panic("cannot open epoll fd: " + err.Error())
} else {
defer func() {
if err = syscall.Close(fd); err != nil {
panic("cannot close epoll fd: " + err.Error())
}
}()
epoll = fd
}
if err := syscall.EpollCtl(epoll, syscall.EPOLL_CTL_ADD, int(f.Fd()), &syscall.EpollEvent{}); err != nil {
panic("cannot add status pipe to epoll: " + err.Error())
}
events := make([]syscall.EpollEvent, 1)
if _, err := syscall.EpollWait(epoll, events, -1); err != nil {
panic("cannot poll status pipe: " + err.Error())
}
if events[0].Events != syscall.EPOLLERR {
panic(strconv.Itoa(int(events[0].Events)))
}
close(wait)
}()
}
if wait != nil {
<-wait
}
}
func bwrapStub(argsFD, statFD *int) {
// the bwrap launcher does not ever launch with sync fd
if *statFD != -1 {
panic("attempted to launch bwrap with status monitoring")
}
// test args pipe behaviour
func() {
if *argsFD == -1 {
panic("attempted to start bwrap without passing args pipe fd")
}
f := os.NewFile(uintptr(*argsFD), "|0")
if f == nil {
panic("attempted to start helper without args pipe")
}
got, want := new(strings.Builder), new(strings.Builder)
if _, err := io.Copy(got, f); err != nil {
panic("cannot read args: " + err.Error())
}
// hardcoded bwrap configuration used by test
if _, err := MustNewCheckedArgs((&bwrap.Config{
Unshare: nil,
Net: true,
UserNS: false,
Hostname: "localhost",
Chdir: "/nonexistent",
Clearenv: true,
NewSession: true,
DieWithParent: true,
AsInit: true,
}).Args()).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() {
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()...),
os.Environ()); err != nil {
panic("cannot start general stub: " + err.Error())
}
}