fmsg: implement suspend in writer
All checks were successful
Test / Create distribution (push) Successful in 24s
Test / Run NixOS test (push) Successful in 2m18s

This removes the requirement to call fmsg.Exit on every exit path, and enables direct use of the "log" package. However, fmsg.BeforeExit is still encouraged when possible to catch exit on suspended output.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2025-02-16 17:26:09 +09:00
parent 33a4ab11c2
commit e599b5583d
38 changed files with 336 additions and 382 deletions

View File

@@ -3,6 +3,7 @@ package shim
import (
"context"
"errors"
"log"
"os"
"os/exec"
"os/signal"
@@ -25,12 +26,11 @@ import (
func Main() {
// sharing stdout with fortify
// USE WITH CAUTION
fmsg.SetPrefix("shim")
fmsg.Prepare("shim")
// setting this prevents ptrace
if err := internal.PR_SET_DUMPABLE__SUID_DUMP_DISABLE(); err != nil {
fmsg.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
panic("unreachable")
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
}
// receive setup payload
@@ -40,21 +40,20 @@ func Main() {
)
if f, err := proc.Receive(Env, &payload); err != nil {
if errors.Is(err, proc.ErrInvalid) {
fmsg.Fatal("invalid config descriptor")
log.Fatal("invalid config descriptor")
}
if errors.Is(err, proc.ErrNotSet) {
fmsg.Fatal("FORTIFY_SHIM not set")
log.Fatal("FORTIFY_SHIM not set")
}
fmsg.Fatalf("cannot decode shim setup payload: %v", err)
panic("unreachable")
log.Fatalf("cannot decode shim setup payload: %v", err)
} else {
fmsg.SetVerbose(payload.Verbose)
fmsg.Store(payload.Verbose)
closeSetup = f
}
if payload.Bwrap == nil {
fmsg.Fatal("bwrap config not supplied")
log.Fatal("bwrap config not supplied")
}
// restore bwrap sync fd
@@ -65,7 +64,7 @@ func Main() {
// close setup socket
if err := closeSetup(); err != nil {
fmsg.Println("cannot close setup pipe:", err)
log.Println("cannot close setup pipe:", err)
// not fatal
}
@@ -73,15 +72,15 @@ func Main() {
if s, err := os.Stat(payload.Home); err != nil {
if os.IsNotExist(err) {
if err = os.Mkdir(payload.Home, 0700); err != nil {
fmsg.Fatalf("cannot create home directory: %v", err)
log.Fatalf("cannot create home directory: %v", err)
}
} else {
fmsg.Fatalf("cannot access home directory: %v", err)
log.Fatalf("cannot access home directory: %v", err)
}
// home directory is created, proceed
} else if !s.IsDir() {
fmsg.Fatalf("data path %q is not a directory", payload.Home)
log.Fatalf("data path %q is not a directory", payload.Home)
}
var ic init0.Payload
@@ -95,10 +94,10 @@ func Main() {
// no argv, look up shell instead
var ok bool
if payload.Bwrap.SetEnv == nil {
fmsg.Fatal("no command was specified and environment is unset")
log.Fatal("no command was specified and environment is unset")
}
if ic.Argv0, ok = payload.Bwrap.SetEnv["SHELL"]; !ok {
fmsg.Fatal("no command was specified and $SHELL was unset")
log.Fatal("no command was specified and $SHELL was unset")
}
ic.Argv = []string{ic.Argv0}
@@ -110,20 +109,20 @@ func Main() {
// serve setup payload
if fd, encoder, err := proc.Setup(&extraFiles); err != nil {
fmsg.Fatalf("cannot pipe: %v", err)
log.Fatalf("cannot pipe: %v", err)
} else {
conf.SetEnv[init0.Env] = strconv.Itoa(fd)
go func() {
fmsg.VPrintln("transmitting config to init")
fmsg.Verbose("transmitting config to init")
if err = encoder.Encode(&ic); err != nil {
fmsg.Fatalf("cannot transmit init config: %v", err)
log.Fatalf("cannot transmit init config: %v", err)
}
}()
}
helper.BubblewrapName = payload.Exec[0] // resolved bwrap path by parent
if fmsg.Verbose() {
seccomp.CPrintln = fmsg.Println
if fmsg.Load() {
seccomp.CPrintln = log.Println
}
if b, err := helper.NewBwrap(
conf, path.Join(fst.Tmp, "sbin/init"),
@@ -131,7 +130,7 @@ func Main() {
extraFiles,
syncFd,
); err != nil {
fmsg.Fatalf("malformed sandbox config: %v", err)
log.Fatalf("malformed sandbox config: %v", err)
} else {
b.Stdin(os.Stdin).Stdout(os.Stdout).Stderr(os.Stderr)
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
@@ -139,15 +138,15 @@ func Main() {
// run and pass through exit code
if err = b.Start(ctx, false); err != nil {
fmsg.Fatalf("cannot start target process: %v", err)
log.Fatalf("cannot start target process: %v", err)
} else if err = b.Wait(); err != nil {
var exitError *exec.ExitError
if !errors.As(err, &exitError) {
fmsg.Println("wait:", err)
fmsg.Exit(127)
log.Printf("wait: %v", err)
internal.Exit(127)
panic("unreachable")
}
fmsg.Exit(exitError.ExitCode())
internal.Exit(exitError.ExitCode())
panic("unreachable")
}
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/gob"
"errors"
"log"
"os"
"os/exec"
"strconv"
@@ -54,8 +55,7 @@ func (s *Shim) Start(
// prepare user switcher invocation
var fsu string
if p, ok := internal.Path(internal.Fsu); !ok {
fmsg.Fatal("invalid fsu path, this copy of fortify is not compiled correctly")
panic("unreachable")
log.Fatal("invalid fsu path, this copy of fortify is not compiled correctly")
} else {
fsu = p
}
@@ -75,7 +75,7 @@ func (s *Shim) Start(
// format fsu supplementary groups
if len(supp) > 0 {
fmsg.VPrintf("attaching supplementary group ids %s", supp)
fmsg.Verbosef("attaching supplementary group ids %s", supp)
s.cmd.Env = append(s.cmd.Env, "FORTIFY_GROUPS="+strings.Join(supp, " "))
}
s.cmd.Stdin, s.cmd.Stdout, s.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
@@ -87,7 +87,7 @@ func (s *Shim) Start(
s.sync = &fd
}
fmsg.VPrintln("starting shim via fsu:", s.cmd)
fmsg.Verbose("starting shim via fsu:", s.cmd)
// withhold messages to stderr
fmsg.Suspend()
if err := s.cmd.Start(); err != nil {