diff options
Diffstat (limited to 'internal/app/start.go')
| -rw-r--r-- | internal/app/start.go | 218 |
1 files changed, 115 insertions, 103 deletions
diff --git a/internal/app/start.go b/internal/app/start.go index cc131817..6fffda55 100644 --- a/internal/app/start.go +++ b/internal/app/start.go @@ -1,11 +1,13 @@ package app import ( + "context" "errors" "fmt" "os/exec" "path/filepath" "strings" + "time" shim0 "git.gensokyo.uk/security/fortify/cmd/fshim/ipc" "git.gensokyo.uk/security/fortify/cmd/fshim/ipc/shim" @@ -15,12 +17,16 @@ import ( "git.gensokyo.uk/security/fortify/internal/system" ) -// Start selects a user switcher and starts shim. -// Note that Wait must be called regardless of error returned by Start. -func (a *app) Start() error { +const shimSetupTimeout = 5 * time.Second + +func (a *app) Run(ctx context.Context, rs *RunState) error { a.lock.Lock() defer a.lock.Unlock() + if rs == nil { + panic("attempted to pass nil state to run") + } + // resolve exec paths shimExec := [2]string{helper.BubblewrapName} if len(a.seal.command) > 0 { @@ -64,10 +70,30 @@ func (a *app) Start() error { // export sync pipe from sys a.seal.sys.bwrap.SetSync(a.seal.sys.Sync()) + // start shim via manager + waitErr := make(chan error, 1) if startTime, err := a.shim.Start(); err != nil { return err } else { - // shim start and setup success, create process state + // shim process created + rs.Start = true + + shimSetupCtx, shimSetupCancel := context.WithDeadline(ctx, time.Now().Add(shimSetupTimeout)) + defer shimSetupCancel() + + // start waiting for shim + go func() { + waitErr <- a.shim.Unwrap().Wait() + // cancel shim setup in case shim died before receiving payload + shimSetupCancel() + }() + + // send payload + if err = a.shim.Serve(shimSetupCtx); err != nil { + return err + } + + // shim accepted setup payload, create process state sd := state.State{ ID: *a.id, PID: a.shim.Unwrap().Process.Pid, @@ -81,109 +107,40 @@ func (a *app) Start() error { err0.InnerErr = c.Save(&sd) }) a.seal.sys.saveState = true - return err0.equiv("cannot save process state:") - } -} - -// StateStoreError is returned for a failed state save -type StateStoreError struct { - // whether inner function was called - Inner bool - // error returned by state.Store Do method - DoErr error - // error returned by state.Backend Save method - InnerErr error - // any other errors needing to be tracked - Err error -} - -func (e *StateStoreError) equiv(a ...any) error { - if e.Inner && e.DoErr == nil && e.InnerErr == nil && e.Err == nil { - return nil - } else { - return fmsg.WrapErrorSuffix(e, a...) - } -} - -func (e *StateStoreError) Error() string { - if e.Inner && e.InnerErr != nil { - return e.InnerErr.Error() - } - - if e.DoErr != nil { - return e.DoErr.Error() - } - - if e.Err != nil { - return e.Err.Error() - } - - return "(nil)" -} - -func (e *StateStoreError) Unwrap() (errs []error) { - errs = make([]error, 0, 3) - if e.DoErr != nil { - errs = append(errs, e.DoErr) - } - if e.InnerErr != nil { - errs = append(errs, e.InnerErr) - } - if e.Err != nil { - errs = append(errs, e.Err) - } - return -} - -type RevertCompoundError interface { - Error() string - Unwrap() []error -} - -func (a *app) Wait() (int, error) { - a.lock.Lock() - defer a.lock.Unlock() - - if a.shim == nil { - fmsg.VPrintln("shim not initialised, skipping cleanup") - return 1, nil + if err = err0.equiv("cannot save process state:"); err != nil { + return err + } } - var r int - - if cmd := a.shim.Unwrap(); cmd == nil { - // failure prior to process start - r = 255 - } else { - wait := make(chan error, 1) - go func() { wait <- cmd.Wait() }() - - select { - // wait for process and resolve exit code - case err := <-wait: - if err != nil { - var exitError *exec.ExitError - if !errors.As(err, &exitError) { - // should be unreachable - a.waitErr = err - } - - // store non-zero return code - r = exitError.ExitCode() - } else { - r = cmd.ProcessState.ExitCode() + select { + // wait for process and resolve exit code + case err := <-waitErr: + if err != nil { + var exitError *exec.ExitError + if !errors.As(err, &exitError) { + // should be unreachable + rs.WaitErr = err } - fmsg.VPrintf("process %d exited with exit code %d", cmd.Process.Pid, r) - // alternative exit path when kill was unsuccessful - case err := <-a.shim.WaitFallback(): - r = 255 - if err != nil { - fmsg.Printf("cannot terminate shim on faulted setup: %v", err) - } else { - fmsg.VPrintln("alternative exit path selected") - } + // store non-zero return code + rs.ExitCode = exitError.ExitCode() + } else { + rs.ExitCode = a.shim.Unwrap().ProcessState.ExitCode() + } + if fmsg.Verbose() { + fmsg.VPrintf("process %d exited with exit code %d", a.shim.Unwrap().Process.Pid, rs.ExitCode) } + + // this is reached when a fault makes an already running shim impossible to continue execution + // however a kill signal could not be delivered (should actually always happen like that since fsu) + // the effects of this is similar to the alternative exit path and ensures shim death + case err := <-a.shim.WaitFallback(): + rs.ExitCode = 255 + fmsg.Printf("cannot terminate shim on faulted setup: %v", err) + + // alternative exit path relying on shim behaviour on monitor process exit + case <-ctx.Done(): + fmsg.VPrintln("alternative exit path selected") } // child process exited, resume output @@ -262,5 +219,60 @@ func (a *app) Wait() (int, error) { }) e.Err = a.seal.store.Close() - return r, e.equiv("error returned during cleanup:", e) + return e.equiv("error returned during cleanup:", e) +} + +// StateStoreError is returned for a failed state save +type StateStoreError struct { + // whether inner function was called + Inner bool + // error returned by state.Store Do method + DoErr error + // error returned by state.Backend Save method + InnerErr error + // any other errors needing to be tracked + Err error +} + +func (e *StateStoreError) equiv(a ...any) error { + if e.Inner && e.DoErr == nil && e.InnerErr == nil && e.Err == nil { + return nil + } else { + return fmsg.WrapErrorSuffix(e, a...) + } +} + +func (e *StateStoreError) Error() string { + if e.Inner && e.InnerErr != nil { + return e.InnerErr.Error() + } + + if e.DoErr != nil { + return e.DoErr.Error() + } + + if e.Err != nil { + return e.Err.Error() + } + + return "(nil)" +} + +func (e *StateStoreError) Unwrap() (errs []error) { + errs = make([]error, 0, 3) + if e.DoErr != nil { + errs = append(errs, e.DoErr) + } + if e.InnerErr != nil { + errs = append(errs, e.InnerErr) + } + if e.Err != nil { + errs = append(errs, e.Err) + } + return +} + +type RevertCompoundError interface { + Error() string + Unwrap() []error } |
