Compare commits

..

No commits in common. "master" and "v0.3.0" have entirely different histories.

162 changed files with 2563 additions and 4993 deletions

View File

@ -1,2 +0,0 @@
ColumnLimit: 0
IndentWidth: 4

View File

@ -11,24 +11,21 @@ import (
"strconv" "strconv"
"sync" "sync"
"time" "time"
_ "unsafe" // for go:linkname _ "unsafe"
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/dbus" "hakurei.app/internal"
"hakurei.app/internal/env" "hakurei.app/internal/env"
"hakurei.app/internal/info"
"hakurei.app/internal/outcome" "hakurei.app/internal/outcome"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system/dbus"
) )
// optionalErrorUnwrap calls [errors.Unwrap] and returns the resulting value
// if it is not nil, or the original value if it is.
//
//go:linkname optionalErrorUnwrap hakurei.app/container.optionalErrorUnwrap //go:linkname optionalErrorUnwrap hakurei.app/container.optionalErrorUnwrap
func optionalErrorUnwrap(err error) error func optionalErrorUnwrap(_ error) error
func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErrs, out io.Writer) command.Command { func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErrs, out io.Writer) command.Command {
var ( var (
@ -55,26 +52,20 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
c.Command("shim", command.UsageInternal, func([]string) error { outcome.Shim(msg); return errSuccess }) c.Command("shim", command.UsageInternal, func([]string) error { outcome.Shim(msg); return errSuccess })
{ c.Command("app", "Load and start container from configuration file", func(args []string) error {
var (
flagIdentifierFile int
)
c.NewCommand("app", "Load and start container from configuration file", func(args []string) error {
if len(args) < 1 { if len(args) < 1 {
log.Fatal("app requires at least 1 argument") log.Fatal("app requires at least 1 argument")
} }
// config extraArgs...
config := tryPath(msg, args[0]) config := tryPath(msg, args[0])
if config != nil && config.Container != nil { if config != nil && config.Container != nil {
config.Container.Args = append(config.Container.Args, args[1:]...) config.Container.Args = append(config.Container.Args, args[1:]...)
} }
outcome.Main(ctx, msg, config, flagIdentifierFile) outcome.Main(ctx, msg, config)
panic("unreachable") panic("unreachable")
}). })
Flag(&flagIdentifierFile, "identifier-fd", command.IntFlag(-1),
"Write identifier of current instance to fd after successful startup")
}
{ {
var ( var (
@ -266,7 +257,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
} }
} }
outcome.Main(ctx, msg, config, -1) outcome.Main(ctx, msg, config)
panic("unreachable") panic("unreachable")
}). }).
Flag(&flagDBusConfigSession, "dbus-config", command.StringFlag("builtin"), Flag(&flagDBusConfigSession, "dbus-config", command.StringFlag("builtin"),
@ -353,7 +344,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
}).Flag(&flagShort, "short", command.BoolFlag(false), "Print instance id") }).Flag(&flagShort, "short", command.BoolFlag(false), "Print instance id")
} }
c.Command("version", "Display version information", func(args []string) error { fmt.Println(info.Version()); return errSuccess }) c.Command("version", "Display version information", func(args []string) error { fmt.Println(internal.Version()); return errSuccess })
c.Command("license", "Show full license text", func(args []string) error { fmt.Println(license); return errSuccess }) c.Command("license", "Show full license text", func(args []string) error { fmt.Println(license); return errSuccess })
c.Command("template", "Produce a config template", func(args []string) error { encodeJSON(log.Fatal, os.Stdout, false, hst.Template()); return errSuccess }) c.Command("template", "Produce a config template", func(args []string) error { encodeJSON(log.Fatal, os.Stdout, false, hst.Template()); return errSuccess })
c.Command("help", "Show this help message", func([]string) error { c.PrintHelp(); return errSuccess }) c.Command("help", "Show this help message", func([]string) error { c.PrintHelp(); return errSuccess })

View File

@ -1,13 +1,18 @@
package main package main_test
import ( import (
"io"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
_ "unsafe"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
//go:linkname decodeJSON hakurei.app/cmd/hakurei.decodeJSON
func decodeJSON(fatal func(v ...any), op string, r io.Reader, v any)
func TestDecodeJSON(t *testing.T) { func TestDecodeJSON(t *testing.T) {
t.Parallel() t.Parallel()
@ -57,6 +62,9 @@ func TestDecodeJSON(t *testing.T) {
} }
} }
//go:linkname encodeJSON hakurei.app/cmd/hakurei.encodeJSON
func encodeJSON(fatal func(v ...any), output io.Writer, short bool, v any)
func TestEncodeJSON(t *testing.T) { func TestEncodeJSON(t *testing.T) {
t.Parallel() t.Parallel()
@ -66,7 +74,7 @@ func TestEncodeJSON(t *testing.T) {
want string want string
}{ }{
{"marshaler", errorJSONMarshaler{}, {"marshaler", errorJSONMarshaler{},
`cannot encode json for main.errorJSONMarshaler: unique error 3735928559 injected by the test suite`}, `cannot encode json for main_test.errorJSONMarshaler: unique error 3735928559 injected by the test suite`},
{"default", func() {}, {"default", func() {},
`cannot write json: json: unsupported type: func()`}, `cannot write json: json: unsupported type: func()`},
} }

View File

@ -11,7 +11,6 @@ import (
"syscall" "syscall"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/outcome"
"hakurei.app/internal/store" "hakurei.app/internal/store"
"hakurei.app/message" "hakurei.app/message"
) )
@ -54,23 +53,14 @@ func tryFd(msg message.Msg, name string) io.ReadCloser {
} }
return nil return nil
} else { } else {
if v < 3 { // reject standard streams
return nil
}
msg.Verbosef("trying config stream from %d", v) msg.Verbosef("trying config stream from %d", v)
fd := uintptr(v) fd := uintptr(v)
if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_GETFD, 0); errno != 0 { if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_GETFD, 0); errno != 0 {
if errors.Is(errno, syscall.EBADF) { // reject bad fd if errors.Is(errno, syscall.EBADF) {
return nil return nil
} }
log.Fatalf("cannot get fd %d: %v", fd, errno) log.Fatalf("cannot get fd %d: %v", fd, errno)
} }
if outcome.IsPollDescriptor(fd) { // reject runtime internals
log.Fatalf("invalid config stream %d", fd)
}
return os.NewFile(fd, strconv.Itoa(v)) return os.NewFile(fd, strconv.Itoa(v))
} }
} }

View File

@ -12,8 +12,8 @@ import (
"time" "time"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal"
"hakurei.app/internal/env" "hakurei.app/internal/env"
"hakurei.app/internal/info"
"hakurei.app/internal/outcome" "hakurei.app/internal/outcome"
"hakurei.app/internal/store" "hakurei.app/internal/store"
"hakurei.app/message" "hakurei.app/message"
@ -24,20 +24,20 @@ func printShowSystem(output io.Writer, short, flagJSON bool) {
t := newPrinter(output) t := newPrinter(output)
defer t.MustFlush() defer t.MustFlush()
hi := &hst.Info{Version: info.Version(), User: new(outcome.Hsu).MustID(nil)} info := &hst.Info{Version: internal.Version(), User: new(outcome.Hsu).MustID(nil)}
env.CopyPaths().Copy(&hi.Paths, hi.User) env.CopyPaths().Copy(&info.Paths, info.User)
if flagJSON { if flagJSON {
encodeJSON(log.Fatal, output, short, hi) encodeJSON(log.Fatal, output, short, info)
return return
} }
t.Printf("Version:\t%s\n", hi.Version) t.Printf("Version:\t%s\n", info.Version)
t.Printf("User:\t%d\n", hi.User) t.Printf("User:\t%d\n", info.User)
t.Printf("TempDir:\t%s\n", hi.TempDir) t.Printf("TempDir:\t%s\n", info.TempDir)
t.Printf("SharePath:\t%s\n", hi.SharePath) t.Printf("SharePath:\t%s\n", info.SharePath)
t.Printf("RuntimePath:\t%s\n", hi.RuntimePath) t.Printf("RuntimePath:\t%s\n", info.RuntimePath)
t.Printf("RunDirPath:\t%s\n", hi.RunDirPath) t.Printf("RunDirPath:\t%s\n", info.RunDirPath)
} }
// printShowInstance writes a representation of [hst.State] or [hst.Config] to output. // printShowInstance writes a representation of [hst.State] or [hst.Config] to output.
@ -90,6 +90,12 @@ func printShowInstance(
t.Printf(" Groups:\t%s\n", strings.Join(config.Groups, ", ")) t.Printf(" Groups:\t%s\n", strings.Join(config.Groups, ", "))
} }
if config.Container != nil { if config.Container != nil {
if config.Container.Home != nil {
t.Printf(" Home:\t%s\n", config.Container.Home)
}
if config.Container.Hostname != "" {
t.Printf(" Hostname:\t%s\n", config.Container.Hostname)
}
flags := config.Container.Flags.String() flags := config.Container.Flags.String()
// this is included in the upper hst.Config struct but is relevant here // this is included in the upper hst.Config struct but is relevant here
@ -104,12 +110,6 @@ func printShowInstance(
} }
t.Printf(" Flags:\t%s\n", flags) t.Printf(" Flags:\t%s\n", flags)
if config.Container.Home != nil {
t.Printf(" Home:\t%s\n", config.Container.Home)
}
if config.Container.Hostname != "" {
t.Printf(" Hostname:\t%s\n", config.Container.Hostname)
}
if config.Container.Path != nil { if config.Container.Path != nil {
t.Printf(" Path:\t%s\n", config.Container.Path) t.Printf(" Path:\t%s\n", config.Container.Path)
} }

View File

@ -64,9 +64,9 @@ func TestPrintShowInstance(t *testing.T) {
Identity: 9 (org.chromium.Chromium) Identity: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pulseaudio Enablements: wayland, dbus, pulseaudio
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
@ -161,9 +161,9 @@ App
Identity: 9 (org.chromium.Chromium) Identity: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pulseaudio Enablements: wayland, dbus, pulseaudio
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland

View File

@ -10,11 +10,11 @@ import (
"os/exec" "os/exec"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/info" "hakurei.app/internal"
"hakurei.app/message" "hakurei.app/message"
) )
var hakureiPathVal = info.MustHakureiPath().String() var hakureiPathVal = internal.MustHakureiPath().String()
func mustRunApp(ctx context.Context, msg message.Msg, config *hst.Config, beforeFail func()) { func mustRunApp(ctx context.Context, msg message.Msg, config *hst.Config, beforeFail func()) {
var ( var (

View File

@ -56,7 +56,7 @@ func NewAbs(pathname string) (*Absolute, error) {
// MustAbs calls [NewAbs] and panics on error. // MustAbs calls [NewAbs] and panics on error.
func MustAbs(pathname string) *Absolute { func MustAbs(pathname string) *Absolute {
if a, err := NewAbs(pathname); err != nil { if a, err := NewAbs(pathname); err != nil {
panic(err) panic(err.Error())
} else { } else {
return a return a
} }

View File

@ -9,15 +9,13 @@ import (
"strings" "strings"
"syscall" "syscall"
"testing" "testing"
_ "unsafe" // for go:linkname _ "unsafe"
. "hakurei.app/container/check" . "hakurei.app/container/check"
) )
// unsafeAbs returns check.Absolute on any string value.
//
//go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs //go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs
func unsafeAbs(pathname string) *Absolute func unsafeAbs(_ string) *Absolute
func TestAbsoluteError(t *testing.T) { func TestAbsoluteError(t *testing.T) {
t.Parallel() t.Parallel()
@ -84,9 +82,9 @@ func TestNewAbs(t *testing.T) {
t.Parallel() t.Parallel()
defer func() { defer func() {
wantPanic := &AbsoluteError{Pathname: "etc"} wantPanic := `path "etc" is not absolute`
if r := recover(); !reflect.DeepEqual(r, wantPanic) { if r := recover(); r != wantPanic {
t.Errorf("MustAbs: panic = %v; want %v", r, wantPanic) t.Errorf("MustAbs: panic = %v; want %v", r, wantPanic)
} }
}() }()

View File

@ -11,7 +11,6 @@ import (
"os/exec" "os/exec"
"runtime" "runtime"
"strconv" "strconv"
"sync"
. "syscall" . "syscall"
"time" "time"
@ -144,18 +143,11 @@ func (e *StartError) Error() string {
// Message returns a user-facing error message. // Message returns a user-facing error message.
func (e *StartError) Message() string { func (e *StartError) Message() string {
if e.Passthrough { if e.Passthrough {
var (
numError *strconv.NumError
)
switch { switch {
case errors.As(e.Err, new(*os.PathError)), case errors.As(e.Err, new(*os.PathError)),
errors.As(e.Err, new(*os.SyscallError)): errors.As(e.Err, new(*os.SyscallError)):
return "cannot " + e.Err.Error() return "cannot " + e.Err.Error()
case errors.As(e.Err, &numError) && numError != nil:
return "cannot parse " + strconv.Quote(numError.Num) + ": " + numError.Err.Error()
default: default:
return e.Err.Error() return e.Err.Error()
} }
@ -166,39 +158,6 @@ func (e *StartError) Message() string {
return "cannot " + e.Error() return "cannot " + e.Error()
} }
// for ensureCloseOnExec
var (
closeOnExecOnce sync.Once
closeOnExecErr error
)
// ensureCloseOnExec ensures all currently open file descriptors have the syscall.FD_CLOEXEC flag set.
// This is only ran once as it is intended to handle files left open by the parent, and any file opened
// on this side should already have syscall.FD_CLOEXEC set.
func ensureCloseOnExec() error {
closeOnExecOnce.Do(func() {
const fdPrefixPath = "/proc/self/fd/"
var entries []os.DirEntry
if entries, closeOnExecErr = os.ReadDir(fdPrefixPath); closeOnExecErr != nil {
return
}
var fd int
for _, ent := range entries {
if fd, closeOnExecErr = strconv.Atoi(ent.Name()); closeOnExecErr != nil {
break // not reached
}
CloseOnExec(fd)
}
})
if closeOnExecErr == nil {
return nil
}
return &StartError{Fatal: true, Step: "set FD_CLOEXEC on all open files", Err: closeOnExecErr, Passthrough: true}
}
// Start starts the container init. The init process blocks until Serve is called. // Start starts the container init. The init process blocks until Serve is called.
func (p *Container) Start() error { func (p *Container) Start() error {
if p == nil || p.cmd == nil || if p == nil || p.cmd == nil ||
@ -209,10 +168,6 @@ func (p *Container) Start() error {
return errors.New("container: already started") return errors.New("container: already started")
} }
if err := ensureCloseOnExec(); err != nil {
return err
}
// map to overflow id to work around ownership checks // map to overflow id to work around ownership checks
if p.Uid < 1 { if p.Uid < 1 {
p.Uid = OverflowUid(p.msg) p.Uid = OverflowUid(p.msg)

View File

@ -21,7 +21,6 @@ import (
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/fhs"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/container/vfs" "hakurei.app/container/vfs"
@ -30,45 +29,6 @@ import (
"hakurei.app/message" "hakurei.app/message"
) )
// Note: this package requires cgo, which is unavailable in the Go playground.
func Example() {
// Must be called early if the current process starts containers.
container.TryArgv0(nil)
// Configure the container.
z := container.New(context.Background(), nil)
z.Hostname = "hakurei-example"
z.Proc(fhs.AbsProc).Dev(fhs.AbsDev, true)
z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr
// Bind / for demonstration.
z.Bind(fhs.AbsRoot, fhs.AbsRoot, 0)
if name, err := exec.LookPath("hostname"); err != nil {
panic(err)
} else {
z.Path = check.MustAbs(name)
}
// This completes the first stage of container setup and starts the container init process.
// The new process blocks until the Serve method is called.
if err := z.Start(); err != nil {
panic(err)
}
// This serves the setup payload to the container init process,
// starting the second stage of container setup.
if err := z.Serve(); err != nil {
panic(err)
}
// Must be called if the Start method succeeds.
if err := z.Wait(); err != nil {
panic(err)
}
// Output: hakurei-example
}
func TestStartError(t *testing.T) { func TestStartError(t *testing.T) {
t.Parallel() t.Parallel()
@ -84,7 +44,8 @@ func TestStartError(t *testing.T) {
Fatal: true, Fatal: true,
Step: "set up params stream", Step: "set up params stream",
Err: container.ErrReceiveEnv, Err: container.ErrReceiveEnv,
}, "set up params stream: environment variable not set", },
"set up params stream: environment variable not set",
container.ErrReceiveEnv, syscall.EBADF, container.ErrReceiveEnv, syscall.EBADF,
"cannot set up params stream: environment variable not set"}, "cannot set up params stream: environment variable not set"},
@ -92,7 +53,8 @@ func TestStartError(t *testing.T) {
Fatal: true, Fatal: true,
Step: "set up params stream", Step: "set up params stream",
Err: &os.SyscallError{Syscall: "pipe2", Err: syscall.EBADF}, Err: &os.SyscallError{Syscall: "pipe2", Err: syscall.EBADF},
}, "set up params stream pipe2: bad file descriptor", },
"set up params stream pipe2: bad file descriptor",
syscall.EBADF, os.ErrInvalid, syscall.EBADF, os.ErrInvalid,
"cannot set up params stream pipe2: bad file descriptor"}, "cannot set up params stream pipe2: bad file descriptor"},
@ -100,14 +62,16 @@ func TestStartError(t *testing.T) {
Fatal: true, Fatal: true,
Step: "prctl(PR_SET_NO_NEW_PRIVS)", Step: "prctl(PR_SET_NO_NEW_PRIVS)",
Err: syscall.EPERM, Err: syscall.EPERM,
}, "prctl(PR_SET_NO_NEW_PRIVS): operation not permitted", },
"prctl(PR_SET_NO_NEW_PRIVS): operation not permitted",
syscall.EPERM, syscall.EACCES, syscall.EPERM, syscall.EACCES,
"cannot prctl(PR_SET_NO_NEW_PRIVS): operation not permitted"}, "cannot prctl(PR_SET_NO_NEW_PRIVS): operation not permitted"},
{"landlock abi", &container.StartError{ {"landlock abi", &container.StartError{
Step: "get landlock ABI", Step: "get landlock ABI",
Err: syscall.ENOSYS, Err: syscall.ENOSYS,
}, "get landlock ABI: function not implemented", },
"get landlock ABI: function not implemented",
syscall.ENOSYS, syscall.ENOEXEC, syscall.ENOSYS, syscall.ENOEXEC,
"cannot get landlock ABI: function not implemented"}, "cannot get landlock ABI: function not implemented"},
@ -115,7 +79,8 @@ func TestStartError(t *testing.T) {
Step: "kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET", Step: "kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET",
Err: syscall.ENOSYS, Err: syscall.ENOSYS,
Origin: true, Origin: true,
}, "kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET", },
"kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET",
syscall.ENOSYS, syscall.ENOSPC, syscall.ENOSYS, syscall.ENOSPC,
"kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET"}, "kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET"},
@ -123,7 +88,8 @@ func TestStartError(t *testing.T) {
Fatal: true, Fatal: true,
Step: "create landlock ruleset", Step: "create landlock ruleset",
Err: syscall.EBADFD, Err: syscall.EBADFD,
}, "create landlock ruleset: file descriptor in bad state", },
"create landlock ruleset: file descriptor in bad state",
syscall.EBADFD, syscall.EBADF, syscall.EBADFD, syscall.EBADF,
"cannot create landlock ruleset: file descriptor in bad state"}, "cannot create landlock ruleset: file descriptor in bad state"},
@ -131,7 +97,8 @@ func TestStartError(t *testing.T) {
Fatal: true, Fatal: true,
Step: "enforce landlock ruleset", Step: "enforce landlock ruleset",
Err: syscall.ENOTRECOVERABLE, Err: syscall.ENOTRECOVERABLE,
}, "enforce landlock ruleset: state not recoverable", },
"enforce landlock ruleset: state not recoverable",
syscall.ENOTRECOVERABLE, syscall.ETIMEDOUT, syscall.ENOTRECOVERABLE, syscall.ETIMEDOUT,
"cannot enforce landlock ruleset: state not recoverable"}, "cannot enforce landlock ruleset: state not recoverable"},
@ -142,7 +109,8 @@ func TestStartError(t *testing.T) {
Path: "/proc/nonexistent", Path: "/proc/nonexistent",
Err: syscall.ENOENT, Err: syscall.ENOENT,
}, Passthrough: true, }, Passthrough: true,
}, "fork/exec /proc/nonexistent: no such file or directory", },
"fork/exec /proc/nonexistent: no such file or directory",
syscall.ENOENT, syscall.ENOSYS, syscall.ENOENT, syscall.ENOSYS,
"cannot fork/exec /proc/nonexistent: no such file or directory"}, "cannot fork/exec /proc/nonexistent: no such file or directory"},
@ -152,19 +120,11 @@ func TestStartError(t *testing.T) {
Syscall: "open", Syscall: "open",
Err: syscall.ENOSYS, Err: syscall.ENOSYS,
}, Passthrough: true, }, Passthrough: true,
}, "open: function not implemented", },
"open: function not implemented",
syscall.ENOSYS, syscall.ENOENT, syscall.ENOSYS, syscall.ENOENT,
"cannot open: function not implemented"}, "cannot open: function not implemented"},
{"start FD_CLOEXEC", &container.StartError{
Fatal: true,
Step: "set FD_CLOEXEC on all open files",
Err: func() error { _, err := strconv.Atoi("invalid"); return err }(),
Passthrough: true,
}, `strconv.Atoi: parsing "invalid": invalid syntax`,
strconv.ErrSyntax, os.ErrInvalid,
`cannot parse "invalid": invalid syntax`},
{"start other", &container.StartError{ {"start other", &container.StartError{
Step: "start container init", Step: "start container init",
Err: &net.OpError{ Err: &net.OpError{
@ -172,7 +132,8 @@ func TestStartError(t *testing.T) {
Net: "unix", Net: "unix",
Err: syscall.ECONNREFUSED, Err: syscall.ECONNREFUSED,
}, Passthrough: true, }, Passthrough: true,
}, "dial unix: connection refused", },
"dial unix: connection refused",
syscall.ECONNREFUSED, syscall.ECONNABORTED, syscall.ECONNREFUSED, syscall.ECONNABORTED,
"dial unix: connection refused"}, "dial unix: connection refused"},
} }
@ -761,15 +722,12 @@ func TestMain(m *testing.M) {
func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*check.Absolute, args ...string) (c *container.Container) { func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*check.Absolute, args ...string) (c *container.Container) {
msg := message.New(nil) msg := message.New(nil)
msg.SwapVerbose(testing.Verbose())
executable := check.MustAbs(container.MustExecutable(msg))
c = container.NewCommand(ctx, msg, absHelperInnerPath, "helper", args...) c = container.NewCommand(ctx, msg, absHelperInnerPath, "helper", args...)
c.Env = append(c.Env, envDoCheck+"=1") c.Env = append(c.Env, envDoCheck+"=1")
c.Bind(executable, absHelperInnerPath, 0) c.Bind(check.MustAbs(os.Args[0]), absHelperInnerPath, 0)
// in case test has cgo enabled // in case test has cgo enabled
if entries, err := ldd.Resolve(ctx, msg, executable); err != nil { if entries, err := ldd.Exec(ctx, msg, os.Args[0]); err != nil {
log.Fatalf("ldd: %v", err) log.Fatalf("ldd: %v", err)
} else { } else {
*libPaths = ldd.Path(entries) *libPaths = ldd.Path(entries)

View File

@ -1,17 +1,15 @@
package fhs package fhs
import ( import (
_ "unsafe" // for go:linkname _ "unsafe"
"hakurei.app/container/check" "hakurei.app/container/check"
) )
/* constants in this file bypass abs check, be extremely careful when changing them! */ /* constants in this file bypass abs check, be extremely careful when changing them! */
// unsafeAbs returns check.Absolute on any string value.
//
//go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs //go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs
func unsafeAbs(pathname string) *check.Absolute func unsafeAbs(_ string) *check.Absolute
var ( var (
// AbsRoot is [Root] as [check.Absolute]. // AbsRoot is [Root] as [check.Absolute].
@ -36,8 +34,6 @@ var (
// AbsDev is [Dev] as [check.Absolute]. // AbsDev is [Dev] as [check.Absolute].
AbsDev = unsafeAbs(Dev) AbsDev = unsafeAbs(Dev)
// AbsDevShm is [DevShm] as [check.Absolute].
AbsDevShm = unsafeAbs(DevShm)
// AbsProc is [Proc] as [check.Absolute]. // AbsProc is [Proc] as [check.Absolute].
AbsProc = unsafeAbs(Proc) AbsProc = unsafeAbs(Proc)
// AbsSys is [Sys] as [check.Absolute]. // AbsSys is [Sys] as [check.Absolute].

View File

@ -29,8 +29,6 @@ const (
// Dev points to the root directory for device nodes. // Dev points to the root directory for device nodes.
Dev = "/dev/" Dev = "/dev/"
// DevShm is the place for POSIX shared memory segments, as created via shm_open(3).
DevShm = "/dev/shm/"
// Proc points to a virtual kernel file system exposing the process list and other functionality. // Proc points to a virtual kernel file system exposing the process list and other functionality.
Proc = "/proc/" Proc = "/proc/"
// ProcSys points to a hierarchy below /proc/ that exposes a number of kernel tunables. // ProcSys points to a hierarchy below /proc/ that exposes a number of kernel tunables.

View File

@ -330,10 +330,6 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
} }
k.umask(oldmask) k.umask(oldmask)
if err := closeSetup(); err != nil {
k.fatalf(msg, "cannot close setup pipe: %v", err)
}
cmd := exec.Command(params.Path.String()) cmd := exec.Command(params.Path.String())
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.Args = params.Args cmd.Args = params.Args
@ -346,6 +342,11 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
k.fatalf(msg, "%v", err) k.fatalf(msg, "%v", err)
} }
if err := closeSetup(); err != nil {
k.printf(msg, "cannot close setup pipe: %v", err)
// not fatal
}
type winfo struct { type winfo struct {
wpid int wpid int
wstatus WaitStatus wstatus WaitStatus
@ -426,16 +427,6 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
} }
if w.wpid == cmd.Process.Pid { if w.wpid == cmd.Process.Pid {
// start timeout early
go func() { time.Sleep(params.AdoptWaitDelay); close(timeout) }()
// close initial process files; this also keeps them alive
for _, f := range extraFiles {
if err := f.Close(); err != nil {
msg.Verbose(err.Error())
}
}
switch { switch {
case w.wstatus.Exited(): case w.wstatus.Exited():
r = w.wstatus.ExitStatus() r = w.wstatus.ExitStatus()
@ -449,6 +440,8 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
r = 255 r = 255
msg.Verbosef("initial process exited with status %#x", w.wstatus) msg.Verbosef("initial process exited with status %#x", w.wstatus)
} }
go func() { time.Sleep(params.AdoptWaitDelay); close(timeout) }()
} }
case <-timeout: case <-timeout:

View File

@ -1983,7 +1983,6 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(13)}}, nil, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, nil, stub.UniqueError(12)), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, nil, stub.UniqueError(12)),
call("fatalf", stub.ExpectArgs{"%v", []any{stub.UniqueError(12)}}, nil, nil), call("fatalf", stub.ExpectArgs{"%v", []any{stub.UniqueError(12)}}, nil, nil),
@ -2062,17 +2061,15 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(10)}}, nil, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(10)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- CancelSignal }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- CancelSignal }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{"forwarding context cancellation"}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"forwarding context cancellation"}}, nil, nil),
// magicWait4Signal as ret causes wait4 stub to unblock // magicWait4Signal as ret causes wait4 stub to unblock
call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", os.Interrupt}, magicWait4Signal, stub.UniqueError(9)), call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", os.Interrupt}, magicWait4Signal, stub.UniqueError(9)),
call("printf", stub.ExpectArgs{"cannot forward cancellation: %v", []any{stub.UniqueError(9)}}, nil, nil), call("printf", stub.ExpectArgs{"cannot forward cancellation: %v", []any{stub.UniqueError(9)}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil),
call("printf", stub.ExpectArgs{"timeout exceeded waiting for lingering processes", ([]any)(nil)}, nil, nil), call("printf", stub.ExpectArgs{"timeout exceeded waiting for lingering processes", ([]any)(nil)}, nil, nil),
call("beforeExit", stub.ExpectArgs{}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil),
@ -2162,17 +2159,15 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- syscall.SIGQUIT }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- syscall.SIGQUIT }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
call("verbosef", stub.ExpectArgs{"got %s, forwarding to initial process", []any{"quit"}}, nil, nil), call("verbosef", stub.ExpectArgs{"got %s, forwarding to initial process", []any{"quit"}}, nil, nil),
// magicWait4Signal as ret causes wait4 stub to unblock // magicWait4Signal as ret causes wait4 stub to unblock
call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", syscall.SIGQUIT}, magicWait4Signal, stub.UniqueError(0xfe)), call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", syscall.SIGQUIT}, magicWait4Signal, stub.UniqueError(0xfe)),
call("printf", stub.ExpectArgs{"cannot forward signal: %v", []any{stub.UniqueError(0xfe)}}, nil, nil), call("printf", stub.ExpectArgs{"cannot forward signal: %v", []any{stub.UniqueError(0xfe)}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil),
call("printf", stub.ExpectArgs{"timeout exceeded waiting for lingering processes", []any(nil)}, nil, nil), call("printf", stub.ExpectArgs{"timeout exceeded waiting for lingering processes", []any(nil)}, nil, nil),
call("beforeExit", stub.ExpectArgs{}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil),
@ -2262,9 +2257,9 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- os.Interrupt }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- os.Interrupt }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
call("verbosef", stub.ExpectArgs{"got %s", []any{"interrupt"}}, nil, nil), call("verbosef", stub.ExpectArgs{"got %s", []any{"interrupt"}}, nil, nil),
@ -2353,13 +2348,11 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(5)}}, nil, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(5)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil),
call("printf", stub.ExpectArgs{"timeout exceeded waiting for lingering processes", ([]any)(nil)}, nil, nil), call("printf", stub.ExpectArgs{"timeout exceeded waiting for lingering processes", ([]any)(nil)}, nil, nil),
call("beforeExit", stub.ExpectArgs{}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil),
@ -2448,13 +2441,11 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(3)}}, nil, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(3)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil),
call("beforeExit", stub.ExpectArgs{}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil),
call("exit", stub.ExpectArgs{0xce}, nil, nil), call("exit", stub.ExpectArgs{0xce}, nil, nil),
@ -2586,13 +2577,11 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(1)}}, nil, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(1)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbosef", stub.ExpectArgs{"initial process exited with code %d", []any{1}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with code %d", []any{1}}, nil, nil),
call("beforeExit", stub.ExpectArgs{}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil),
call("exit", stub.ExpectArgs{1}, nil, nil), call("exit", stub.ExpectArgs{1}, nil, nil),
@ -2728,14 +2717,11 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(11), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(11), "extra file 1"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(12), "extra file 2"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(12), "extra file 2"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(0)}}, nil, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/bin/zsh")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/bin/zsh")}}, nil, nil),
call("start", stub.ExpectArgs{"/bin/zsh", []string{"zsh", "-c", "exec vim"}, []string{"DISPLAY=:0"}, "/.hakurei"}, &os.Process{Pid: 0xcafe}, nil), call("start", stub.ExpectArgs{"/bin/zsh", []string{"zsh", "-c", "exec vim"}, []string{"DISPLAY=:0"}, "/.hakurei"}, &os.Process{Pid: 0xcafe}, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(0)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
call("verbosef", stub.ExpectArgs{"initial process exited with status %#x", []any{syscall.WaitStatus(0xfade007f)}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with status %#x", []any{syscall.WaitStatus(0xfade007f)}}, nil, nil),
call("beforeExit", stub.ExpectArgs{}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil),
call("exit", stub.ExpectArgs{0xff}, nil, nil), call("exit", stub.ExpectArgs{0xff}, nil, nil),

View File

@ -9,8 +9,7 @@
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0])) #define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
int32_t hakurei_scmp_make_filter( int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
int *ret_p, uintptr_t allocate_p,
uint32_t arch, uint32_t multiarch, uint32_t arch, uint32_t multiarch,
struct hakurei_syscall_rule *rules, struct hakurei_syscall_rule *rules,
size_t rules_sz, hakurei_export_flag flags) { size_t rules_sz, hakurei_export_flag flags) {
@ -73,9 +72,11 @@ int32_t hakurei_scmp_make_filter(
assert(rule->m_errno == EPERM || rule->m_errno == ENOSYS); assert(rule->m_errno == EPERM || rule->m_errno == ENOSYS);
if (rule->arg) if (rule->arg)
*ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno), rule->syscall, 1, *rule->arg); *ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno),
rule->syscall, 1, *rule->arg);
else else
*ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno), rule->syscall, 0); *ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno),
rule->syscall, 0);
if (*ret_p == -EFAULT) { if (*ret_p == -EFAULT) {
res = 4; res = 4;
@ -92,17 +93,22 @@ int32_t hakurei_scmp_make_filter(
last_allowed_family = -1; last_allowed_family = -1;
for (i = 0; i < LEN(socket_family_allowlist); i++) { for (i = 0; i < LEN(socket_family_allowlist); i++) {
if (socket_family_allowlist[i].flags_mask != 0 && if (socket_family_allowlist[i].flags_mask != 0 &&
(socket_family_allowlist[i].flags_mask & flags) != socket_family_allowlist[i].flags_mask) (socket_family_allowlist[i].flags_mask & flags) !=
socket_family_allowlist[i].flags_mask)
continue; continue;
for (disallowed = last_allowed_family + 1; disallowed < socket_family_allowlist[i].family; disallowed++) { for (disallowed = last_allowed_family + 1;
disallowed < socket_family_allowlist[i].family; disallowed++) {
/* Blocklist the in-between valid families */ /* Blocklist the in-between valid families */
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_EQ, disallowed)); seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT),
SCMP_SYS(socket), 1,
SCMP_A0(SCMP_CMP_EQ, disallowed));
} }
last_allowed_family = socket_family_allowlist[i].family; last_allowed_family = socket_family_allowlist[i].family;
} }
/* Blocklist the rest */ /* Blocklist the rest */
seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1)); seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1,
SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1));
if (allocate_p == 0) { if (allocate_p == 0) {
*ret_p = seccomp_load(ctx); *ret_p = seccomp_load(ctx);

View File

@ -19,8 +19,7 @@ struct hakurei_syscall_rule {
}; };
extern void *hakurei_scmp_allocate(uintptr_t f, size_t len); extern void *hakurei_scmp_allocate(uintptr_t f, size_t len);
int32_t hakurei_scmp_make_filter( int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
int *ret_p, uintptr_t allocate_p,
uint32_t arch, uint32_t multiarch, uint32_t arch, uint32_t multiarch,
struct hakurei_syscall_rule *rules, struct hakurei_syscall_rule *rules,
size_t rules_sz, hakurei_export_flag flags); size_t rules_sz, hakurei_export_flag flags);

View File

@ -215,10 +215,10 @@ const (
// syscallResolveName resolves a syscall number by name via seccomp_syscall_resolve_name. // syscallResolveName resolves a syscall number by name via seccomp_syscall_resolve_name.
// This function is only for testing the lookup tables and included here for convenience. // This function is only for testing the lookup tables and included here for convenience.
func syscallResolveName(s string) (num std.ScmpSyscall, ok bool) { func syscallResolveName(s string) (trap int, ok bool) {
v := C.CString(s) v := C.CString(s)
num = std.ScmpSyscall(C.seccomp_syscall_resolve_name(v)) trap = int(C.seccomp_syscall_resolve_name(v))
C.free(unsafe.Pointer(v)) C.free(unsafe.Pointer(v))
ok = num != C.__NR_SCMP_ERROR ok = trap != C.__NR_SCMP_ERROR
return return
} }

View File

@ -68,121 +68,121 @@ func Preset(presets FilterPreset, flags ExportFlag) (rules []NativeRule) {
var ( var (
presetCommon = []NativeRule{ presetCommon = []NativeRule{
/* Block dmesg */ /* Block dmesg */
{Syscall: SNR_SYSLOG, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SYSLOG), ScmpErrno(EPERM), nil},
/* Useless old syscall */ /* Useless old syscall */
{Syscall: SNR_USELIB, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_USELIB), ScmpErrno(EPERM), nil},
/* Don't allow disabling accounting */ /* Don't allow disabling accounting */
{Syscall: SNR_ACCT, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_ACCT), ScmpErrno(EPERM), nil},
/* Don't allow reading current quota use */ /* Don't allow reading current quota use */
{Syscall: SNR_QUOTACTL, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_QUOTACTL), ScmpErrno(EPERM), nil},
/* Don't allow access to the kernel keyring */ /* Don't allow access to the kernel keyring */
{Syscall: SNR_ADD_KEY, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_ADD_KEY), ScmpErrno(EPERM), nil},
{Syscall: SNR_KEYCTL, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_KEYCTL), ScmpErrno(EPERM), nil},
{Syscall: SNR_REQUEST_KEY, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_REQUEST_KEY), ScmpErrno(EPERM), nil},
/* Scary VM/NUMA ops */ /* Scary VM/NUMA ops */
{Syscall: SNR_MOVE_PAGES, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_MOVE_PAGES), ScmpErrno(EPERM), nil},
{Syscall: SNR_MBIND, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_MBIND), ScmpErrno(EPERM), nil},
{Syscall: SNR_GET_MEMPOLICY, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_GET_MEMPOLICY), ScmpErrno(EPERM), nil},
{Syscall: SNR_SET_MEMPOLICY, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SET_MEMPOLICY), ScmpErrno(EPERM), nil},
{Syscall: SNR_MIGRATE_PAGES, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_MIGRATE_PAGES), ScmpErrno(EPERM), nil},
} }
/* hakurei: project-specific extensions */ /* hakurei: project-specific extensions */
presetCommonExt = []NativeRule{ presetCommonExt = []NativeRule{
/* system calls for changing the system clock */ /* system calls for changing the system clock */
{Syscall: SNR_ADJTIMEX, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_ADJTIMEX), ScmpErrno(EPERM), nil},
{Syscall: SNR_CLOCK_ADJTIME, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_CLOCK_ADJTIME), ScmpErrno(EPERM), nil},
{Syscall: SNR_CLOCK_ADJTIME64, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_CLOCK_ADJTIME64), ScmpErrno(EPERM), nil},
{Syscall: SNR_CLOCK_SETTIME, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_CLOCK_SETTIME), ScmpErrno(EPERM), nil},
{Syscall: SNR_CLOCK_SETTIME64, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_CLOCK_SETTIME64), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETTIMEOFDAY, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETTIMEOFDAY), ScmpErrno(EPERM), nil},
/* loading and unloading of kernel modules */ /* loading and unloading of kernel modules */
{Syscall: SNR_DELETE_MODULE, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_DELETE_MODULE), ScmpErrno(EPERM), nil},
{Syscall: SNR_FINIT_MODULE, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_FINIT_MODULE), ScmpErrno(EPERM), nil},
{Syscall: SNR_INIT_MODULE, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_INIT_MODULE), ScmpErrno(EPERM), nil},
/* system calls for rebooting and reboot preparation */ /* system calls for rebooting and reboot preparation */
{Syscall: SNR_KEXEC_FILE_LOAD, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_KEXEC_FILE_LOAD), ScmpErrno(EPERM), nil},
{Syscall: SNR_KEXEC_LOAD, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_KEXEC_LOAD), ScmpErrno(EPERM), nil},
{Syscall: SNR_REBOOT, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_REBOOT), ScmpErrno(EPERM), nil},
/* system calls for enabling/disabling swap devices */ /* system calls for enabling/disabling swap devices */
{Syscall: SNR_SWAPOFF, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SWAPOFF), ScmpErrno(EPERM), nil},
{Syscall: SNR_SWAPON, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SWAPON), ScmpErrno(EPERM), nil},
} }
presetNamespace = []NativeRule{ presetNamespace = []NativeRule{
/* Don't allow subnamespace setups: */ /* Don't allow subnamespace setups: */
{Syscall: SNR_UNSHARE, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_UNSHARE), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETNS, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETNS), ScmpErrno(EPERM), nil},
{Syscall: SNR_MOUNT, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_MOUNT), ScmpErrno(EPERM), nil},
{Syscall: SNR_UMOUNT, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_UMOUNT), ScmpErrno(EPERM), nil},
{Syscall: SNR_UMOUNT2, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_UMOUNT2), ScmpErrno(EPERM), nil},
{Syscall: SNR_PIVOT_ROOT, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_PIVOT_ROOT), ScmpErrno(EPERM), nil},
{Syscall: SNR_CHROOT, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_CHROOT), ScmpErrno(EPERM), nil},
{Syscall: SNR_CLONE, Errno: ScmpErrno(EPERM), {ScmpSyscall(SYS_CLONE), ScmpErrno(EPERM),
Arg: &ScmpArgCmp{Arg: cloneArg, Op: SCMP_CMP_MASKED_EQ, DatumA: CLONE_NEWUSER, DatumB: CLONE_NEWUSER}}, &ScmpArgCmp{cloneArg, SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER}},
/* seccomp can't look into clone3()'s struct clone_args to check whether /* seccomp can't look into clone3()'s struct clone_args to check whether
* the flags are OK, so we have no choice but to block clone3(). * the flags are OK, so we have no choice but to block clone3().
* Return ENOSYS so user-space will fall back to clone(). * Return ENOSYS so user-space will fall back to clone().
* (CVE-2021-41133; see also https://github.com/moby/moby/commit/9f6b562d) * (CVE-2021-41133; see also https://github.com/moby/moby/commit/9f6b562d)
*/ */
{Syscall: SNR_CLONE3, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_CLONE3), ScmpErrno(ENOSYS), nil},
/* New mount manipulation APIs can also change our VFS. There's no /* New mount manipulation APIs can also change our VFS. There's no
* legitimate reason to do these in the sandbox, so block all of them * legitimate reason to do these in the sandbox, so block all of them
* rather than thinking about which ones might be dangerous. * rather than thinking about which ones might be dangerous.
* (CVE-2021-41133) */ * (CVE-2021-41133) */
{Syscall: SNR_OPEN_TREE, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_OPEN_TREE), ScmpErrno(ENOSYS), nil},
{Syscall: SNR_MOVE_MOUNT, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_MOVE_MOUNT), ScmpErrno(ENOSYS), nil},
{Syscall: SNR_FSOPEN, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_FSOPEN), ScmpErrno(ENOSYS), nil},
{Syscall: SNR_FSCONFIG, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_FSCONFIG), ScmpErrno(ENOSYS), nil},
{Syscall: SNR_FSMOUNT, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_FSMOUNT), ScmpErrno(ENOSYS), nil},
{Syscall: SNR_FSPICK, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_FSPICK), ScmpErrno(ENOSYS), nil},
{Syscall: SNR_MOUNT_SETATTR, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_MOUNT_SETATTR), ScmpErrno(ENOSYS), nil},
} }
/* hakurei: project-specific extensions */ /* hakurei: project-specific extensions */
presetNamespaceExt = []NativeRule{ presetNamespaceExt = []NativeRule{
/* changing file ownership */ /* changing file ownership */
{Syscall: SNR_CHOWN, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_CHOWN), ScmpErrno(EPERM), nil},
{Syscall: SNR_CHOWN32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_CHOWN32), ScmpErrno(EPERM), nil},
{Syscall: SNR_FCHOWN, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_FCHOWN), ScmpErrno(EPERM), nil},
{Syscall: SNR_FCHOWN32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_FCHOWN32), ScmpErrno(EPERM), nil},
{Syscall: SNR_FCHOWNAT, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_FCHOWNAT), ScmpErrno(EPERM), nil},
{Syscall: SNR_LCHOWN, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_LCHOWN), ScmpErrno(EPERM), nil},
{Syscall: SNR_LCHOWN32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_LCHOWN32), ScmpErrno(EPERM), nil},
/* system calls for changing user ID and group ID credentials */ /* system calls for changing user ID and group ID credentials */
{Syscall: SNR_SETGID, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETGID), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETGID32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETGID32), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETGROUPS, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETGROUPS), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETGROUPS32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETGROUPS32), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETREGID, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETREGID), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETREGID32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETREGID32), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETRESGID, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETRESGID), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETRESGID32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETRESGID32), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETRESUID, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETRESUID), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETRESUID32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETRESUID32), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETREUID, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETREUID), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETREUID32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETREUID32), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETUID, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETUID), ScmpErrno(EPERM), nil},
{Syscall: SNR_SETUID32, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_SETUID32), ScmpErrno(EPERM), nil},
} }
presetTTY = []NativeRule{ presetTTY = []NativeRule{
/* Don't allow faking input to the controlling tty (CVE-2017-5226) */ /* Don't allow faking input to the controlling tty (CVE-2017-5226) */
{Syscall: SNR_IOCTL, Errno: ScmpErrno(EPERM), {ScmpSyscall(SYS_IOCTL), ScmpErrno(EPERM),
Arg: &ScmpArgCmp{Arg: 1, Op: SCMP_CMP_MASKED_EQ, DatumA: 0xFFFFFFFF, DatumB: TIOCSTI}}, &ScmpArgCmp{1, SCMP_CMP_MASKED_EQ, 0xFFFFFFFF, TIOCSTI}},
/* In the unlikely event that the controlling tty is a Linux virtual /* In the unlikely event that the controlling tty is a Linux virtual
* console (/dev/tty2 or similar), copy/paste operations have an effect * console (/dev/tty2 or similar), copy/paste operations have an effect
* similar to TIOCSTI (CVE-2023-28100) */ * similar to TIOCSTI (CVE-2023-28100) */
{Syscall: SNR_IOCTL, Errno: ScmpErrno(EPERM), {ScmpSyscall(SYS_IOCTL), ScmpErrno(EPERM),
Arg: &ScmpArgCmp{Arg: 1, Op: SCMP_CMP_MASKED_EQ, DatumA: 0xFFFFFFFF, DatumB: TIOCLINUX}}, &ScmpArgCmp{1, SCMP_CMP_MASKED_EQ, 0xFFFFFFFF, TIOCLINUX}},
} }
presetEmu = []NativeRule{ presetEmu = []NativeRule{
@ -190,15 +190,15 @@ var (
* so it's disabled as a hardening measure. * so it's disabled as a hardening measure.
* However, it is required to run old 16-bit applications * However, it is required to run old 16-bit applications
* as well as some Wine patches, so it's allowed in multiarch. */ * as well as some Wine patches, so it's allowed in multiarch. */
{Syscall: SNR_MODIFY_LDT, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_MODIFY_LDT), ScmpErrno(EPERM), nil},
} }
/* hakurei: project-specific extensions */ /* hakurei: project-specific extensions */
presetEmuExt = []NativeRule{ presetEmuExt = []NativeRule{
{Syscall: SNR_SUBPAGE_PROT, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_SUBPAGE_PROT), ScmpErrno(ENOSYS), nil},
{Syscall: SNR_SWITCH_ENDIAN, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_SWITCH_ENDIAN), ScmpErrno(ENOSYS), nil},
{Syscall: SNR_VM86, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_VM86), ScmpErrno(ENOSYS), nil},
{Syscall: SNR_VM86OLD, Errno: ScmpErrno(ENOSYS), Arg: nil}, {ScmpSyscall(SYS_VM86OLD), ScmpErrno(ENOSYS), nil},
} }
) )
@ -206,11 +206,11 @@ func presetDevel(allowedPersonality ScmpDatum) []NativeRule {
return []NativeRule{ return []NativeRule{
/* Profiling operations; we expect these to be done by tools from outside /* Profiling operations; we expect these to be done by tools from outside
* the sandbox. In particular perf has been the source of many CVEs. */ * the sandbox. In particular perf has been the source of many CVEs. */
{Syscall: SNR_PERF_EVENT_OPEN, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_PERF_EVENT_OPEN), ScmpErrno(EPERM), nil},
/* Don't allow you to switch to bsd emulation or whatnot */ /* Don't allow you to switch to bsd emulation or whatnot */
{Syscall: SNR_PERSONALITY, Errno: ScmpErrno(EPERM), {ScmpSyscall(SYS_PERSONALITY), ScmpErrno(EPERM),
Arg: &ScmpArgCmp{Arg: 0, Op: SCMP_CMP_NE, DatumA: allowedPersonality}}, &ScmpArgCmp{0, SCMP_CMP_NE, allowedPersonality, 0}},
{Syscall: SNR_PTRACE, Errno: ScmpErrno(EPERM), Arg: nil}, {ScmpSyscall(SYS_PTRACE), ScmpErrno(EPERM), nil},
} }
} }

View File

@ -22,7 +22,7 @@ package std
import . "syscall" import . "syscall"
var syscallNum = map[string]ScmpSyscall{ var syscallNum = map[string]int{
EOF EOF
my $offset = 0; my $offset = 0;
@ -37,14 +37,16 @@ sub fmt {
} }
(my $name_upper = $name) =~ y/a-z/A-Z/; (my $name_upper = $name) =~ y/a-z/A-Z/;
$num = $num + $offset; $num = $num + $offset;
if($num > $syscall_cutoff_arch{$uname_arch} && $state == 0){ # not wired in Go standard library if($num > $syscall_cutoff_arch{$uname_arch}){ # not wired in Go standard library
print " SYS_$name_upper = $num\n"; if($state < 0){
print " \"$name\": SYS_$name_upper,\n";
} }
elsif($state == -1){ else{
print " \"$name\": SNR_$name_upper,\n"; print " SYS_$name_upper = $num;\n";
} }
elsif($state == 1){ }
print " SNR_$name_upper ScmpSyscall = SYS_$name_upper\n"; elsif($state < 0){
print " \"$name\": SYS_$name_upper,\n";
} }
else{ else{
return; return;
@ -79,16 +81,10 @@ while(<GCC>){
} }
} }
if($state == -1){ if($state < 0){
$state = $state + 1;
print "}\n\nconst (\n"; print "}\n\nconst (\n";
goto GENERATE;
} }
elsif($state == 0){
print ")\n\nconst (\n";
}
elsif($state == 1){
print ")";
exit;
}
++$state;
goto GENERATE;
print ")";

View File

@ -46,7 +46,7 @@ type (
// MarshalJSON resolves the name of [ScmpSyscall] and encodes it as a [json] string. // MarshalJSON resolves the name of [ScmpSyscall] and encodes it as a [json] string.
// If such a name does not exist, the syscall number is encoded instead. // If such a name does not exist, the syscall number is encoded instead.
func (num *ScmpSyscall) MarshalJSON() ([]byte, error) { func (num *ScmpSyscall) MarshalJSON() ([]byte, error) {
n := *num n := int(*num)
for name, cur := range Syscalls() { for name, cur := range Syscalls() {
if cur == n { if cur == n {
return json.Marshal(name) return json.Marshal(name)
@ -70,7 +70,7 @@ func (num *ScmpSyscall) UnmarshalJSON(data []byte) error {
if n, ok := SyscallResolveName(name); !ok { if n, ok := SyscallResolveName(name); !ok {
return SyscallNameError(name) return SyscallNameError(name)
} else { } else {
*num = n *num = ScmpSyscall(n)
return nil return nil
} }
} }

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
"math" "math"
"reflect" "reflect"
"syscall"
"testing" "testing"
"hakurei.app/container/std" "hakurei.app/container/std"
@ -19,8 +20,8 @@ func TestScmpSyscall(t *testing.T) {
want std.ScmpSyscall want std.ScmpSyscall
err error err error
}{ }{
{"epoll_create1", `"epoll_create1"`, std.SNR_EPOLL_CREATE1, nil}, {"select", `"select"`, syscall.SYS_SELECT, nil},
{"clone3", `"clone3"`, std.SNR_CLONE3, nil}, {"clone3", `"clone3"`, std.SYS_CLONE3, nil},
{"oob", `-2147483647`, -math.MaxInt32, {"oob", `-2147483647`, -math.MaxInt32,
&json.UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 11}}, &json.UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 11}},

View File

@ -3,8 +3,8 @@ package std
import "iter" import "iter"
// Syscalls returns an iterator over all wired syscalls. // Syscalls returns an iterator over all wired syscalls.
func Syscalls() iter.Seq2[string, ScmpSyscall] { func Syscalls() iter.Seq2[string, int] {
return func(yield func(string, ScmpSyscall) bool) { return func(yield func(string, int) bool) {
for name, num := range syscallNum { for name, num := range syscallNum {
if !yield(name, num) { if !yield(name, num) {
return return
@ -19,7 +19,7 @@ func Syscalls() iter.Seq2[string, ScmpSyscall] {
} }
// SyscallResolveName resolves a syscall number from its string representation. // SyscallResolveName resolves a syscall number from its string representation.
func SyscallResolveName(name string) (num ScmpSyscall, ok bool) { func SyscallResolveName(name string) (num int, ok bool) {
if num, ok = syscallNum[name]; ok { if num, ok = syscallNum[name]; ok {
return return
} }

View File

@ -1,13 +1,13 @@
package std package std
var syscallNumExtra = map[string]ScmpSyscall{ var syscallNumExtra = map[string]int{
"kexec_file_load": SNR_KEXEC_FILE_LOAD, "kexec_file_load": SYS_KEXEC_FILE_LOAD,
"subpage_prot": SNR_SUBPAGE_PROT, "subpage_prot": SYS_SUBPAGE_PROT,
"switch_endian": SNR_SWITCH_ENDIAN, "switch_endian": SYS_SWITCH_ENDIAN,
} }
const ( const (
SNR_KEXEC_FILE_LOAD ScmpSyscall = __PNR_kexec_file_load SYS_KEXEC_FILE_LOAD = __PNR_kexec_file_load
SNR_SUBPAGE_PROT ScmpSyscall = __PNR_subpage_prot SYS_SUBPAGE_PROT = __PNR_subpage_prot
SNR_SWITCH_ENDIAN ScmpSyscall = __PNR_switch_endian SYS_SWITCH_ENDIAN = __PNR_switch_endian
) )

View File

@ -1,41 +1,41 @@
package std package std
var syscallNumExtra = map[string]ScmpSyscall{ var syscallNumExtra = map[string]int{
"umount": SNR_UMOUNT, "umount": SYS_UMOUNT,
"subpage_prot": SNR_SUBPAGE_PROT, "subpage_prot": SYS_SUBPAGE_PROT,
"switch_endian": SNR_SWITCH_ENDIAN, "switch_endian": SYS_SWITCH_ENDIAN,
"vm86": SNR_VM86, "vm86": SYS_VM86,
"vm86old": SNR_VM86OLD, "vm86old": SYS_VM86OLD,
"clock_adjtime64": SNR_CLOCK_ADJTIME64, "clock_adjtime64": SYS_CLOCK_ADJTIME64,
"clock_settime64": SNR_CLOCK_SETTIME64, "clock_settime64": SYS_CLOCK_SETTIME64,
"chown32": SNR_CHOWN32, "chown32": SYS_CHOWN32,
"fchown32": SNR_FCHOWN32, "fchown32": SYS_FCHOWN32,
"lchown32": SNR_LCHOWN32, "lchown32": SYS_LCHOWN32,
"setgid32": SNR_SETGID32, "setgid32": SYS_SETGID32,
"setgroups32": SNR_SETGROUPS32, "setgroups32": SYS_SETGROUPS32,
"setregid32": SNR_SETREGID32, "setregid32": SYS_SETREGID32,
"setresgid32": SNR_SETRESGID32, "setresgid32": SYS_SETRESGID32,
"setresuid32": SNR_SETRESUID32, "setresuid32": SYS_SETRESUID32,
"setreuid32": SNR_SETREUID32, "setreuid32": SYS_SETREUID32,
"setuid32": SNR_SETUID32, "setuid32": SYS_SETUID32,
} }
const ( const (
SNR_UMOUNT ScmpSyscall = __PNR_umount SYS_UMOUNT = __PNR_umount
SNR_SUBPAGE_PROT ScmpSyscall = __PNR_subpage_prot SYS_SUBPAGE_PROT = __PNR_subpage_prot
SNR_SWITCH_ENDIAN ScmpSyscall = __PNR_switch_endian SYS_SWITCH_ENDIAN = __PNR_switch_endian
SNR_VM86 ScmpSyscall = __PNR_vm86 SYS_VM86 = __PNR_vm86
SNR_VM86OLD ScmpSyscall = __PNR_vm86old SYS_VM86OLD = __PNR_vm86old
SNR_CLOCK_ADJTIME64 ScmpSyscall = __PNR_clock_adjtime64 SYS_CLOCK_ADJTIME64 = __PNR_clock_adjtime64
SNR_CLOCK_SETTIME64 ScmpSyscall = __PNR_clock_settime64 SYS_CLOCK_SETTIME64 = __PNR_clock_settime64
SNR_CHOWN32 ScmpSyscall = __PNR_chown32 SYS_CHOWN32 = __PNR_chown32
SNR_FCHOWN32 ScmpSyscall = __PNR_fchown32 SYS_FCHOWN32 = __PNR_fchown32
SNR_LCHOWN32 ScmpSyscall = __PNR_lchown32 SYS_LCHOWN32 = __PNR_lchown32
SNR_SETGID32 ScmpSyscall = __PNR_setgid32 SYS_SETGID32 = __PNR_setgid32
SNR_SETGROUPS32 ScmpSyscall = __PNR_setgroups32 SYS_SETGROUPS32 = __PNR_setgroups32
SNR_SETREGID32 ScmpSyscall = __PNR_setregid32 SYS_SETREGID32 = __PNR_setregid32
SNR_SETRESGID32 ScmpSyscall = __PNR_setresgid32 SYS_SETRESGID32 = __PNR_setresgid32
SNR_SETRESUID32 ScmpSyscall = __PNR_setresuid32 SYS_SETRESUID32 = __PNR_setresuid32
SNR_SETREUID32 ScmpSyscall = __PNR_setreuid32 SYS_SETREUID32 = __PNR_setreuid32
SNR_SETUID32 ScmpSyscall = __PNR_setuid32 SYS_SETUID32 = __PNR_setuid32
) )

View File

@ -6,50 +6,50 @@ const (
SYS_NEWFSTATAT = syscall.SYS_FSTATAT SYS_NEWFSTATAT = syscall.SYS_FSTATAT
) )
var syscallNumExtra = map[string]ScmpSyscall{ var syscallNumExtra = map[string]int{
"uselib": SNR_USELIB, "uselib": SYS_USELIB,
"clock_adjtime64": SNR_CLOCK_ADJTIME64, "clock_adjtime64": SYS_CLOCK_ADJTIME64,
"clock_settime64": SNR_CLOCK_SETTIME64, "clock_settime64": SYS_CLOCK_SETTIME64,
"umount": SNR_UMOUNT, "umount": SYS_UMOUNT,
"chown": SNR_CHOWN, "chown": SYS_CHOWN,
"chown32": SNR_CHOWN32, "chown32": SYS_CHOWN32,
"fchown32": SNR_FCHOWN32, "fchown32": SYS_FCHOWN32,
"lchown": SNR_LCHOWN, "lchown": SYS_LCHOWN,
"lchown32": SNR_LCHOWN32, "lchown32": SYS_LCHOWN32,
"setgid32": SNR_SETGID32, "setgid32": SYS_SETGID32,
"setgroups32": SNR_SETGROUPS32, "setgroups32": SYS_SETGROUPS32,
"setregid32": SNR_SETREGID32, "setregid32": SYS_SETREGID32,
"setresgid32": SNR_SETRESGID32, "setresgid32": SYS_SETRESGID32,
"setresuid32": SNR_SETRESUID32, "setresuid32": SYS_SETRESUID32,
"setreuid32": SNR_SETREUID32, "setreuid32": SYS_SETREUID32,
"setuid32": SNR_SETUID32, "setuid32": SYS_SETUID32,
"modify_ldt": SNR_MODIFY_LDT, "modify_ldt": SYS_MODIFY_LDT,
"subpage_prot": SNR_SUBPAGE_PROT, "subpage_prot": SYS_SUBPAGE_PROT,
"switch_endian": SNR_SWITCH_ENDIAN, "switch_endian": SYS_SWITCH_ENDIAN,
"vm86": SNR_VM86, "vm86": SYS_VM86,
"vm86old": SNR_VM86OLD, "vm86old": SYS_VM86OLD,
} }
const ( const (
SNR_USELIB ScmpSyscall = __PNR_uselib SYS_USELIB = __PNR_uselib
SNR_CLOCK_ADJTIME64 ScmpSyscall = __PNR_clock_adjtime64 SYS_CLOCK_ADJTIME64 = __PNR_clock_adjtime64
SNR_CLOCK_SETTIME64 ScmpSyscall = __PNR_clock_settime64 SYS_CLOCK_SETTIME64 = __PNR_clock_settime64
SNR_UMOUNT ScmpSyscall = __PNR_umount SYS_UMOUNT = __PNR_umount
SNR_CHOWN ScmpSyscall = __PNR_chown SYS_CHOWN = __PNR_chown
SNR_CHOWN32 ScmpSyscall = __PNR_chown32 SYS_CHOWN32 = __PNR_chown32
SNR_FCHOWN32 ScmpSyscall = __PNR_fchown32 SYS_FCHOWN32 = __PNR_fchown32
SNR_LCHOWN ScmpSyscall = __PNR_lchown SYS_LCHOWN = __PNR_lchown
SNR_LCHOWN32 ScmpSyscall = __PNR_lchown32 SYS_LCHOWN32 = __PNR_lchown32
SNR_SETGID32 ScmpSyscall = __PNR_setgid32 SYS_SETGID32 = __PNR_setgid32
SNR_SETGROUPS32 ScmpSyscall = __PNR_setgroups32 SYS_SETGROUPS32 = __PNR_setgroups32
SNR_SETREGID32 ScmpSyscall = __PNR_setregid32 SYS_SETREGID32 = __PNR_setregid32
SNR_SETRESGID32 ScmpSyscall = __PNR_setresgid32 SYS_SETRESGID32 = __PNR_setresgid32
SNR_SETRESUID32 ScmpSyscall = __PNR_setresuid32 SYS_SETRESUID32 = __PNR_setresuid32
SNR_SETREUID32 ScmpSyscall = __PNR_setreuid32 SYS_SETREUID32 = __PNR_setreuid32
SNR_SETUID32 ScmpSyscall = __PNR_setuid32 SYS_SETUID32 = __PNR_setuid32
SNR_MODIFY_LDT ScmpSyscall = __PNR_modify_ldt SYS_MODIFY_LDT = __PNR_modify_ldt
SNR_SUBPAGE_PROT ScmpSyscall = __PNR_subpage_prot SYS_SUBPAGE_PROT = __PNR_subpage_prot
SNR_SWITCH_ENDIAN ScmpSyscall = __PNR_switch_endian SYS_SWITCH_ENDIAN = __PNR_switch_endian
SNR_VM86 ScmpSyscall = __PNR_vm86 SYS_VM86 = __PNR_vm86
SNR_VM86OLD ScmpSyscall = __PNR_vm86old SYS_VM86OLD = __PNR_vm86old
) )

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,325 +5,325 @@ package std
import . "syscall" import . "syscall"
var syscallNum = map[string]ScmpSyscall{ var syscallNum = map[string]int{
"io_setup": SNR_IO_SETUP, "io_setup": SYS_IO_SETUP,
"io_destroy": SNR_IO_DESTROY, "io_destroy": SYS_IO_DESTROY,
"io_submit": SNR_IO_SUBMIT, "io_submit": SYS_IO_SUBMIT,
"io_cancel": SNR_IO_CANCEL, "io_cancel": SYS_IO_CANCEL,
"io_getevents": SNR_IO_GETEVENTS, "io_getevents": SYS_IO_GETEVENTS,
"setxattr": SNR_SETXATTR, "setxattr": SYS_SETXATTR,
"lsetxattr": SNR_LSETXATTR, "lsetxattr": SYS_LSETXATTR,
"fsetxattr": SNR_FSETXATTR, "fsetxattr": SYS_FSETXATTR,
"getxattr": SNR_GETXATTR, "getxattr": SYS_GETXATTR,
"lgetxattr": SNR_LGETXATTR, "lgetxattr": SYS_LGETXATTR,
"fgetxattr": SNR_FGETXATTR, "fgetxattr": SYS_FGETXATTR,
"listxattr": SNR_LISTXATTR, "listxattr": SYS_LISTXATTR,
"llistxattr": SNR_LLISTXATTR, "llistxattr": SYS_LLISTXATTR,
"flistxattr": SNR_FLISTXATTR, "flistxattr": SYS_FLISTXATTR,
"removexattr": SNR_REMOVEXATTR, "removexattr": SYS_REMOVEXATTR,
"lremovexattr": SNR_LREMOVEXATTR, "lremovexattr": SYS_LREMOVEXATTR,
"fremovexattr": SNR_FREMOVEXATTR, "fremovexattr": SYS_FREMOVEXATTR,
"getcwd": SNR_GETCWD, "getcwd": SYS_GETCWD,
"lookup_dcookie": SNR_LOOKUP_DCOOKIE, "lookup_dcookie": SYS_LOOKUP_DCOOKIE,
"eventfd2": SNR_EVENTFD2, "eventfd2": SYS_EVENTFD2,
"epoll_create1": SNR_EPOLL_CREATE1, "epoll_create1": SYS_EPOLL_CREATE1,
"epoll_ctl": SNR_EPOLL_CTL, "epoll_ctl": SYS_EPOLL_CTL,
"epoll_pwait": SNR_EPOLL_PWAIT, "epoll_pwait": SYS_EPOLL_PWAIT,
"dup": SNR_DUP, "dup": SYS_DUP,
"dup3": SNR_DUP3, "dup3": SYS_DUP3,
"fcntl": SNR_FCNTL, "fcntl": SYS_FCNTL,
"inotify_init1": SNR_INOTIFY_INIT1, "inotify_init1": SYS_INOTIFY_INIT1,
"inotify_add_watch": SNR_INOTIFY_ADD_WATCH, "inotify_add_watch": SYS_INOTIFY_ADD_WATCH,
"inotify_rm_watch": SNR_INOTIFY_RM_WATCH, "inotify_rm_watch": SYS_INOTIFY_RM_WATCH,
"ioctl": SNR_IOCTL, "ioctl": SYS_IOCTL,
"ioprio_set": SNR_IOPRIO_SET, "ioprio_set": SYS_IOPRIO_SET,
"ioprio_get": SNR_IOPRIO_GET, "ioprio_get": SYS_IOPRIO_GET,
"flock": SNR_FLOCK, "flock": SYS_FLOCK,
"mknodat": SNR_MKNODAT, "mknodat": SYS_MKNODAT,
"mkdirat": SNR_MKDIRAT, "mkdirat": SYS_MKDIRAT,
"unlinkat": SNR_UNLINKAT, "unlinkat": SYS_UNLINKAT,
"symlinkat": SNR_SYMLINKAT, "symlinkat": SYS_SYMLINKAT,
"linkat": SNR_LINKAT, "linkat": SYS_LINKAT,
"renameat": SNR_RENAMEAT, "renameat": SYS_RENAMEAT,
"umount2": SNR_UMOUNT2, "umount2": SYS_UMOUNT2,
"mount": SNR_MOUNT, "mount": SYS_MOUNT,
"pivot_root": SNR_PIVOT_ROOT, "pivot_root": SYS_PIVOT_ROOT,
"nfsservctl": SNR_NFSSERVCTL, "nfsservctl": SYS_NFSSERVCTL,
"statfs": SNR_STATFS, "statfs": SYS_STATFS,
"fstatfs": SNR_FSTATFS, "fstatfs": SYS_FSTATFS,
"truncate": SNR_TRUNCATE, "truncate": SYS_TRUNCATE,
"ftruncate": SNR_FTRUNCATE, "ftruncate": SYS_FTRUNCATE,
"fallocate": SNR_FALLOCATE, "fallocate": SYS_FALLOCATE,
"faccessat": SNR_FACCESSAT, "faccessat": SYS_FACCESSAT,
"chdir": SNR_CHDIR, "chdir": SYS_CHDIR,
"fchdir": SNR_FCHDIR, "fchdir": SYS_FCHDIR,
"chroot": SNR_CHROOT, "chroot": SYS_CHROOT,
"fchmod": SNR_FCHMOD, "fchmod": SYS_FCHMOD,
"fchmodat": SNR_FCHMODAT, "fchmodat": SYS_FCHMODAT,
"fchownat": SNR_FCHOWNAT, "fchownat": SYS_FCHOWNAT,
"fchown": SNR_FCHOWN, "fchown": SYS_FCHOWN,
"openat": SNR_OPENAT, "openat": SYS_OPENAT,
"close": SNR_CLOSE, "close": SYS_CLOSE,
"vhangup": SNR_VHANGUP, "vhangup": SYS_VHANGUP,
"pipe2": SNR_PIPE2, "pipe2": SYS_PIPE2,
"quotactl": SNR_QUOTACTL, "quotactl": SYS_QUOTACTL,
"getdents64": SNR_GETDENTS64, "getdents64": SYS_GETDENTS64,
"lseek": SNR_LSEEK, "lseek": SYS_LSEEK,
"read": SNR_READ, "read": SYS_READ,
"write": SNR_WRITE, "write": SYS_WRITE,
"readv": SNR_READV, "readv": SYS_READV,
"writev": SNR_WRITEV, "writev": SYS_WRITEV,
"pread64": SNR_PREAD64, "pread64": SYS_PREAD64,
"pwrite64": SNR_PWRITE64, "pwrite64": SYS_PWRITE64,
"preadv": SNR_PREADV, "preadv": SYS_PREADV,
"pwritev": SNR_PWRITEV, "pwritev": SYS_PWRITEV,
"sendfile": SNR_SENDFILE, "sendfile": SYS_SENDFILE,
"pselect6": SNR_PSELECT6, "pselect6": SYS_PSELECT6,
"ppoll": SNR_PPOLL, "ppoll": SYS_PPOLL,
"signalfd4": SNR_SIGNALFD4, "signalfd4": SYS_SIGNALFD4,
"vmsplice": SNR_VMSPLICE, "vmsplice": SYS_VMSPLICE,
"splice": SNR_SPLICE, "splice": SYS_SPLICE,
"tee": SNR_TEE, "tee": SYS_TEE,
"readlinkat": SNR_READLINKAT, "readlinkat": SYS_READLINKAT,
"newfstatat": SNR_NEWFSTATAT, "newfstatat": SYS_NEWFSTATAT,
"fstat": SNR_FSTAT, "fstat": SYS_FSTAT,
"sync": SNR_SYNC, "sync": SYS_SYNC,
"fsync": SNR_FSYNC, "fsync": SYS_FSYNC,
"fdatasync": SNR_FDATASYNC, "fdatasync": SYS_FDATASYNC,
"sync_file_range": SNR_SYNC_FILE_RANGE, "sync_file_range": SYS_SYNC_FILE_RANGE,
"timerfd_create": SNR_TIMERFD_CREATE, "timerfd_create": SYS_TIMERFD_CREATE,
"timerfd_settime": SNR_TIMERFD_SETTIME, "timerfd_settime": SYS_TIMERFD_SETTIME,
"timerfd_gettime": SNR_TIMERFD_GETTIME, "timerfd_gettime": SYS_TIMERFD_GETTIME,
"utimensat": SNR_UTIMENSAT, "utimensat": SYS_UTIMENSAT,
"acct": SNR_ACCT, "acct": SYS_ACCT,
"capget": SNR_CAPGET, "capget": SYS_CAPGET,
"capset": SNR_CAPSET, "capset": SYS_CAPSET,
"personality": SNR_PERSONALITY, "personality": SYS_PERSONALITY,
"exit": SNR_EXIT, "exit": SYS_EXIT,
"exit_group": SNR_EXIT_GROUP, "exit_group": SYS_EXIT_GROUP,
"waitid": SNR_WAITID, "waitid": SYS_WAITID,
"set_tid_address": SNR_SET_TID_ADDRESS, "set_tid_address": SYS_SET_TID_ADDRESS,
"unshare": SNR_UNSHARE, "unshare": SYS_UNSHARE,
"futex": SNR_FUTEX, "futex": SYS_FUTEX,
"set_robust_list": SNR_SET_ROBUST_LIST, "set_robust_list": SYS_SET_ROBUST_LIST,
"get_robust_list": SNR_GET_ROBUST_LIST, "get_robust_list": SYS_GET_ROBUST_LIST,
"nanosleep": SNR_NANOSLEEP, "nanosleep": SYS_NANOSLEEP,
"getitimer": SNR_GETITIMER, "getitimer": SYS_GETITIMER,
"setitimer": SNR_SETITIMER, "setitimer": SYS_SETITIMER,
"kexec_load": SNR_KEXEC_LOAD, "kexec_load": SYS_KEXEC_LOAD,
"init_module": SNR_INIT_MODULE, "init_module": SYS_INIT_MODULE,
"delete_module": SNR_DELETE_MODULE, "delete_module": SYS_DELETE_MODULE,
"timer_create": SNR_TIMER_CREATE, "timer_create": SYS_TIMER_CREATE,
"timer_gettime": SNR_TIMER_GETTIME, "timer_gettime": SYS_TIMER_GETTIME,
"timer_getoverrun": SNR_TIMER_GETOVERRUN, "timer_getoverrun": SYS_TIMER_GETOVERRUN,
"timer_settime": SNR_TIMER_SETTIME, "timer_settime": SYS_TIMER_SETTIME,
"timer_delete": SNR_TIMER_DELETE, "timer_delete": SYS_TIMER_DELETE,
"clock_settime": SNR_CLOCK_SETTIME, "clock_settime": SYS_CLOCK_SETTIME,
"clock_gettime": SNR_CLOCK_GETTIME, "clock_gettime": SYS_CLOCK_GETTIME,
"clock_getres": SNR_CLOCK_GETRES, "clock_getres": SYS_CLOCK_GETRES,
"clock_nanosleep": SNR_CLOCK_NANOSLEEP, "clock_nanosleep": SYS_CLOCK_NANOSLEEP,
"syslog": SNR_SYSLOG, "syslog": SYS_SYSLOG,
"ptrace": SNR_PTRACE, "ptrace": SYS_PTRACE,
"sched_setparam": SNR_SCHED_SETPARAM, "sched_setparam": SYS_SCHED_SETPARAM,
"sched_setscheduler": SNR_SCHED_SETSCHEDULER, "sched_setscheduler": SYS_SCHED_SETSCHEDULER,
"sched_getscheduler": SNR_SCHED_GETSCHEDULER, "sched_getscheduler": SYS_SCHED_GETSCHEDULER,
"sched_getparam": SNR_SCHED_GETPARAM, "sched_getparam": SYS_SCHED_GETPARAM,
"sched_setaffinity": SNR_SCHED_SETAFFINITY, "sched_setaffinity": SYS_SCHED_SETAFFINITY,
"sched_getaffinity": SNR_SCHED_GETAFFINITY, "sched_getaffinity": SYS_SCHED_GETAFFINITY,
"sched_yield": SNR_SCHED_YIELD, "sched_yield": SYS_SCHED_YIELD,
"sched_get_priority_max": SNR_SCHED_GET_PRIORITY_MAX, "sched_get_priority_max": SYS_SCHED_GET_PRIORITY_MAX,
"sched_get_priority_min": SNR_SCHED_GET_PRIORITY_MIN, "sched_get_priority_min": SYS_SCHED_GET_PRIORITY_MIN,
"sched_rr_get_interval": SNR_SCHED_RR_GET_INTERVAL, "sched_rr_get_interval": SYS_SCHED_RR_GET_INTERVAL,
"restart_syscall": SNR_RESTART_SYSCALL, "restart_syscall": SYS_RESTART_SYSCALL,
"kill": SNR_KILL, "kill": SYS_KILL,
"tkill": SNR_TKILL, "tkill": SYS_TKILL,
"tgkill": SNR_TGKILL, "tgkill": SYS_TGKILL,
"sigaltstack": SNR_SIGALTSTACK, "sigaltstack": SYS_SIGALTSTACK,
"rt_sigsuspend": SNR_RT_SIGSUSPEND, "rt_sigsuspend": SYS_RT_SIGSUSPEND,
"rt_sigaction": SNR_RT_SIGACTION, "rt_sigaction": SYS_RT_SIGACTION,
"rt_sigprocmask": SNR_RT_SIGPROCMASK, "rt_sigprocmask": SYS_RT_SIGPROCMASK,
"rt_sigpending": SNR_RT_SIGPENDING, "rt_sigpending": SYS_RT_SIGPENDING,
"rt_sigtimedwait": SNR_RT_SIGTIMEDWAIT, "rt_sigtimedwait": SYS_RT_SIGTIMEDWAIT,
"rt_sigqueueinfo": SNR_RT_SIGQUEUEINFO, "rt_sigqueueinfo": SYS_RT_SIGQUEUEINFO,
"rt_sigreturn": SNR_RT_SIGRETURN, "rt_sigreturn": SYS_RT_SIGRETURN,
"setpriority": SNR_SETPRIORITY, "setpriority": SYS_SETPRIORITY,
"getpriority": SNR_GETPRIORITY, "getpriority": SYS_GETPRIORITY,
"reboot": SNR_REBOOT, "reboot": SYS_REBOOT,
"setregid": SNR_SETREGID, "setregid": SYS_SETREGID,
"setgid": SNR_SETGID, "setgid": SYS_SETGID,
"setreuid": SNR_SETREUID, "setreuid": SYS_SETREUID,
"setuid": SNR_SETUID, "setuid": SYS_SETUID,
"setresuid": SNR_SETRESUID, "setresuid": SYS_SETRESUID,
"getresuid": SNR_GETRESUID, "getresuid": SYS_GETRESUID,
"setresgid": SNR_SETRESGID, "setresgid": SYS_SETRESGID,
"getresgid": SNR_GETRESGID, "getresgid": SYS_GETRESGID,
"setfsuid": SNR_SETFSUID, "setfsuid": SYS_SETFSUID,
"setfsgid": SNR_SETFSGID, "setfsgid": SYS_SETFSGID,
"times": SNR_TIMES, "times": SYS_TIMES,
"setpgid": SNR_SETPGID, "setpgid": SYS_SETPGID,
"getpgid": SNR_GETPGID, "getpgid": SYS_GETPGID,
"getsid": SNR_GETSID, "getsid": SYS_GETSID,
"setsid": SNR_SETSID, "setsid": SYS_SETSID,
"getgroups": SNR_GETGROUPS, "getgroups": SYS_GETGROUPS,
"setgroups": SNR_SETGROUPS, "setgroups": SYS_SETGROUPS,
"uname": SNR_UNAME, "uname": SYS_UNAME,
"sethostname": SNR_SETHOSTNAME, "sethostname": SYS_SETHOSTNAME,
"setdomainname": SNR_SETDOMAINNAME, "setdomainname": SYS_SETDOMAINNAME,
"getrlimit": SNR_GETRLIMIT, "getrlimit": SYS_GETRLIMIT,
"setrlimit": SNR_SETRLIMIT, "setrlimit": SYS_SETRLIMIT,
"getrusage": SNR_GETRUSAGE, "getrusage": SYS_GETRUSAGE,
"umask": SNR_UMASK, "umask": SYS_UMASK,
"prctl": SNR_PRCTL, "prctl": SYS_PRCTL,
"getcpu": SNR_GETCPU, "getcpu": SYS_GETCPU,
"gettimeofday": SNR_GETTIMEOFDAY, "gettimeofday": SYS_GETTIMEOFDAY,
"settimeofday": SNR_SETTIMEOFDAY, "settimeofday": SYS_SETTIMEOFDAY,
"adjtimex": SNR_ADJTIMEX, "adjtimex": SYS_ADJTIMEX,
"getpid": SNR_GETPID, "getpid": SYS_GETPID,
"getppid": SNR_GETPPID, "getppid": SYS_GETPPID,
"getuid": SNR_GETUID, "getuid": SYS_GETUID,
"geteuid": SNR_GETEUID, "geteuid": SYS_GETEUID,
"getgid": SNR_GETGID, "getgid": SYS_GETGID,
"getegid": SNR_GETEGID, "getegid": SYS_GETEGID,
"gettid": SNR_GETTID, "gettid": SYS_GETTID,
"sysinfo": SNR_SYSINFO, "sysinfo": SYS_SYSINFO,
"mq_open": SNR_MQ_OPEN, "mq_open": SYS_MQ_OPEN,
"mq_unlink": SNR_MQ_UNLINK, "mq_unlink": SYS_MQ_UNLINK,
"mq_timedsend": SNR_MQ_TIMEDSEND, "mq_timedsend": SYS_MQ_TIMEDSEND,
"mq_timedreceive": SNR_MQ_TIMEDRECEIVE, "mq_timedreceive": SYS_MQ_TIMEDRECEIVE,
"mq_notify": SNR_MQ_NOTIFY, "mq_notify": SYS_MQ_NOTIFY,
"mq_getsetattr": SNR_MQ_GETSETATTR, "mq_getsetattr": SYS_MQ_GETSETATTR,
"msgget": SNR_MSGGET, "msgget": SYS_MSGGET,
"msgctl": SNR_MSGCTL, "msgctl": SYS_MSGCTL,
"msgrcv": SNR_MSGRCV, "msgrcv": SYS_MSGRCV,
"msgsnd": SNR_MSGSND, "msgsnd": SYS_MSGSND,
"semget": SNR_SEMGET, "semget": SYS_SEMGET,
"semctl": SNR_SEMCTL, "semctl": SYS_SEMCTL,
"semtimedop": SNR_SEMTIMEDOP, "semtimedop": SYS_SEMTIMEDOP,
"semop": SNR_SEMOP, "semop": SYS_SEMOP,
"shmget": SNR_SHMGET, "shmget": SYS_SHMGET,
"shmctl": SNR_SHMCTL, "shmctl": SYS_SHMCTL,
"shmat": SNR_SHMAT, "shmat": SYS_SHMAT,
"shmdt": SNR_SHMDT, "shmdt": SYS_SHMDT,
"socket": SNR_SOCKET, "socket": SYS_SOCKET,
"socketpair": SNR_SOCKETPAIR, "socketpair": SYS_SOCKETPAIR,
"bind": SNR_BIND, "bind": SYS_BIND,
"listen": SNR_LISTEN, "listen": SYS_LISTEN,
"accept": SNR_ACCEPT, "accept": SYS_ACCEPT,
"connect": SNR_CONNECT, "connect": SYS_CONNECT,
"getsockname": SNR_GETSOCKNAME, "getsockname": SYS_GETSOCKNAME,
"getpeername": SNR_GETPEERNAME, "getpeername": SYS_GETPEERNAME,
"sendto": SNR_SENDTO, "sendto": SYS_SENDTO,
"recvfrom": SNR_RECVFROM, "recvfrom": SYS_RECVFROM,
"setsockopt": SNR_SETSOCKOPT, "setsockopt": SYS_SETSOCKOPT,
"getsockopt": SNR_GETSOCKOPT, "getsockopt": SYS_GETSOCKOPT,
"shutdown": SNR_SHUTDOWN, "shutdown": SYS_SHUTDOWN,
"sendmsg": SNR_SENDMSG, "sendmsg": SYS_SENDMSG,
"recvmsg": SNR_RECVMSG, "recvmsg": SYS_RECVMSG,
"readahead": SNR_READAHEAD, "readahead": SYS_READAHEAD,
"brk": SNR_BRK, "brk": SYS_BRK,
"munmap": SNR_MUNMAP, "munmap": SYS_MUNMAP,
"mremap": SNR_MREMAP, "mremap": SYS_MREMAP,
"add_key": SNR_ADD_KEY, "add_key": SYS_ADD_KEY,
"request_key": SNR_REQUEST_KEY, "request_key": SYS_REQUEST_KEY,
"keyctl": SNR_KEYCTL, "keyctl": SYS_KEYCTL,
"clone": SNR_CLONE, "clone": SYS_CLONE,
"execve": SNR_EXECVE, "execve": SYS_EXECVE,
"mmap": SNR_MMAP, "mmap": SYS_MMAP,
"fadvise64": SNR_FADVISE64, "fadvise64": SYS_FADVISE64,
"swapon": SNR_SWAPON, "swapon": SYS_SWAPON,
"swapoff": SNR_SWAPOFF, "swapoff": SYS_SWAPOFF,
"mprotect": SNR_MPROTECT, "mprotect": SYS_MPROTECT,
"msync": SNR_MSYNC, "msync": SYS_MSYNC,
"mlock": SNR_MLOCK, "mlock": SYS_MLOCK,
"munlock": SNR_MUNLOCK, "munlock": SYS_MUNLOCK,
"mlockall": SNR_MLOCKALL, "mlockall": SYS_MLOCKALL,
"munlockall": SNR_MUNLOCKALL, "munlockall": SYS_MUNLOCKALL,
"mincore": SNR_MINCORE, "mincore": SYS_MINCORE,
"madvise": SNR_MADVISE, "madvise": SYS_MADVISE,
"remap_file_pages": SNR_REMAP_FILE_PAGES, "remap_file_pages": SYS_REMAP_FILE_PAGES,
"mbind": SNR_MBIND, "mbind": SYS_MBIND,
"get_mempolicy": SNR_GET_MEMPOLICY, "get_mempolicy": SYS_GET_MEMPOLICY,
"set_mempolicy": SNR_SET_MEMPOLICY, "set_mempolicy": SYS_SET_MEMPOLICY,
"migrate_pages": SNR_MIGRATE_PAGES, "migrate_pages": SYS_MIGRATE_PAGES,
"move_pages": SNR_MOVE_PAGES, "move_pages": SYS_MOVE_PAGES,
"rt_tgsigqueueinfo": SNR_RT_TGSIGQUEUEINFO, "rt_tgsigqueueinfo": SYS_RT_TGSIGQUEUEINFO,
"perf_event_open": SNR_PERF_EVENT_OPEN, "perf_event_open": SYS_PERF_EVENT_OPEN,
"accept4": SNR_ACCEPT4, "accept4": SYS_ACCEPT4,
"recvmmsg": SNR_RECVMMSG, "recvmmsg": SYS_RECVMMSG,
"wait4": SNR_WAIT4, "wait4": SYS_WAIT4,
"prlimit64": SNR_PRLIMIT64, "prlimit64": SYS_PRLIMIT64,
"fanotify_init": SNR_FANOTIFY_INIT, "fanotify_init": SYS_FANOTIFY_INIT,
"fanotify_mark": SNR_FANOTIFY_MARK, "fanotify_mark": SYS_FANOTIFY_MARK,
"name_to_handle_at": SNR_NAME_TO_HANDLE_AT, "name_to_handle_at": SYS_NAME_TO_HANDLE_AT,
"open_by_handle_at": SNR_OPEN_BY_HANDLE_AT, "open_by_handle_at": SYS_OPEN_BY_HANDLE_AT,
"clock_adjtime": SNR_CLOCK_ADJTIME, "clock_adjtime": SYS_CLOCK_ADJTIME,
"syncfs": SNR_SYNCFS, "syncfs": SYS_SYNCFS,
"setns": SNR_SETNS, "setns": SYS_SETNS,
"sendmmsg": SNR_SENDMMSG, "sendmmsg": SYS_SENDMMSG,
"process_vm_readv": SNR_PROCESS_VM_READV, "process_vm_readv": SYS_PROCESS_VM_READV,
"process_vm_writev": SNR_PROCESS_VM_WRITEV, "process_vm_writev": SYS_PROCESS_VM_WRITEV,
"kcmp": SNR_KCMP, "kcmp": SYS_KCMP,
"finit_module": SNR_FINIT_MODULE, "finit_module": SYS_FINIT_MODULE,
"sched_setattr": SNR_SCHED_SETATTR, "sched_setattr": SYS_SCHED_SETATTR,
"sched_getattr": SNR_SCHED_GETATTR, "sched_getattr": SYS_SCHED_GETATTR,
"renameat2": SNR_RENAMEAT2, "renameat2": SYS_RENAMEAT2,
"seccomp": SNR_SECCOMP, "seccomp": SYS_SECCOMP,
"getrandom": SNR_GETRANDOM, "getrandom": SYS_GETRANDOM,
"memfd_create": SNR_MEMFD_CREATE, "memfd_create": SYS_MEMFD_CREATE,
"bpf": SNR_BPF, "bpf": SYS_BPF,
"execveat": SNR_EXECVEAT, "execveat": SYS_EXECVEAT,
"userfaultfd": SNR_USERFAULTFD, "userfaultfd": SYS_USERFAULTFD,
"membarrier": SNR_MEMBARRIER, "membarrier": SYS_MEMBARRIER,
"mlock2": SNR_MLOCK2, "mlock2": SYS_MLOCK2,
"copy_file_range": SNR_COPY_FILE_RANGE, "copy_file_range": SYS_COPY_FILE_RANGE,
"preadv2": SNR_PREADV2, "preadv2": SYS_PREADV2,
"pwritev2": SNR_PWRITEV2, "pwritev2": SYS_PWRITEV2,
"pkey_mprotect": SNR_PKEY_MPROTECT, "pkey_mprotect": SYS_PKEY_MPROTECT,
"pkey_alloc": SNR_PKEY_ALLOC, "pkey_alloc": SYS_PKEY_ALLOC,
"pkey_free": SNR_PKEY_FREE, "pkey_free": SYS_PKEY_FREE,
"statx": SNR_STATX, "statx": SYS_STATX,
"io_pgetevents": SNR_IO_PGETEVENTS, "io_pgetevents": SYS_IO_PGETEVENTS,
"rseq": SNR_RSEQ, "rseq": SYS_RSEQ,
"kexec_file_load": SNR_KEXEC_FILE_LOAD, "kexec_file_load": SYS_KEXEC_FILE_LOAD,
"pidfd_send_signal": SNR_PIDFD_SEND_SIGNAL, "pidfd_send_signal": SYS_PIDFD_SEND_SIGNAL,
"io_uring_setup": SNR_IO_URING_SETUP, "io_uring_setup": SYS_IO_URING_SETUP,
"io_uring_enter": SNR_IO_URING_ENTER, "io_uring_enter": SYS_IO_URING_ENTER,
"io_uring_register": SNR_IO_URING_REGISTER, "io_uring_register": SYS_IO_URING_REGISTER,
"open_tree": SNR_OPEN_TREE, "open_tree": SYS_OPEN_TREE,
"move_mount": SNR_MOVE_MOUNT, "move_mount": SYS_MOVE_MOUNT,
"fsopen": SNR_FSOPEN, "fsopen": SYS_FSOPEN,
"fsconfig": SNR_FSCONFIG, "fsconfig": SYS_FSCONFIG,
"fsmount": SNR_FSMOUNT, "fsmount": SYS_FSMOUNT,
"fspick": SNR_FSPICK, "fspick": SYS_FSPICK,
"pidfd_open": SNR_PIDFD_OPEN, "pidfd_open": SYS_PIDFD_OPEN,
"clone3": SNR_CLONE3, "clone3": SYS_CLONE3,
"close_range": SNR_CLOSE_RANGE, "close_range": SYS_CLOSE_RANGE,
"openat2": SNR_OPENAT2, "openat2": SYS_OPENAT2,
"pidfd_getfd": SNR_PIDFD_GETFD, "pidfd_getfd": SYS_PIDFD_GETFD,
"faccessat2": SNR_FACCESSAT2, "faccessat2": SYS_FACCESSAT2,
"process_madvise": SNR_PROCESS_MADVISE, "process_madvise": SYS_PROCESS_MADVISE,
"epoll_pwait2": SNR_EPOLL_PWAIT2, "epoll_pwait2": SYS_EPOLL_PWAIT2,
"mount_setattr": SNR_MOUNT_SETATTR, "mount_setattr": SYS_MOUNT_SETATTR,
"quotactl_fd": SNR_QUOTACTL_FD, "quotactl_fd": SYS_QUOTACTL_FD,
"landlock_create_ruleset": SNR_LANDLOCK_CREATE_RULESET, "landlock_create_ruleset": SYS_LANDLOCK_CREATE_RULESET,
"landlock_add_rule": SNR_LANDLOCK_ADD_RULE, "landlock_add_rule": SYS_LANDLOCK_ADD_RULE,
"landlock_restrict_self": SNR_LANDLOCK_RESTRICT_SELF, "landlock_restrict_self": SYS_LANDLOCK_RESTRICT_SELF,
"memfd_secret": SNR_MEMFD_SECRET, "memfd_secret": SYS_MEMFD_SECRET,
"process_mrelease": SNR_PROCESS_MRELEASE, "process_mrelease": SYS_PROCESS_MRELEASE,
"futex_waitv": SNR_FUTEX_WAITV, "futex_waitv": SYS_FUTEX_WAITV,
"set_mempolicy_home_node": SNR_SET_MEMPOLICY_HOME_NODE, "set_mempolicy_home_node": SYS_SET_MEMPOLICY_HOME_NODE,
"cachestat": SNR_CACHESTAT, "cachestat": SYS_CACHESTAT,
"fchmodat2": SNR_FCHMODAT2, "fchmodat2": SYS_FCHMODAT2,
"map_shadow_stack": SNR_MAP_SHADOW_STACK, "map_shadow_stack": SYS_MAP_SHADOW_STACK,
"futex_wake": SNR_FUTEX_WAKE, "futex_wake": SYS_FUTEX_WAKE,
"futex_wait": SNR_FUTEX_WAIT, "futex_wait": SYS_FUTEX_WAIT,
"futex_requeue": SNR_FUTEX_REQUEUE, "futex_requeue": SYS_FUTEX_REQUEUE,
"statmount": SNR_STATMOUNT, "statmount": SYS_STATMOUNT,
"listmount": SNR_LISTMOUNT, "listmount": SYS_LISTMOUNT,
"lsm_get_self_attr": SNR_LSM_GET_SELF_ATTR, "lsm_get_self_attr": SYS_LSM_GET_SELF_ATTR,
"lsm_set_self_attr": SNR_LSM_SET_SELF_ATTR, "lsm_set_self_attr": SYS_LSM_SET_SELF_ATTR,
"lsm_list_modules": SNR_LSM_LIST_MODULES, "lsm_list_modules": SYS_LSM_LIST_MODULES,
"mseal": SNR_MSEAL, "mseal": SYS_MSEAL,
} }
const ( const (
@ -380,324 +380,3 @@ const (
SYS_LSM_LIST_MODULES = 461 SYS_LSM_LIST_MODULES = 461
SYS_MSEAL = 462 SYS_MSEAL = 462
) )
const (
SNR_IO_SETUP ScmpSyscall = SYS_IO_SETUP
SNR_IO_DESTROY ScmpSyscall = SYS_IO_DESTROY
SNR_IO_SUBMIT ScmpSyscall = SYS_IO_SUBMIT
SNR_IO_CANCEL ScmpSyscall = SYS_IO_CANCEL
SNR_IO_GETEVENTS ScmpSyscall = SYS_IO_GETEVENTS
SNR_SETXATTR ScmpSyscall = SYS_SETXATTR
SNR_LSETXATTR ScmpSyscall = SYS_LSETXATTR
SNR_FSETXATTR ScmpSyscall = SYS_FSETXATTR
SNR_GETXATTR ScmpSyscall = SYS_GETXATTR
SNR_LGETXATTR ScmpSyscall = SYS_LGETXATTR
SNR_FGETXATTR ScmpSyscall = SYS_FGETXATTR
SNR_LISTXATTR ScmpSyscall = SYS_LISTXATTR
SNR_LLISTXATTR ScmpSyscall = SYS_LLISTXATTR
SNR_FLISTXATTR ScmpSyscall = SYS_FLISTXATTR
SNR_REMOVEXATTR ScmpSyscall = SYS_REMOVEXATTR
SNR_LREMOVEXATTR ScmpSyscall = SYS_LREMOVEXATTR
SNR_FREMOVEXATTR ScmpSyscall = SYS_FREMOVEXATTR
SNR_GETCWD ScmpSyscall = SYS_GETCWD
SNR_LOOKUP_DCOOKIE ScmpSyscall = SYS_LOOKUP_DCOOKIE
SNR_EVENTFD2 ScmpSyscall = SYS_EVENTFD2
SNR_EPOLL_CREATE1 ScmpSyscall = SYS_EPOLL_CREATE1
SNR_EPOLL_CTL ScmpSyscall = SYS_EPOLL_CTL
SNR_EPOLL_PWAIT ScmpSyscall = SYS_EPOLL_PWAIT
SNR_DUP ScmpSyscall = SYS_DUP
SNR_DUP3 ScmpSyscall = SYS_DUP3
SNR_FCNTL ScmpSyscall = SYS_FCNTL
SNR_INOTIFY_INIT1 ScmpSyscall = SYS_INOTIFY_INIT1
SNR_INOTIFY_ADD_WATCH ScmpSyscall = SYS_INOTIFY_ADD_WATCH
SNR_INOTIFY_RM_WATCH ScmpSyscall = SYS_INOTIFY_RM_WATCH
SNR_IOCTL ScmpSyscall = SYS_IOCTL
SNR_IOPRIO_SET ScmpSyscall = SYS_IOPRIO_SET
SNR_IOPRIO_GET ScmpSyscall = SYS_IOPRIO_GET
SNR_FLOCK ScmpSyscall = SYS_FLOCK
SNR_MKNODAT ScmpSyscall = SYS_MKNODAT
SNR_MKDIRAT ScmpSyscall = SYS_MKDIRAT
SNR_UNLINKAT ScmpSyscall = SYS_UNLINKAT
SNR_SYMLINKAT ScmpSyscall = SYS_SYMLINKAT
SNR_LINKAT ScmpSyscall = SYS_LINKAT
SNR_RENAMEAT ScmpSyscall = SYS_RENAMEAT
SNR_UMOUNT2 ScmpSyscall = SYS_UMOUNT2
SNR_MOUNT ScmpSyscall = SYS_MOUNT
SNR_PIVOT_ROOT ScmpSyscall = SYS_PIVOT_ROOT
SNR_NFSSERVCTL ScmpSyscall = SYS_NFSSERVCTL
SNR_STATFS ScmpSyscall = SYS_STATFS
SNR_FSTATFS ScmpSyscall = SYS_FSTATFS
SNR_TRUNCATE ScmpSyscall = SYS_TRUNCATE
SNR_FTRUNCATE ScmpSyscall = SYS_FTRUNCATE
SNR_FALLOCATE ScmpSyscall = SYS_FALLOCATE
SNR_FACCESSAT ScmpSyscall = SYS_FACCESSAT
SNR_CHDIR ScmpSyscall = SYS_CHDIR
SNR_FCHDIR ScmpSyscall = SYS_FCHDIR
SNR_CHROOT ScmpSyscall = SYS_CHROOT
SNR_FCHMOD ScmpSyscall = SYS_FCHMOD
SNR_FCHMODAT ScmpSyscall = SYS_FCHMODAT
SNR_FCHOWNAT ScmpSyscall = SYS_FCHOWNAT
SNR_FCHOWN ScmpSyscall = SYS_FCHOWN
SNR_OPENAT ScmpSyscall = SYS_OPENAT
SNR_CLOSE ScmpSyscall = SYS_CLOSE
SNR_VHANGUP ScmpSyscall = SYS_VHANGUP
SNR_PIPE2 ScmpSyscall = SYS_PIPE2
SNR_QUOTACTL ScmpSyscall = SYS_QUOTACTL
SNR_GETDENTS64 ScmpSyscall = SYS_GETDENTS64
SNR_LSEEK ScmpSyscall = SYS_LSEEK
SNR_READ ScmpSyscall = SYS_READ
SNR_WRITE ScmpSyscall = SYS_WRITE
SNR_READV ScmpSyscall = SYS_READV
SNR_WRITEV ScmpSyscall = SYS_WRITEV
SNR_PREAD64 ScmpSyscall = SYS_PREAD64
SNR_PWRITE64 ScmpSyscall = SYS_PWRITE64
SNR_PREADV ScmpSyscall = SYS_PREADV
SNR_PWRITEV ScmpSyscall = SYS_PWRITEV
SNR_SENDFILE ScmpSyscall = SYS_SENDFILE
SNR_PSELECT6 ScmpSyscall = SYS_PSELECT6
SNR_PPOLL ScmpSyscall = SYS_PPOLL
SNR_SIGNALFD4 ScmpSyscall = SYS_SIGNALFD4
SNR_VMSPLICE ScmpSyscall = SYS_VMSPLICE
SNR_SPLICE ScmpSyscall = SYS_SPLICE
SNR_TEE ScmpSyscall = SYS_TEE
SNR_READLINKAT ScmpSyscall = SYS_READLINKAT
SNR_NEWFSTATAT ScmpSyscall = SYS_NEWFSTATAT
SNR_FSTAT ScmpSyscall = SYS_FSTAT
SNR_SYNC ScmpSyscall = SYS_SYNC
SNR_FSYNC ScmpSyscall = SYS_FSYNC
SNR_FDATASYNC ScmpSyscall = SYS_FDATASYNC
SNR_SYNC_FILE_RANGE ScmpSyscall = SYS_SYNC_FILE_RANGE
SNR_TIMERFD_CREATE ScmpSyscall = SYS_TIMERFD_CREATE
SNR_TIMERFD_SETTIME ScmpSyscall = SYS_TIMERFD_SETTIME
SNR_TIMERFD_GETTIME ScmpSyscall = SYS_TIMERFD_GETTIME
SNR_UTIMENSAT ScmpSyscall = SYS_UTIMENSAT
SNR_ACCT ScmpSyscall = SYS_ACCT
SNR_CAPGET ScmpSyscall = SYS_CAPGET
SNR_CAPSET ScmpSyscall = SYS_CAPSET
SNR_PERSONALITY ScmpSyscall = SYS_PERSONALITY
SNR_EXIT ScmpSyscall = SYS_EXIT
SNR_EXIT_GROUP ScmpSyscall = SYS_EXIT_GROUP
SNR_WAITID ScmpSyscall = SYS_WAITID
SNR_SET_TID_ADDRESS ScmpSyscall = SYS_SET_TID_ADDRESS
SNR_UNSHARE ScmpSyscall = SYS_UNSHARE
SNR_FUTEX ScmpSyscall = SYS_FUTEX
SNR_SET_ROBUST_LIST ScmpSyscall = SYS_SET_ROBUST_LIST
SNR_GET_ROBUST_LIST ScmpSyscall = SYS_GET_ROBUST_LIST
SNR_NANOSLEEP ScmpSyscall = SYS_NANOSLEEP
SNR_GETITIMER ScmpSyscall = SYS_GETITIMER
SNR_SETITIMER ScmpSyscall = SYS_SETITIMER
SNR_KEXEC_LOAD ScmpSyscall = SYS_KEXEC_LOAD
SNR_INIT_MODULE ScmpSyscall = SYS_INIT_MODULE
SNR_DELETE_MODULE ScmpSyscall = SYS_DELETE_MODULE
SNR_TIMER_CREATE ScmpSyscall = SYS_TIMER_CREATE
SNR_TIMER_GETTIME ScmpSyscall = SYS_TIMER_GETTIME
SNR_TIMER_GETOVERRUN ScmpSyscall = SYS_TIMER_GETOVERRUN
SNR_TIMER_SETTIME ScmpSyscall = SYS_TIMER_SETTIME
SNR_TIMER_DELETE ScmpSyscall = SYS_TIMER_DELETE
SNR_CLOCK_SETTIME ScmpSyscall = SYS_CLOCK_SETTIME
SNR_CLOCK_GETTIME ScmpSyscall = SYS_CLOCK_GETTIME
SNR_CLOCK_GETRES ScmpSyscall = SYS_CLOCK_GETRES
SNR_CLOCK_NANOSLEEP ScmpSyscall = SYS_CLOCK_NANOSLEEP
SNR_SYSLOG ScmpSyscall = SYS_SYSLOG
SNR_PTRACE ScmpSyscall = SYS_PTRACE
SNR_SCHED_SETPARAM ScmpSyscall = SYS_SCHED_SETPARAM
SNR_SCHED_SETSCHEDULER ScmpSyscall = SYS_SCHED_SETSCHEDULER
SNR_SCHED_GETSCHEDULER ScmpSyscall = SYS_SCHED_GETSCHEDULER
SNR_SCHED_GETPARAM ScmpSyscall = SYS_SCHED_GETPARAM
SNR_SCHED_SETAFFINITY ScmpSyscall = SYS_SCHED_SETAFFINITY
SNR_SCHED_GETAFFINITY ScmpSyscall = SYS_SCHED_GETAFFINITY
SNR_SCHED_YIELD ScmpSyscall = SYS_SCHED_YIELD
SNR_SCHED_GET_PRIORITY_MAX ScmpSyscall = SYS_SCHED_GET_PRIORITY_MAX
SNR_SCHED_GET_PRIORITY_MIN ScmpSyscall = SYS_SCHED_GET_PRIORITY_MIN
SNR_SCHED_RR_GET_INTERVAL ScmpSyscall = SYS_SCHED_RR_GET_INTERVAL
SNR_RESTART_SYSCALL ScmpSyscall = SYS_RESTART_SYSCALL
SNR_KILL ScmpSyscall = SYS_KILL
SNR_TKILL ScmpSyscall = SYS_TKILL
SNR_TGKILL ScmpSyscall = SYS_TGKILL
SNR_SIGALTSTACK ScmpSyscall = SYS_SIGALTSTACK
SNR_RT_SIGSUSPEND ScmpSyscall = SYS_RT_SIGSUSPEND
SNR_RT_SIGACTION ScmpSyscall = SYS_RT_SIGACTION
SNR_RT_SIGPROCMASK ScmpSyscall = SYS_RT_SIGPROCMASK
SNR_RT_SIGPENDING ScmpSyscall = SYS_RT_SIGPENDING
SNR_RT_SIGTIMEDWAIT ScmpSyscall = SYS_RT_SIGTIMEDWAIT
SNR_RT_SIGQUEUEINFO ScmpSyscall = SYS_RT_SIGQUEUEINFO
SNR_RT_SIGRETURN ScmpSyscall = SYS_RT_SIGRETURN
SNR_SETPRIORITY ScmpSyscall = SYS_SETPRIORITY
SNR_GETPRIORITY ScmpSyscall = SYS_GETPRIORITY
SNR_REBOOT ScmpSyscall = SYS_REBOOT
SNR_SETREGID ScmpSyscall = SYS_SETREGID
SNR_SETGID ScmpSyscall = SYS_SETGID
SNR_SETREUID ScmpSyscall = SYS_SETREUID
SNR_SETUID ScmpSyscall = SYS_SETUID
SNR_SETRESUID ScmpSyscall = SYS_SETRESUID
SNR_GETRESUID ScmpSyscall = SYS_GETRESUID
SNR_SETRESGID ScmpSyscall = SYS_SETRESGID
SNR_GETRESGID ScmpSyscall = SYS_GETRESGID
SNR_SETFSUID ScmpSyscall = SYS_SETFSUID
SNR_SETFSGID ScmpSyscall = SYS_SETFSGID
SNR_TIMES ScmpSyscall = SYS_TIMES
SNR_SETPGID ScmpSyscall = SYS_SETPGID
SNR_GETPGID ScmpSyscall = SYS_GETPGID
SNR_GETSID ScmpSyscall = SYS_GETSID
SNR_SETSID ScmpSyscall = SYS_SETSID
SNR_GETGROUPS ScmpSyscall = SYS_GETGROUPS
SNR_SETGROUPS ScmpSyscall = SYS_SETGROUPS
SNR_UNAME ScmpSyscall = SYS_UNAME
SNR_SETHOSTNAME ScmpSyscall = SYS_SETHOSTNAME
SNR_SETDOMAINNAME ScmpSyscall = SYS_SETDOMAINNAME
SNR_GETRLIMIT ScmpSyscall = SYS_GETRLIMIT
SNR_SETRLIMIT ScmpSyscall = SYS_SETRLIMIT
SNR_GETRUSAGE ScmpSyscall = SYS_GETRUSAGE
SNR_UMASK ScmpSyscall = SYS_UMASK
SNR_PRCTL ScmpSyscall = SYS_PRCTL
SNR_GETCPU ScmpSyscall = SYS_GETCPU
SNR_GETTIMEOFDAY ScmpSyscall = SYS_GETTIMEOFDAY
SNR_SETTIMEOFDAY ScmpSyscall = SYS_SETTIMEOFDAY
SNR_ADJTIMEX ScmpSyscall = SYS_ADJTIMEX
SNR_GETPID ScmpSyscall = SYS_GETPID
SNR_GETPPID ScmpSyscall = SYS_GETPPID
SNR_GETUID ScmpSyscall = SYS_GETUID
SNR_GETEUID ScmpSyscall = SYS_GETEUID
SNR_GETGID ScmpSyscall = SYS_GETGID
SNR_GETEGID ScmpSyscall = SYS_GETEGID
SNR_GETTID ScmpSyscall = SYS_GETTID
SNR_SYSINFO ScmpSyscall = SYS_SYSINFO
SNR_MQ_OPEN ScmpSyscall = SYS_MQ_OPEN
SNR_MQ_UNLINK ScmpSyscall = SYS_MQ_UNLINK
SNR_MQ_TIMEDSEND ScmpSyscall = SYS_MQ_TIMEDSEND
SNR_MQ_TIMEDRECEIVE ScmpSyscall = SYS_MQ_TIMEDRECEIVE
SNR_MQ_NOTIFY ScmpSyscall = SYS_MQ_NOTIFY
SNR_MQ_GETSETATTR ScmpSyscall = SYS_MQ_GETSETATTR
SNR_MSGGET ScmpSyscall = SYS_MSGGET
SNR_MSGCTL ScmpSyscall = SYS_MSGCTL
SNR_MSGRCV ScmpSyscall = SYS_MSGRCV
SNR_MSGSND ScmpSyscall = SYS_MSGSND
SNR_SEMGET ScmpSyscall = SYS_SEMGET
SNR_SEMCTL ScmpSyscall = SYS_SEMCTL
SNR_SEMTIMEDOP ScmpSyscall = SYS_SEMTIMEDOP
SNR_SEMOP ScmpSyscall = SYS_SEMOP
SNR_SHMGET ScmpSyscall = SYS_SHMGET
SNR_SHMCTL ScmpSyscall = SYS_SHMCTL
SNR_SHMAT ScmpSyscall = SYS_SHMAT
SNR_SHMDT ScmpSyscall = SYS_SHMDT
SNR_SOCKET ScmpSyscall = SYS_SOCKET
SNR_SOCKETPAIR ScmpSyscall = SYS_SOCKETPAIR
SNR_BIND ScmpSyscall = SYS_BIND
SNR_LISTEN ScmpSyscall = SYS_LISTEN
SNR_ACCEPT ScmpSyscall = SYS_ACCEPT
SNR_CONNECT ScmpSyscall = SYS_CONNECT
SNR_GETSOCKNAME ScmpSyscall = SYS_GETSOCKNAME
SNR_GETPEERNAME ScmpSyscall = SYS_GETPEERNAME
SNR_SENDTO ScmpSyscall = SYS_SENDTO
SNR_RECVFROM ScmpSyscall = SYS_RECVFROM
SNR_SETSOCKOPT ScmpSyscall = SYS_SETSOCKOPT
SNR_GETSOCKOPT ScmpSyscall = SYS_GETSOCKOPT
SNR_SHUTDOWN ScmpSyscall = SYS_SHUTDOWN
SNR_SENDMSG ScmpSyscall = SYS_SENDMSG
SNR_RECVMSG ScmpSyscall = SYS_RECVMSG
SNR_READAHEAD ScmpSyscall = SYS_READAHEAD
SNR_BRK ScmpSyscall = SYS_BRK
SNR_MUNMAP ScmpSyscall = SYS_MUNMAP
SNR_MREMAP ScmpSyscall = SYS_MREMAP
SNR_ADD_KEY ScmpSyscall = SYS_ADD_KEY
SNR_REQUEST_KEY ScmpSyscall = SYS_REQUEST_KEY
SNR_KEYCTL ScmpSyscall = SYS_KEYCTL
SNR_CLONE ScmpSyscall = SYS_CLONE
SNR_EXECVE ScmpSyscall = SYS_EXECVE
SNR_MMAP ScmpSyscall = SYS_MMAP
SNR_FADVISE64 ScmpSyscall = SYS_FADVISE64
SNR_SWAPON ScmpSyscall = SYS_SWAPON
SNR_SWAPOFF ScmpSyscall = SYS_SWAPOFF
SNR_MPROTECT ScmpSyscall = SYS_MPROTECT
SNR_MSYNC ScmpSyscall = SYS_MSYNC
SNR_MLOCK ScmpSyscall = SYS_MLOCK
SNR_MUNLOCK ScmpSyscall = SYS_MUNLOCK
SNR_MLOCKALL ScmpSyscall = SYS_MLOCKALL
SNR_MUNLOCKALL ScmpSyscall = SYS_MUNLOCKALL
SNR_MINCORE ScmpSyscall = SYS_MINCORE
SNR_MADVISE ScmpSyscall = SYS_MADVISE
SNR_REMAP_FILE_PAGES ScmpSyscall = SYS_REMAP_FILE_PAGES
SNR_MBIND ScmpSyscall = SYS_MBIND
SNR_GET_MEMPOLICY ScmpSyscall = SYS_GET_MEMPOLICY
SNR_SET_MEMPOLICY ScmpSyscall = SYS_SET_MEMPOLICY
SNR_MIGRATE_PAGES ScmpSyscall = SYS_MIGRATE_PAGES
SNR_MOVE_PAGES ScmpSyscall = SYS_MOVE_PAGES
SNR_RT_TGSIGQUEUEINFO ScmpSyscall = SYS_RT_TGSIGQUEUEINFO
SNR_PERF_EVENT_OPEN ScmpSyscall = SYS_PERF_EVENT_OPEN
SNR_ACCEPT4 ScmpSyscall = SYS_ACCEPT4
SNR_RECVMMSG ScmpSyscall = SYS_RECVMMSG
SNR_WAIT4 ScmpSyscall = SYS_WAIT4
SNR_PRLIMIT64 ScmpSyscall = SYS_PRLIMIT64
SNR_FANOTIFY_INIT ScmpSyscall = SYS_FANOTIFY_INIT
SNR_FANOTIFY_MARK ScmpSyscall = SYS_FANOTIFY_MARK
SNR_NAME_TO_HANDLE_AT ScmpSyscall = SYS_NAME_TO_HANDLE_AT
SNR_OPEN_BY_HANDLE_AT ScmpSyscall = SYS_OPEN_BY_HANDLE_AT
SNR_CLOCK_ADJTIME ScmpSyscall = SYS_CLOCK_ADJTIME
SNR_SYNCFS ScmpSyscall = SYS_SYNCFS
SNR_SETNS ScmpSyscall = SYS_SETNS
SNR_SENDMMSG ScmpSyscall = SYS_SENDMMSG
SNR_PROCESS_VM_READV ScmpSyscall = SYS_PROCESS_VM_READV
SNR_PROCESS_VM_WRITEV ScmpSyscall = SYS_PROCESS_VM_WRITEV
SNR_KCMP ScmpSyscall = SYS_KCMP
SNR_FINIT_MODULE ScmpSyscall = SYS_FINIT_MODULE
SNR_SCHED_SETATTR ScmpSyscall = SYS_SCHED_SETATTR
SNR_SCHED_GETATTR ScmpSyscall = SYS_SCHED_GETATTR
SNR_RENAMEAT2 ScmpSyscall = SYS_RENAMEAT2
SNR_SECCOMP ScmpSyscall = SYS_SECCOMP
SNR_GETRANDOM ScmpSyscall = SYS_GETRANDOM
SNR_MEMFD_CREATE ScmpSyscall = SYS_MEMFD_CREATE
SNR_BPF ScmpSyscall = SYS_BPF
SNR_EXECVEAT ScmpSyscall = SYS_EXECVEAT
SNR_USERFAULTFD ScmpSyscall = SYS_USERFAULTFD
SNR_MEMBARRIER ScmpSyscall = SYS_MEMBARRIER
SNR_MLOCK2 ScmpSyscall = SYS_MLOCK2
SNR_COPY_FILE_RANGE ScmpSyscall = SYS_COPY_FILE_RANGE
SNR_PREADV2 ScmpSyscall = SYS_PREADV2
SNR_PWRITEV2 ScmpSyscall = SYS_PWRITEV2
SNR_PKEY_MPROTECT ScmpSyscall = SYS_PKEY_MPROTECT
SNR_PKEY_ALLOC ScmpSyscall = SYS_PKEY_ALLOC
SNR_PKEY_FREE ScmpSyscall = SYS_PKEY_FREE
SNR_STATX ScmpSyscall = SYS_STATX
SNR_IO_PGETEVENTS ScmpSyscall = SYS_IO_PGETEVENTS
SNR_RSEQ ScmpSyscall = SYS_RSEQ
SNR_KEXEC_FILE_LOAD ScmpSyscall = SYS_KEXEC_FILE_LOAD
SNR_PIDFD_SEND_SIGNAL ScmpSyscall = SYS_PIDFD_SEND_SIGNAL
SNR_IO_URING_SETUP ScmpSyscall = SYS_IO_URING_SETUP
SNR_IO_URING_ENTER ScmpSyscall = SYS_IO_URING_ENTER
SNR_IO_URING_REGISTER ScmpSyscall = SYS_IO_URING_REGISTER
SNR_OPEN_TREE ScmpSyscall = SYS_OPEN_TREE
SNR_MOVE_MOUNT ScmpSyscall = SYS_MOVE_MOUNT
SNR_FSOPEN ScmpSyscall = SYS_FSOPEN
SNR_FSCONFIG ScmpSyscall = SYS_FSCONFIG
SNR_FSMOUNT ScmpSyscall = SYS_FSMOUNT
SNR_FSPICK ScmpSyscall = SYS_FSPICK
SNR_PIDFD_OPEN ScmpSyscall = SYS_PIDFD_OPEN
SNR_CLONE3 ScmpSyscall = SYS_CLONE3
SNR_CLOSE_RANGE ScmpSyscall = SYS_CLOSE_RANGE
SNR_OPENAT2 ScmpSyscall = SYS_OPENAT2
SNR_PIDFD_GETFD ScmpSyscall = SYS_PIDFD_GETFD
SNR_FACCESSAT2 ScmpSyscall = SYS_FACCESSAT2
SNR_PROCESS_MADVISE ScmpSyscall = SYS_PROCESS_MADVISE
SNR_EPOLL_PWAIT2 ScmpSyscall = SYS_EPOLL_PWAIT2
SNR_MOUNT_SETATTR ScmpSyscall = SYS_MOUNT_SETATTR
SNR_QUOTACTL_FD ScmpSyscall = SYS_QUOTACTL_FD
SNR_LANDLOCK_CREATE_RULESET ScmpSyscall = SYS_LANDLOCK_CREATE_RULESET
SNR_LANDLOCK_ADD_RULE ScmpSyscall = SYS_LANDLOCK_ADD_RULE
SNR_LANDLOCK_RESTRICT_SELF ScmpSyscall = SYS_LANDLOCK_RESTRICT_SELF
SNR_MEMFD_SECRET ScmpSyscall = SYS_MEMFD_SECRET
SNR_PROCESS_MRELEASE ScmpSyscall = SYS_PROCESS_MRELEASE
SNR_FUTEX_WAITV ScmpSyscall = SYS_FUTEX_WAITV
SNR_SET_MEMPOLICY_HOME_NODE ScmpSyscall = SYS_SET_MEMPOLICY_HOME_NODE
SNR_CACHESTAT ScmpSyscall = SYS_CACHESTAT
SNR_FCHMODAT2 ScmpSyscall = SYS_FCHMODAT2
SNR_MAP_SHADOW_STACK ScmpSyscall = SYS_MAP_SHADOW_STACK
SNR_FUTEX_WAKE ScmpSyscall = SYS_FUTEX_WAKE
SNR_FUTEX_WAIT ScmpSyscall = SYS_FUTEX_WAIT
SNR_FUTEX_REQUEUE ScmpSyscall = SYS_FUTEX_REQUEUE
SNR_STATMOUNT ScmpSyscall = SYS_STATMOUNT
SNR_LISTMOUNT ScmpSyscall = SYS_LISTMOUNT
SNR_LSM_GET_SELF_ATTR ScmpSyscall = SYS_LSM_GET_SELF_ATTR
SNR_LSM_SET_SELF_ATTR ScmpSyscall = SYS_LSM_SET_SELF_ATTR
SNR_LSM_LIST_MODULES ScmpSyscall = SYS_LSM_LIST_MODULES
SNR_MSEAL ScmpSyscall = SYS_MSEAL
)

View File

@ -2,15 +2,13 @@ package stub_test
import ( import (
"testing" "testing"
_ "unsafe" // for go:linkname _ "unsafe"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
// Made available here to check panic recovery behaviour.
//
//go:linkname handleExitNew hakurei.app/container/stub.handleExitNew //go:linkname handleExitNew hakurei.app/container/stub.handleExitNew
func handleExitNew(t testing.TB) func handleExitNew(_ testing.TB)
// overrideTFailNow overrides the Fail and FailNow method. // overrideTFailNow overrides the Fail and FailNow method.
type overrideTFailNow struct { type overrideTFailNow struct {

6
dist/release.sh vendored
View File

@ -10,9 +10,9 @@ cp -rv "dist/comp" "${out}"
go generate ./... go generate ./...
go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static' go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static'
-X hakurei.app/internal/info.buildVersion=${VERSION} -X hakurei.app/internal.buildVersion=${VERSION}
-X hakurei.app/internal/info.hakureiPath=/usr/bin/hakurei -X hakurei.app/internal.hakureiPath=/usr/bin/hakurei
-X hakurei.app/internal/info.hsuPath=/usr/bin/hsu -X hakurei.app/internal.hsuPath=/usr/bin/hsu
-X main.hakureiPath=/usr/bin/hakurei" ./... -X main.hakureiPath=/usr/bin/hakurei" ./...
rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}" rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}"

View File

@ -114,7 +114,7 @@
inherit (pkgs) inherit (pkgs)
# passthru.buildInputs # passthru.buildInputs
go go
clang gcc
# nativeBuildInputs # nativeBuildInputs
pkg-config pkg-config
@ -129,10 +129,6 @@
zstd zstd
gnutar gnutar
coreutils coreutils
# for check
util-linux
nettools
; ;
}; };
hsu = pkgs.callPackage ./cmd/hsu/package.nix { inherit (self.packages.${system}) hakurei; }; hsu = pkgs.callPackage ./cmd/hsu/package.nix { inherit (self.packages.${system}) hakurei; };
@ -148,7 +144,7 @@
&& chmod -R +w . && chmod -R +w .
export HAKUREI_VERSION="v${hakurei.version}" export HAKUREI_VERSION="v${hakurei.version}"
CC="clang -O3 -Werror" ./dist/release.sh && mkdir $out && cp -v "dist/hakurei-$HAKUREI_VERSION.tar.gz"* $out ./dist/release.sh && mkdir $out && cp -v "dist/hakurei-$HAKUREI_VERSION.tar.gz"* $out
''; '';
} }
); );

View File

@ -7,7 +7,7 @@ import (
"syscall" "syscall"
"testing" "testing"
"hakurei.app/internal/helper" "hakurei.app/helper"
) )
func TestArgsString(t *testing.T) { func TestArgsString(t *testing.T) {

View File

@ -10,7 +10,7 @@ import (
"sync" "sync"
"syscall" "syscall"
"hakurei.app/internal/helper/proc" "hakurei.app/helper/proc"
) )
// NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer. // NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer.

View File

@ -9,7 +9,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/internal/helper" "hakurei.app/helper"
) )
func TestCmd(t *testing.T) { func TestCmd(t *testing.T) {

View File

@ -11,7 +11,7 @@ import (
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/internal/helper/proc" "hakurei.app/helper/proc"
"hakurei.app/message" "hakurei.app/message"
) )

View File

@ -9,7 +9,7 @@ import (
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/internal/helper" "hakurei.app/helper"
) )
func TestContainer(t *testing.T) { func TestContainer(t *testing.T) {

View File

@ -1,73 +0,0 @@
// Package helper exposes the internal/helper package.
//
// Deprecated: This package will be removed in 0.4.
package helper
import (
"context"
"io"
"os"
"os/exec"
"time"
_ "unsafe" // for go:linkname
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/internal/helper"
"hakurei.app/message"
)
//go:linkname WaitDelay hakurei.app/internal/helper.WaitDelay
var WaitDelay time.Duration
const (
// HakureiHelper is set to 1 when args fd is enabled and 0 otherwise.
HakureiHelper = helper.HakureiHelper
// HakureiStatus is set to 1 when stat fd is enabled and 0 otherwise.
HakureiStatus = helper.HakureiStatus
)
type Helper = helper.Helper
// NewCheckedArgs returns a checked null-terminated argument writer for a copy of args.
//
//go:linkname NewCheckedArgs hakurei.app/internal/helper.NewCheckedArgs
func NewCheckedArgs(args ...string) (wt io.WriterTo, err error)
// MustNewCheckedArgs returns a checked null-terminated argument writer for a copy of args.
// If s contains a NUL byte this function panics instead of returning an error.
//
//go:linkname MustNewCheckedArgs hakurei.app/internal/helper.MustNewCheckedArgs
func MustNewCheckedArgs(args ...string) io.WriterTo
// NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer.
// Function argF returns an array of arguments passed directly to the child process.
//
//go:linkname NewDirect hakurei.app/internal/helper.NewDirect
func NewDirect(
ctx context.Context,
name string,
wt io.WriterTo,
stat bool,
argF func(argsFd, statFd int) []string,
cmdF func(cmd *exec.Cmd),
extraFiles []*os.File,
) Helper
// New initialises a Helper instance with wt as the null-terminated argument writer.
//
//go:linkname New hakurei.app/internal/helper.New
func New(
ctx context.Context,
msg message.Msg,
pathname *check.Absolute, name string,
wt io.WriterTo,
stat bool,
argF func(argsFd, statFd int) []string,
cmdF func(z *container.Container),
extraFiles []*os.File,
) Helper
// InternalHelperStub is an internal function but exported because it is cross-package;
// it is part of the implementation of the helper stub.
func InternalHelperStub() { helper.InternalHelperStub() }

View File

@ -8,7 +8,7 @@ import (
"os" "os"
"time" "time"
"hakurei.app/internal/helper/proc" "hakurei.app/helper/proc"
) )
var WaitDelay = 2 * time.Second var WaitDelay = 2 * time.Second

View File

@ -13,7 +13,7 @@ import (
"testing" "testing"
"time" "time"
"hakurei.app/internal/helper" "hakurei.app/helper"
) )
var ( var (

View File

@ -1,63 +0,0 @@
// Deprecated: This package will be removed in 0.4.
package proc
import (
"context"
"io"
"os"
"os/exec"
"time"
_ "unsafe" // for go:linkname
"hakurei.app/internal/helper/proc"
)
//go:linkname FulfillmentTimeout hakurei.app/internal/helper/proc.FulfillmentTimeout
var FulfillmentTimeout time.Duration
// A File is an extra file with deferred initialisation.
type File = proc.File
// ExtraFilesPre is a linked list storing addresses of [os.File].
type ExtraFilesPre = proc.ExtraFilesPre
// Fulfill calls the [File.Fulfill] method on all files, starts cmd and blocks until all fulfillment completes.
//
//go:linkname Fulfill hakurei.app/internal/helper/proc.Fulfill
func Fulfill(ctx context.Context,
v *[]*os.File, start func() error,
files []File, extraFiles *ExtraFilesPre,
) (err error)
// InitFile initialises f as part of the slice extraFiles points to,
// and returns its final fd value.
//
//go:linkname InitFile hakurei.app/internal/helper/proc.InitFile
func InitFile(f File, extraFiles *ExtraFilesPre) (fd uintptr)
// BaseFile implements the Init method of the File interface and provides indirect access to extra file state.
type BaseFile = proc.BaseFile
//go:linkname ExtraFile hakurei.app/internal/helper/proc.ExtraFile
func ExtraFile(cmd *exec.Cmd, f *os.File) (fd uintptr)
//go:linkname ExtraFileSlice hakurei.app/internal/helper/proc.ExtraFileSlice
func ExtraFileSlice(extraFiles *[]*os.File, f *os.File) (fd uintptr)
// NewWriterTo returns a [File] that receives content from wt on fulfillment.
//
//go:linkname NewWriterTo hakurei.app/internal/helper/proc.NewWriterTo
func NewWriterTo(wt io.WriterTo) File
// NewStat returns a [File] implementing the behaviour
// of the receiving end of xdg-dbus-proxy stat fd.
//
//go:linkname NewStat hakurei.app/internal/helper/proc.NewStat
func NewStat(s *io.Closer) File
var (
//go:linkname ErrStatFault hakurei.app/internal/helper/proc.ErrStatFault
ErrStatFault error
//go:linkname ErrStatRead hakurei.app/internal/helper/proc.ErrStatRead
ErrStatRead error
)

View File

@ -5,7 +5,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/internal/helper" "hakurei.app/helper"
) )
func TestMain(m *testing.M) { container.TryArgv0(nil); helper.InternalHelperStub(); os.Exit(m.Run()) } func TestMain(m *testing.M) { container.TryArgv0(nil); helper.InternalHelperStub(); os.Exit(m.Run()) }

View File

@ -6,13 +6,11 @@ import (
"reflect" "reflect"
"testing" "testing"
"time" "time"
_ "unsafe" // for go:linkname _ "unsafe"
"hakurei.app/hst" "hakurei.app/hst"
) )
// Made available here to check time encoding behaviour of [hst.ID].
//
//go:linkname newInstanceID hakurei.app/hst.newInstanceID //go:linkname newInstanceID hakurei.app/hst.newInstanceID
func newInstanceID(id *hst.ID, p uint64) error func newInstanceID(id *hst.ID, p uint64) error

View File

@ -1,90 +0,0 @@
#include "libacl-helper.h"
#include <acl/libacl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/acl.h>
int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid,
acl_perm_t *perms, size_t plen) {
int ret;
bool v;
int i;
acl_t acl;
acl_entry_t entry;
acl_tag_t tag_type;
void *qualifier_p;
acl_permset_t permset;
ret = -1; /* acl_get_file */
acl = acl_get_file(path_p, ACL_TYPE_ACCESS);
if (acl == NULL)
goto out;
/* prune entries by uid */
for (i = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); i == 1;
i = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) {
ret = -2; /* acl_get_tag_type */
if (acl_get_tag_type(entry, &tag_type) != 0)
goto out;
if (tag_type != ACL_USER)
continue;
ret = -3; /* acl_get_qualifier */
qualifier_p = acl_get_qualifier(entry);
if (qualifier_p == NULL)
goto out;
v = *(uid_t *)qualifier_p == uid;
acl_free(qualifier_p);
if (!v)
continue;
ret = -4; /* acl_delete_entry */
if (acl_delete_entry(acl, entry) != 0)
goto out;
}
if (plen == 0)
goto set;
ret = -5; /* acl_create_entry */
if (acl_create_entry(&acl, &entry) != 0)
goto out;
ret = -6; /* acl_get_permset */
if (acl_get_permset(entry, &permset) != 0)
goto out;
ret = -7; /* acl_add_perm */
for (i = 0; i < plen; i++) {
if (acl_add_perm(permset, perms[i]) != 0)
goto out;
}
ret = -8; /* acl_set_tag_type */
if (acl_set_tag_type(entry, ACL_USER) != 0)
goto out;
ret = -9; /* acl_set_qualifier */
if (acl_set_qualifier(entry, (void *)&uid) != 0)
goto out;
set:
ret = -10; /* acl_calc_mask */
if (acl_calc_mask(&acl) != 0)
goto out;
ret = -11; /* acl_valid */
if (acl_valid(acl) != 0)
goto out;
ret = -12; /* acl_set_file */
if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) == 0)
ret = 0;
out:
free((void *)path_p);
if (acl != NULL)
acl_free((void *)acl);
return ret;
}

View File

@ -14,9 +14,9 @@ import (
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/internal/dbus" "hakurei.app/internal"
"hakurei.app/internal/info"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system/dbus"
) )
// osFile represents [os.File]. // osFile represents [os.File].
@ -156,7 +156,7 @@ func (direct) seccompLoad(rules []std.NativeRule, flags seccomp.ExportFlag) erro
return seccomp.Load(rules, flags) return seccomp.Load(rules, flags)
} }
func (direct) mustHsuPath() *check.Absolute { return info.MustHsuPath() } func (direct) mustHsuPath() *check.Absolute { return internal.MustHsuPath() }
func (direct) dbusAddress() (session, system string) { return dbus.Address() } func (direct) dbusAddress() (session, system string) { return dbus.Address() }

View File

@ -24,8 +24,8 @@ import (
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/system"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system"
) )
// call initialises a [stub.Call]. // call initialises a [stub.Call].

View File

@ -8,8 +8,8 @@ import (
"os/user" "os/user"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/system"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system"
) )
func newWithMessage(msg string) error { return newWithMessageError(msg, os.ErrInvalid) } func newWithMessage(msg string) error { return newWithMessageError(msg, os.ErrInvalid) }
@ -55,7 +55,7 @@ func (k *outcome) finalise(ctx context.Context, msg message.Msg, id *hst.ID, con
if errors.As(err, &unknownGroupError) { if errors.As(err, &unknownGroupError) {
return newWithMessageError(fmt.Sprintf("unknown group %q", name), unknownGroupError) return newWithMessageError(fmt.Sprintf("unknown group %q", name), unknownGroupError)
} else { } else {
return &hst.AppError{Step: "look up group by name", Err: err, Msg: err.Error()} return &hst.AppError{Step: "look up group by name", Err: err}
} }
} else { } else {
supp[i] = gid supp[i] = gid

30
internal/outcome/main.go Normal file
View File

@ -0,0 +1,30 @@
package outcome
import (
"context"
"log"
"time"
"hakurei.app/hst"
"hakurei.app/message"
)
// Main runs an app according to [hst.Config] and terminates. Main does not return.
func Main(ctx context.Context, msg message.Msg, config *hst.Config) {
var id hst.ID
if err := hst.NewInstanceID(&id); err != nil {
log.Fatal(err.Error())
}
seal := outcome{syscallDispatcher: direct{msg}}
finaliseTime := time.Now()
if err := seal.finalise(ctx, msg, &id, config); err != nil {
printMessageError(msg.GetLogger().Fatalln, "cannot seal app:", err)
panic("unreachable")
}
msg.Verbosef("finalise took %.2f ms", float64(time.Since(finaliseTime).Nanoseconds())/1e6)
seal.main(msg)
panic("unreachable")
}

View File

@ -21,10 +21,10 @@ import (
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl"
"hakurei.app/internal/dbus"
"hakurei.app/internal/system"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system"
"hakurei.app/system/acl"
"hakurei.app/system/dbus"
) )
func TestOutcomeMain(t *testing.T) { func TestOutcomeMain(t *testing.T) {
@ -141,7 +141,7 @@ func TestOutcomeMain(t *testing.T) {
Proc(fhs.AbsProc). Proc(fhs.AbsProc).
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755). Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice). Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice).
Tmpfs(fhs.AbsDevShm, 0, 01777). Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777).
// spRuntimeOp // spRuntimeOp
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
@ -243,7 +243,7 @@ func TestOutcomeMain(t *testing.T) {
Proc(m("/proc/")). Proc(m("/proc/")).
Tmpfs(hst.AbsPrivateTmp, 4096, 0755). Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
DevWritable(m("/dev/"), true). DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm/"), 0, 01777). Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755). Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/0"), m("/run/user/65534"), std.BindWritable). Bind(m("/tmp/hakurei.0/runtime/0"), m("/run/user/65534"), std.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/0"), m("/tmp/"), std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/0"), m("/tmp/"), std.BindWritable).
@ -412,7 +412,7 @@ func TestOutcomeMain(t *testing.T) {
Proc(m("/proc/")). Proc(m("/proc/")).
Tmpfs(hst.AbsPrivateTmp, 4096, 0755). Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
DevWritable(m("/dev/"), true). DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm/"), 0, 01777). Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755). Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/65534"), std.BindWritable). Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/65534"), std.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/9"), m("/tmp/"), std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/9"), m("/tmp/"), std.BindWritable).
@ -558,7 +558,7 @@ func TestOutcomeMain(t *testing.T) {
Proc(m("/proc/")). Proc(m("/proc/")).
Tmpfs(hst.AbsPrivateTmp, 4096, 0755). Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
DevWritable(m("/dev/"), true). DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm/"), 0, 01777). Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755). Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), std.BindWritable). Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), std.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), std.BindWritable).

View File

@ -9,10 +9,10 @@ import (
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl"
"hakurei.app/internal/env" "hakurei.app/internal/env"
"hakurei.app/internal/system"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system"
"hakurei.app/system/acl"
) )
// envAllocSize is the initial size of the env map pre-allocated when the configured env map is nil. // envAllocSize is the initial size of the env map pre-allocated when the configured env map is nil.

View File

@ -16,10 +16,10 @@ import (
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/info" "hakurei.app/internal"
"hakurei.app/internal/store" "hakurei.app/internal/store"
"hakurei.app/internal/system"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system"
) )
const ( const (
@ -33,13 +33,13 @@ const (
func NewStore(sc *hst.Paths) *store.Store { return store.New(sc.SharePath.Append("state")) } func NewStore(sc *hst.Paths) *store.Store { return store.New(sc.SharePath.Append("state")) }
// main carries out outcome and terminates. main does not return. // main carries out outcome and terminates. main does not return.
func (k *outcome) main(msg message.Msg, identifierFd int) { func (k *outcome) main(msg message.Msg) {
if k.ctx == nil || k.sys == nil || k.state == nil { if k.ctx == nil || k.sys == nil || k.state == nil {
panic("outcome: did not finalise") panic("outcome: did not finalise")
} }
// read comp value early for early failure // read comp value early for early failure
hsuPath := info.MustHsuPath() hsuPath := internal.MustHsuPath()
const ( const (
// transitions to processCommit, or processFinal on failure // transitions to processCommit, or processFinal on failure
@ -163,21 +163,6 @@ func (k *outcome) main(msg message.Msg, identifierFd int) {
continue continue
} }
if f := os.NewFile(uintptr(identifierFd), "identifier"); f != nil {
_, err = f.Write(k.state.id.v[:])
if err != nil {
unlock()
// transition here to avoid the commit/revert cycle on the doomed instance
perrorFatal(&hst.AppError{Step: "write instance identifier", Err: err},
"write instance identifier", processLifecycle)
continue
}
msg.Verbosef("wrote identifier to %d", identifierFd)
if err = f.Close(); err != nil {
msg.Verbose(err.Error())
}
}
err = k.sys.Commit() err = k.sys.Commit()
unlock() unlock()
if err != nil { if err != nil {

View File

@ -1,45 +0,0 @@
package outcome
import (
"context"
"log"
"time"
_ "unsafe" // for go:linkname
"hakurei.app/hst"
"hakurei.app/message"
)
// IsPollDescriptor reports whether fd is the descriptor being used by the poller.
//
// Made available here to determine and reject impossible fd.
//
//go:linkname IsPollDescriptor internal/poll.IsPollDescriptor
func IsPollDescriptor(fd uintptr) bool
// Main runs an app according to [hst.Config] and terminates. Main does not return.
func Main(ctx context.Context, msg message.Msg, config *hst.Config, fd int) {
// avoids runtime internals or standard streams
if fd >= 0 {
if IsPollDescriptor(uintptr(fd)) || fd < 3 {
log.Fatalf("invalid identifier fd %d", fd)
}
}
var id hst.ID
if err := hst.NewInstanceID(&id); err != nil {
log.Fatal(err.Error())
}
k := outcome{syscallDispatcher: direct{msg}}
finaliseTime := time.Now()
if err := k.finalise(ctx, msg, &id, config); err != nil {
printMessageError(msg.GetLogger().Fatalln, "cannot seal app:", err)
panic("unreachable")
}
msg.Verbosef("finalise took %.2f ms", float64(time.Since(finaliseTime).Nanoseconds())/1e6)
k.main(msg, fd)
panic("unreachable")
}

View File

@ -38,7 +38,7 @@ static void hakurei_shim_sigaction(int sig, siginfo_t *si, void *ucontext) {
void hakurei_shim_setup_cont_signal(pid_t ppid, int fd) { void hakurei_shim_setup_cont_signal(pid_t ppid, int fd) {
if (hakurei_shim_param_ppid != -1 || hakurei_shim_fd != -1) if (hakurei_shim_param_ppid != -1 || hakurei_shim_fd != -1)
*(volatile int *)NULL = 0; /* unreachable */ *(int *)NULL = 0; /* unreachable */
struct sigaction new_action = {0}, old_action = {0}; struct sigaction new_action = {0}, old_action = {0};
if (sigaction(SIGCONT, NULL, &old_action) != 0) if (sigaction(SIGCONT, NULL, &old_action) != 0)

View File

@ -66,7 +66,7 @@ func TestShimEntrypoint(t *testing.T) {
Proc(fhs.AbsProc). Proc(fhs.AbsProc).
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755). Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice). Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice).
Tmpfs(fhs.AbsDevShm, 0, 01777). Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777).
// spRuntimeOp // spRuntimeOp
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).

View File

@ -16,11 +16,11 @@ import (
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl"
"hakurei.app/internal/dbus"
"hakurei.app/internal/system"
"hakurei.app/internal/validate" "hakurei.app/internal/validate"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system"
"hakurei.app/system/acl"
"hakurei.app/system/dbus"
) )
const varRunNscd = fhs.Var + "run/nscd" const varRunNscd = fhs.Var + "run/nscd"
@ -116,7 +116,7 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error {
state.params.Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice) state.params.Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice)
} }
// /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it // /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it
state.params.Tmpfs(fhs.AbsDevShm, 0, 01777) state.params.Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777)
return nil return nil
} }

View File

@ -14,9 +14,9 @@ import (
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system"
"hakurei.app/internal/dbus" "hakurei.app/system/acl"
"hakurei.app/internal/system" "hakurei.app/system/dbus"
) )
func TestSpParamsOp(t *testing.T) { func TestSpParamsOp(t *testing.T) {
@ -72,7 +72,7 @@ func TestSpParamsOp(t *testing.T) {
Root(m("/var/lib/hakurei/base/org.debian"), std.BindWritable). Root(m("/var/lib/hakurei/base/org.debian"), std.BindWritable).
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755). Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
DevWritable(fhs.AbsDev, true). DevWritable(fhs.AbsDev, true).
Tmpfs(fhs.AbsDevShm, 0, 01777), Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"TERM": "xterm", "TERM": "xterm",
}, func(t *testing.T, state *outcomeStateParams) { }, func(t *testing.T, state *outcomeStateParams) {
@ -110,7 +110,7 @@ func TestSpParamsOp(t *testing.T) {
Root(m("/var/lib/hakurei/base/org.debian"), std.BindWritable). Root(m("/var/lib/hakurei/base/org.debian"), std.BindWritable).
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755). Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice). Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice).
Tmpfs(fhs.AbsDevShm, 0, 01777), Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"TERM": "xterm", "TERM": "xterm",
}, func(t *testing.T, state *outcomeStateParams) { }, func(t *testing.T, state *outcomeStateParams) {

View File

@ -5,8 +5,8 @@ import (
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system/acl"
"hakurei.app/internal/dbus" "hakurei.app/system/dbus"
) )
func init() { gob.Register(new(spDBusOp)) } func init() { gob.Register(new(spDBusOp)) }

View File

@ -6,12 +6,12 @@ import (
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/helper"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl"
"hakurei.app/internal/dbus"
"hakurei.app/internal/helper"
"hakurei.app/internal/system"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system"
"hakurei.app/system/acl"
"hakurei.app/system/dbus"
) )
func TestSpDBusOp(t *testing.T) { func TestSpDBusOp(t *testing.T) {

View File

@ -11,8 +11,8 @@ import (
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system"
"hakurei.app/internal/system" "hakurei.app/system/acl"
) )
func TestSpPulseOp(t *testing.T) { func TestSpPulseOp(t *testing.T) {

View File

@ -7,8 +7,8 @@ import (
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system"
"hakurei.app/internal/system" "hakurei.app/system/acl"
) )
const ( const (

View File

@ -8,8 +8,8 @@ import (
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system"
"hakurei.app/internal/system" "hakurei.app/system/acl"
) )
func TestSpRuntimeOp(t *testing.T) { func TestSpRuntimeOp(t *testing.T) {

View File

@ -7,8 +7,8 @@ import (
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system"
"hakurei.app/internal/system" "hakurei.app/system/acl"
) )
func init() { gob.Register(spTmpdirOp{}) } func init() { gob.Register(spTmpdirOp{}) }

View File

@ -8,8 +8,8 @@ import (
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system"
"hakurei.app/internal/system" "hakurei.app/system/acl"
) )
func TestSpTmpdirOp(t *testing.T) { func TestSpTmpdirOp(t *testing.T) {

View File

@ -5,8 +5,8 @@ import (
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system/acl"
"hakurei.app/internal/wayland" "hakurei.app/system/wayland"
) )
func init() { gob.Register(new(spWaylandOp)) } func init() { gob.Register(new(spWaylandOp)) }
@ -25,8 +25,8 @@ func (s *spWaylandOp) toSystem(state *outcomeStateSys) error {
// outer wayland socket (usually `/run/user/%d/wayland-%d`) // outer wayland socket (usually `/run/user/%d/wayland-%d`)
var socketPath *check.Absolute var socketPath *check.Absolute
if name, ok := state.k.lookupEnv(wayland.Display); !ok { if name, ok := state.k.lookupEnv(wayland.WaylandDisplay); !ok {
state.msg.Verbose(wayland.Display + " is not set, assuming " + wayland.FallbackName) state.msg.Verbose(wayland.WaylandDisplay + " is not set, assuming " + wayland.FallbackName)
socketPath = state.sc.RuntimePath.Append(wayland.FallbackName) socketPath = state.sc.RuntimePath.Append(wayland.FallbackName)
} else if a, err := check.NewAbs(name); err != nil { } else if a, err := check.NewAbs(name); err != nil {
socketPath = state.sc.RuntimePath.Append(name) socketPath = state.sc.RuntimePath.Append(name)
@ -53,7 +53,7 @@ func (s *spWaylandOp) toSystem(state *outcomeStateSys) error {
func (s *spWaylandOp) toContainer(state *outcomeStateParams) error { func (s *spWaylandOp) toContainer(state *outcomeStateParams) error {
innerPath := state.runtimeDir.Append(wayland.FallbackName) innerPath := state.runtimeDir.Append(wayland.FallbackName)
state.env[wayland.Display] = wayland.FallbackName state.env[wayland.WaylandDisplay] = wayland.FallbackName
if s.SocketPath == nil { if s.SocketPath == nil {
state.params.Bind(state.instancePath().Append("wayland"), innerPath, 0) state.params.Bind(state.instancePath().Append("wayland"), innerPath, 0)
} else { } else {

View File

@ -6,9 +6,9 @@ import (
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system"
"hakurei.app/internal/system" "hakurei.app/system/acl"
"hakurei.app/internal/wayland" "hakurei.app/system/wayland"
) )
func TestSpWaylandOp(t *testing.T) { func TestSpWaylandOp(t *testing.T) {
@ -47,7 +47,7 @@ func TestSpWaylandOp(t *testing.T) {
Ops: new(container.Ops). Ops: new(container.Ops).
Bind(m(wantInstancePrefix+"/wayland"), m("/run/user/1000/wayland-0"), 0), Bind(m(wantInstancePrefix+"/wayland"), m("/run/user/1000/wayland-0"), 0),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
wayland.Display: wayland.FallbackName, wayland.WaylandDisplay: wayland.FallbackName,
}, nil), nil}, }, nil), nil},
{"success direct", func(isShim, _ bool) outcomeOp { {"success direct", func(isShim, _ bool) outcomeOp {
@ -75,7 +75,7 @@ func TestSpWaylandOp(t *testing.T) {
Ops: new(container.Ops). Ops: new(container.Ops).
Bind(m("/proc/nonexistent/wayland"), m("/run/user/1000/wayland-0"), 0), Bind(m("/proc/nonexistent/wayland"), m("/run/user/1000/wayland-0"), 0),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
wayland.Display: wayland.FallbackName, wayland.WaylandDisplay: wayland.FallbackName,
}, nil), nil}, }, nil), nil},
{"success", func(bool, bool) outcomeOp { {"success", func(bool, bool) outcomeOp {
@ -98,7 +98,7 @@ func TestSpWaylandOp(t *testing.T) {
Ops: new(container.Ops). Ops: new(container.Ops).
Bind(m(wantInstancePrefix+"/wayland"), m("/run/user/1000/wayland-0"), 0), Bind(m(wantInstancePrefix+"/wayland"), m("/run/user/1000/wayland-0"), 0),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
wayland.Display: wayland.FallbackName, wayland.WaylandDisplay: wayland.FallbackName,
}, nil), nil}, }, nil), nil},
}) })
} }

View File

@ -11,7 +11,7 @@ import (
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system/acl"
) )
var absX11SocketDir = fhs.AbsTmp.Append(".X11-unix") var absX11SocketDir = fhs.AbsTmp.Append(".X11-unix")

View File

@ -7,7 +7,7 @@ import (
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system/acl"
) )
func TestSpX11Op(t *testing.T) { func TestSpX11Op(t *testing.T) {

View File

@ -1,4 +1,4 @@
package info package internal
import ( import (
"log" "log"

View File

@ -1,4 +1,4 @@
package info package internal
import ( import (
"reflect" "reflect"

View File

@ -27,7 +27,7 @@ func TestEntryData(t *testing.T) {
return buf.String() return buf.String()
} }
} }
templateStateGob := mustEncodeGob(NewTemplateState()) templateStateGob := mustEncodeGob(newTemplateState())
testCases := []struct { testCases := []struct {
name string name string
@ -45,11 +45,11 @@ func TestEntryData(t *testing.T) {
Step: "validate configuration", Err: hst.ErrConfigNull, Step: "validate configuration", Err: hst.ErrConfigNull,
Msg: "invalid configuration"}}, Msg: "invalid configuration"}},
{"inconsistent enablement", "\x00\xff\xca\xfe\x00\x00\xff\x00" + templateStateGob, NewTemplateState(), &hst.AppError{ {"inconsistent enablement", "\x00\xff\xca\xfe\x00\x00\xff\x00" + templateStateGob, newTemplateState(), &hst.AppError{
Step: "validate state enablement", Err: os.ErrInvalid, Step: "validate state enablement", Err: os.ErrInvalid,
Msg: "state entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has unexpected enablement byte 0xd, 0xff"}}, Msg: "state entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has unexpected enablement byte 0xd, 0xff"}},
{"template", "\x00\xff\xca\xfe\x00\x00\x0d\xf2" + templateStateGob, NewTemplateState(), nil}, {"template", "\x00\xff\xca\xfe\x00\x00\x0d\xf2" + templateStateGob, newTemplateState(), nil},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@ -105,7 +105,7 @@ func TestEntryData(t *testing.T) {
t.Run("encode fault", func(t *testing.T) { t.Run("encode fault", func(t *testing.T) {
t.Parallel() t.Parallel()
s := NewTemplateState() s := newTemplateState()
t.Run("gob", func(t *testing.T) { t.Run("gob", func(t *testing.T) {
var want = &hst.AppError{Step: "encode state body", Err: stub.UniqueError(0xcafe)} var want = &hst.AppError{Step: "encode state body", Err: stub.UniqueError(0xcafe)}
@ -123,8 +123,8 @@ func TestEntryData(t *testing.T) {
}) })
} }
// NewTemplateState returns the address of a new template [hst.State] struct. // newTemplateState returns the address of a new template [hst.State] struct.
func NewTemplateState() *hst.State { func newTemplateState() *hst.State {
return &hst.State{ return &hst.State{
ID: hst.ID(bytes.Repeat([]byte{0xaa}, len(hst.ID{}))), ID: hst.ID(bytes.Repeat([]byte{0xaa}, len(hst.ID{}))),
PID: 0xcafe, PID: 0xcafe,

View File

@ -10,7 +10,7 @@ import (
"strings" "strings"
"syscall" "syscall"
"testing" "testing"
_ "unsafe" // for go:linkname _ "unsafe"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
@ -18,23 +18,18 @@ import (
"hakurei.app/internal/store" "hakurei.app/internal/store"
) )
// Made available here for direct validation of state entry files. //go:linkname newTemplateState hakurei.app/internal/store.newTemplateState
// func newTemplateState() *hst.State
//go:linkname entryDecode hakurei.app/internal/store.entryDecode //go:linkname entryDecode hakurei.app/internal/store.entryDecode
func entryDecode(r io.Reader, p *hst.State) (hst.Enablement, error) func entryDecode(r io.Reader, p *hst.State) (hst.Enablement, error)
// Made available here for direct access to known segment handles.
//
//go:linkname newHandle hakurei.app/internal/store.newHandle //go:linkname newHandle hakurei.app/internal/store.newHandle
func newHandle(base *check.Absolute, identity int) *store.Handle func newHandle(base *check.Absolute, identity int) *store.Handle
// Made available here to check open error handling behaviour.
//
//go:linkname open hakurei.app/internal/store.(*EntryHandle).open //go:linkname open hakurei.app/internal/store.(*EntryHandle).open
func open(eh *store.EntryHandle, flag int, perm os.FileMode) (*os.File, error) func open(eh *store.EntryHandle, flag int, perm os.FileMode) (*os.File, error)
// Made available here to check the saveload cycle.
//
//go:linkname save hakurei.app/internal/store.(*EntryHandle).save //go:linkname save hakurei.app/internal/store.(*EntryHandle).save
func save(eh *store.EntryHandle, state *hst.State) error func save(eh *store.EntryHandle, state *hst.State) error
@ -96,9 +91,9 @@ func TestStateEntryHandle(t *testing.T) {
t.Run("saveload", func(t *testing.T) { t.Run("saveload", func(t *testing.T) {
t.Parallel() t.Parallel()
eh := store.EntryHandle{Pathname: check.MustAbs(t.TempDir()).Append("entry"), eh := store.EntryHandle{Pathname: check.MustAbs(t.TempDir()).Append("entry"),
ID: store.NewTemplateState().ID} ID: newTemplateState().ID}
if err := save(&eh, store.NewTemplateState()); err != nil { if err := save(&eh, newTemplateState()); err != nil {
t.Fatalf("save: error = %v", err) t.Fatalf("save: error = %v", err)
} }
@ -117,7 +112,7 @@ func TestStateEntryHandle(t *testing.T) {
t.Fatal(f.Close()) t.Fatal(f.Close())
} }
if want := store.NewTemplateState(); !reflect.DeepEqual(&got, want) { if want := newTemplateState(); !reflect.DeepEqual(&got, want) {
t.Errorf("entryDecode: %#v, want %#v", &got, want) t.Errorf("entryDecode: %#v, want %#v", &got, want)
} }
}) })
@ -127,7 +122,7 @@ func TestStateEntryHandle(t *testing.T) {
if et, err := eh.Load(nil); err != nil { if et, err := eh.Load(nil); err != nil {
t.Fatalf("load: error = %v", err) t.Fatalf("load: error = %v", err)
} else if want := store.NewTemplateState().Enablements.Unwrap(); et != want { } else if want := newTemplateState().Enablements.Unwrap(); et != want {
t.Errorf("load: et = %x, want %x", et, want) t.Errorf("load: et = %x, want %x", et, want)
} }
}) })
@ -138,7 +133,7 @@ func TestStateEntryHandle(t *testing.T) {
var got hst.State var got hst.State
if _, err := eh.Load(&got); err != nil { if _, err := eh.Load(&got); err != nil {
t.Fatalf("load: error = %v", err) t.Fatalf("load: error = %v", err)
} else if want := store.NewTemplateState(); !reflect.DeepEqual(&got, want) { } else if want := newTemplateState(); !reflect.DeepEqual(&got, want) {
t.Errorf("load: %#v, want %#v", &got, want) t.Errorf("load: %#v, want %#v", &got, want)
} }
}) })

View File

@ -12,15 +12,13 @@ import (
"syscall" "syscall"
"testing" "testing"
"time" "time"
_ "unsafe" // for go:linkname _ "unsafe"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/store" "hakurei.app/internal/store"
) )
// Made available here to check bigLock error handling behaviour.
//
//go:linkname bigLock hakurei.app/internal/store.(*Store).bigLock //go:linkname bigLock hakurei.app/internal/store.(*Store).bigLock
func bigLock(s *store.Store) (unlock func(), err error) func bigLock(s *store.Store) (unlock func(), err error)

View File

@ -1,81 +0,0 @@
package system
import (
"errors"
"fmt"
"os"
"hakurei.app/container/check"
"hakurei.app/hst"
"hakurei.app/internal/acl"
"hakurei.app/internal/wayland"
)
// Wayland maintains a wayland socket with security-context-v1 attached via [wayland].
// The socket stops accepting connections once the pipe referred to by sync is closed.
// The socket is pathname only and is destroyed on revert.
func (sys *I) Wayland(dst, src *check.Absolute, appID, instanceID string) *I {
sys.ops = append(sys.ops, &waylandOp{nil,
dst, src, appID, instanceID})
return sys
}
// waylandOp implements [I.Wayland].
type waylandOp struct {
ctx *wayland.SecurityContext
dst, src *check.Absolute
appID, instanceID string
}
func (w *waylandOp) Type() hst.Enablement { return Process }
func (w *waylandOp) apply(sys *I) (err error) {
if w.ctx, err = sys.waylandNew(w.src, w.dst, w.appID, w.instanceID); err != nil {
return newOpError("wayland", err, false)
} else {
sys.msg.Verbosef("wayland pathname socket on %q via %q", w.dst, w.src)
if err = sys.chmod(w.dst.String(), 0); err != nil {
if closeErr := w.ctx.Close(); closeErr != nil {
return newOpError("wayland", errors.Join(err, closeErr), false)
}
return newOpError("wayland", err, false)
}
if err = sys.aclUpdate(w.dst.String(), sys.uid, acl.Read, acl.Write, acl.Execute); err != nil {
if closeErr := w.ctx.Close(); closeErr != nil {
return newOpError("wayland", errors.Join(err, closeErr), false)
}
return newOpError("wayland", err, false)
}
return nil
}
}
func (w *waylandOp) revert(sys *I, _ *Criteria) error {
var (
hangupErr error
removeErr error
)
sys.msg.Verbosef("hanging up wayland socket on %q", w.dst)
if w.ctx != nil {
hangupErr = w.ctx.Close()
}
if err := sys.remove(w.dst.String()); err != nil && !errors.Is(err, os.ErrNotExist) {
removeErr = err
}
return newOpError("wayland", errors.Join(hangupErr, removeErr), true)
}
func (w *waylandOp) Is(o Op) bool {
target, ok := o.(*waylandOp)
return ok && w != nil && target != nil &&
w.dst.Is(target.dst) && w.src.Is(target.src) &&
w.appID == target.appID && w.instanceID == target.instanceID
}
func (w *waylandOp) Path() string { return w.dst.String() }
func (w *waylandOp) String() string { return fmt.Sprintf("wayland socket at %q", w.dst) }

View File

@ -1,157 +0,0 @@
package system
import (
"errors"
"os"
"testing"
"hakurei.app/container/stub"
"hakurei.app/internal/acl"
)
func TestWaylandOp(t *testing.T) {
t.Parallel()
checkOpBehaviour(t, []opBehaviourTestCase{
{"chmod", 0xbeef, 0xff, &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, []stub.Call{
call("waylandNew", stub.ExpectArgs{m("/run/user/1971/wayland-0"), m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"}, nil, nil),
call("verbosef", stub.ExpectArgs{"wayland pathname socket on %q via %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0")}}, nil, nil),
call("chmod", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", os.FileMode(0)}, nil, stub.UniqueError(3)),
}, &OpError{Op: "wayland", Err: errors.Join(stub.UniqueError(3), os.ErrInvalid)}, nil, nil},
{"aclUpdate", 0xbeef, 0xff, &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, []stub.Call{
call("waylandNew", stub.ExpectArgs{m("/run/user/1971/wayland-0"), m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"}, nil, nil),
call("verbosef", stub.ExpectArgs{"wayland pathname socket on %q via %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0")}}, nil, nil),
call("chmod", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", os.FileMode(0)}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", 0xbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, stub.UniqueError(2)),
}, &OpError{Op: "wayland", Err: errors.Join(stub.UniqueError(2), os.ErrInvalid)}, nil, nil},
{"remove", 0xbeef, 0xff, &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, []stub.Call{
call("waylandNew", stub.ExpectArgs{m("/run/user/1971/wayland-0"), m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"}, nil, nil),
call("verbosef", stub.ExpectArgs{"wayland pathname socket on %q via %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0")}}, nil, nil),
call("chmod", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", os.FileMode(0)}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", 0xbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil),
}, nil, []stub.Call{
call("verbosef", stub.ExpectArgs{"hanging up wayland socket on %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland")}}, nil, nil),
call("remove", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"}, nil, stub.UniqueError(1)),
}, &OpError{Op: "wayland", Err: errors.Join(stub.UniqueError(1)), Revert: true}},
{"success", 0xbeef, 0xff, &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, []stub.Call{
call("waylandNew", stub.ExpectArgs{m("/run/user/1971/wayland-0"), m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"}, nil, nil),
call("verbosef", stub.ExpectArgs{"wayland pathname socket on %q via %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0")}}, nil, nil),
call("chmod", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", os.FileMode(0)}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", 0xbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil),
}, nil, []stub.Call{
call("verbosef", stub.ExpectArgs{"hanging up wayland socket on %q", []any{m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland")}}, nil, nil),
call("remove", stub.ExpectArgs{"/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"}, nil, nil),
}, nil},
})
checkOpsBuilder(t, "Wayland", []opsBuilderTestCase{
{"chromium", 0xcafe, func(_ *testing.T, sys *I) {
sys.Wayland(
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
)
}, []Op{&waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}}, stub.Expect{}},
})
checkOpIs(t, []opIsTestCase{
{"dst differs", &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7d/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, false},
{"src differs", &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-1"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, false},
{"appID differs", &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium",
"ebf083d1b175911782d413369b64ce7c",
}, &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, false},
{"instanceID differs", &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7d",
}, &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, false},
{"equals", &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, true},
})
checkOpMeta(t, []opMetaTestCase{
{"chromium", &waylandOp{nil,
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"ebf083d1b175911782d413369b64ce7c",
}, Process, "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland",
`wayland socket at "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"`},
})
}

View File

@ -1,4 +1,4 @@
package info package internal
// FallbackVersion is returned when a version string was not set by the linker. // FallbackVersion is returned when a version string was not set by the linker.
const FallbackVersion = "dirty" const FallbackVersion = "dirty"

View File

@ -1,94 +0,0 @@
package wayland
import (
"errors"
"os"
"syscall"
"hakurei.app/container/check"
)
// SecurityContext holds resources associated with a Wayland security_context.
type SecurityContext struct {
// Pipe with its write end passed to security-context-v1.
closeFds [2]int
}
// Close releases any resources held by [SecurityContext], and prevents further
// connections to its associated socket.
func (sc *SecurityContext) Close() error {
if sc == nil {
return os.ErrInvalid
}
return errors.Join(
syscall.Close(sc.closeFds[1]),
syscall.Close(sc.closeFds[0]),
)
}
// New creates a new security context on the Wayland display at displayPath
// and associates it with a new socket bound to bindPath.
//
// New does not attach a finalizer to the resulting [SecurityContext] struct.
// The caller is responsible for calling [SecurityContext.Close].
//
// A non-nil error unwraps to concrete type [Error].
func New(displayPath, bindPath *check.Absolute, appID, instanceID string) (*SecurityContext, error) {
// ensure bindPath is available
if f, err := os.Create(bindPath.String()); err != nil {
return nil, &Error{RCreate, bindPath.String(), displayPath.String(), err}
} else if err = f.Close(); err != nil {
return nil, &Error{RCreate, bindPath.String(), displayPath.String(), err}
} else if err = os.Remove(bindPath.String()); err != nil {
return nil, &Error{RCreate, bindPath.String(), displayPath.String(), err}
}
if fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0); err != nil {
return nil, &Error{RHostSocket, bindPath.String(), displayPath.String(), err}
} else if err = syscall.Connect(fd, &syscall.SockaddrUnix{Name: displayPath.String()}); err != nil {
_ = syscall.Close(fd)
return nil, &Error{RHostConnect, bindPath.String(), displayPath.String(), err}
} else {
closeFds, bindErr := securityContextBindPipe(fd, bindPath, appID, instanceID)
if bindErr != nil {
// do not leak the pipe and socket
err = errors.Join(bindErr, // already wrapped
syscall.Close(closeFds[1]),
syscall.Close(closeFds[0]),
syscall.Close(fd),
)
}
return &SecurityContext{closeFds}, err
}
}
// securityContextBindPipe binds a socket associated to a security context created on serverFd,
// returning the pipe file descriptors used for security-context-v1 close_fd.
//
// A non-nil error unwraps to concrete type [Error].
func securityContextBindPipe(
serverFd int,
bindPath *check.Absolute,
appID, instanceID string,
) ([2]int, error) {
// write end passed to security-context-v1 close_fd
var closeFds [2]int
if err := syscall.Pipe2(closeFds[0:], syscall.O_CLOEXEC); err != nil {
return closeFds, err
}
// returned error is already wrapped
if err := securityContextBind(
bindPath.String(),
serverFd,
appID, instanceID,
closeFds[1],
); err != nil {
return closeFds, errors.Join(err,
syscall.Close(closeFds[1]),
syscall.Close(closeFds[0]),
)
} else {
return closeFds, nil
}
}

View File

@ -1,54 +0,0 @@
package wayland
import (
"errors"
"os"
"reflect"
"syscall"
"testing"
"hakurei.app/container/check"
)
func TestSecurityContextClose(t *testing.T) {
t.Parallel()
if err := (*SecurityContext)(nil).Close(); !reflect.DeepEqual(err, os.ErrInvalid) {
t.Fatalf("Close: error = %v", err)
}
var ctx SecurityContext
if err := syscall.Pipe2(ctx.closeFds[0:], syscall.O_CLOEXEC); err != nil {
t.Fatalf("Pipe: error = %v", err)
}
t.Cleanup(func() { _ = syscall.Close(ctx.closeFds[0]); _ = syscall.Close(ctx.closeFds[1]) })
if err := ctx.Close(); err != nil {
t.Fatalf("Close: error = %v", err)
}
wantErr := errors.Join(syscall.EBADF, syscall.EBADF)
if err := ctx.Close(); !reflect.DeepEqual(err, wantErr) {
t.Fatalf("Close: error = %#v, want %#v", err, wantErr)
}
}
func TestNewEnsure(t *testing.T) {
existingDirPath := check.MustAbs(t.TempDir()).Append("dir")
if err := os.MkdirAll(existingDirPath.String(), 0700); err != nil {
t.Fatal(err)
}
nonexistent := check.MustAbs("/proc/nonexistent")
wantErr := &Error{RCreate, existingDirPath.String(), nonexistent.String(), &os.PathError{
Op: "open",
Path: existingDirPath.String(),
Err: syscall.EISDIR,
}}
if _, err := New(
nonexistent,
existingDirPath, "", "",
); !reflect.DeepEqual(err, wantErr) {
t.Fatalf("New: error = %#v, want %#v", err, wantErr)
}
}

View File

@ -1,117 +0,0 @@
#include "wayland-client-helper.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "security-context-v1-protocol.h"
#include <wayland-client.h>
static void registry_handle_global(
void *data,
struct wl_registry *registry,
uint32_t name,
const char *interface,
uint32_t version) {
struct wp_security_context_manager_v1 **out = data;
if (strcmp(interface, wp_security_context_manager_v1_interface.name) == 0)
*out = wl_registry_bind(registry, name, &wp_security_context_manager_v1_interface, 1);
}
static void registry_handle_global_remove(
void *data,
struct wl_registry *registry,
uint32_t name) {} /* no-op */
static const struct wl_registry_listener registry_listener = {
.global = registry_handle_global,
.global_remove = registry_handle_global_remove,
};
hakurei_wayland_res hakurei_security_context_bind(
char *socket_path,
int server_fd,
const char *app_id,
const char *instance_id,
int close_fd) {
hakurei_wayland_res res = HAKUREI_WAYLAND_SUCCESS; /* see wayland.go for handling */
struct wl_display *display = NULL;
struct wl_registry *registry;
struct wp_security_context_manager_v1 *security_context_manager = NULL;
int event_cnt;
int listen_fd = -1;
struct sockaddr_un sockaddr = {0};
struct wp_security_context_v1 *security_context;
display = wl_display_connect_to_fd(server_fd);
if (display == NULL) {
res = HAKUREI_WAYLAND_CONNECT;
goto out;
};
registry = wl_display_get_registry(display);
if (wl_registry_add_listener(registry, &registry_listener, &security_context_manager) < 0) {
res = HAKUREI_WAYLAND_LISTENER;
goto out;
}
event_cnt = wl_display_roundtrip(display);
wl_registry_destroy(registry);
if (event_cnt < 0) {
res = HAKUREI_WAYLAND_ROUNDTRIP;
goto out;
}
if (security_context_manager == NULL) {
res = HAKUREI_WAYLAND_NOT_AVAIL;
goto out;
}
listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (listen_fd < 0) {
res = HAKUREI_WAYLAND_SOCKET;
goto out;
}
sockaddr.sun_family = AF_UNIX;
snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", socket_path);
if (bind(listen_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) {
res = HAKUREI_WAYLAND_BIND;
goto out;
}
if (listen(listen_fd, 0) != 0) {
res = HAKUREI_WAYLAND_LISTEN;
goto out;
}
security_context = wp_security_context_manager_v1_create_listener(security_context_manager, listen_fd, close_fd);
if (security_context == NULL) { /* not reached */
res = HAKUREI_WAYLAND_NOT_AVAIL;
goto out;
}
wp_security_context_v1_set_sandbox_engine(security_context, "app.hakurei");
wp_security_context_v1_set_app_id(security_context, app_id);
wp_security_context_v1_set_instance_id(security_context, instance_id);
wp_security_context_v1_commit(security_context);
wp_security_context_v1_destroy(security_context);
if (wl_display_roundtrip(display) < 0) {
res = HAKUREI_WAYLAND_ROUNDTRIP;
goto out;
}
out:
if (listen_fd >= 0)
close(listen_fd);
if (security_context_manager != NULL)
wp_security_context_manager_v1_destroy(security_context_manager);
if (display != NULL)
wl_display_disconnect(display);
free((void *)socket_path);
free((void *)app_id);
free((void *)instance_id);
return res;
}

View File

@ -1,40 +0,0 @@
#include <stdbool.h>
#include <sys/un.h>
typedef enum {
HAKUREI_WAYLAND_SUCCESS,
/* wl_display_connect_to_fd failed, errno */
HAKUREI_WAYLAND_CONNECT,
/* wl_registry_add_listener failed, errno */
HAKUREI_WAYLAND_LISTENER,
/* wl_display_roundtrip failed, errno */
HAKUREI_WAYLAND_ROUNDTRIP,
/* compositor does not implement wp_security_context_v1 */
HAKUREI_WAYLAND_NOT_AVAIL,
/* socket failed, errno */
HAKUREI_WAYLAND_SOCKET,
/* bind failed, errno */
HAKUREI_WAYLAND_BIND,
/* listen failed, errno */
HAKUREI_WAYLAND_LISTEN,
/* ensure pathname failed, implemented in conn.go */
HAKUREI_WAYLAND_CREAT,
/* socket for host server failed, implemented in conn.go */
HAKUREI_WAYLAND_HOST_SOCKET,
/* connect for host server failed, implemented in conn.go */
HAKUREI_WAYLAND_HOST_CONNECT,
} hakurei_wayland_res;
hakurei_wayland_res hakurei_security_context_bind(
char *socket_path,
int server_fd,
const char *app_id,
const char *instance_id,
int close_fd);
/* returns whether the specified size fits in the sun_path field of sockaddr_un */
static inline bool hakurei_is_valid_size_sun_path(size_t sz) {
struct sockaddr_un sockaddr;
return sz <= sizeof(sockaddr.sun_path);
};

View File

@ -1,161 +0,0 @@
// Package wayland implements Wayland security_context_v1 protocol.
package wayland
//go:generate sh -c "wayland-scanner client-header `pkg-config --variable=datarootdir wayland-protocols`/wayland-protocols/staging/security-context/security-context-v1.xml security-context-v1-protocol.h"
//go:generate sh -c "wayland-scanner private-code `pkg-config --variable=datarootdir wayland-protocols`/wayland-protocols/staging/security-context/security-context-v1.xml security-context-v1-protocol.c"
/*
#cgo linux pkg-config: --static wayland-client
#cgo freebsd openbsd LDFLAGS: -lwayland-client
#include "wayland-client-helper.h"
*/
import "C"
import (
"errors"
"strings"
)
const (
// Display contains the name of the server socket
// (https://gitlab.freedesktop.org/wayland/wayland/-/blob/1.23.1/src/wayland-client.c#L1147)
// which is concatenated with XDG_RUNTIME_DIR
// (https://gitlab.freedesktop.org/wayland/wayland/-/blob/1.23.1/src/wayland-client.c#L1171)
// or used as-is if absolute
// (https://gitlab.freedesktop.org/wayland/wayland/-/blob/1.23.1/src/wayland-client.c#L1176).
Display = "WAYLAND_DISPLAY"
// FallbackName is used as the wayland socket name if WAYLAND_DISPLAY is unset
// (https://gitlab.freedesktop.org/wayland/wayland/-/blob/1.23.1/src/wayland-client.c#L1149).
FallbackName = "wayland-0"
)
type (
// Res is the outcome of a call to [New].
Res = C.hakurei_wayland_res
// An Error represents a failure during [New].
Error struct {
// Where the failure occurred.
Cause Res
// Attempted pathname socket.
Path string
// Pathname socket to host server. Omitted for libwayland errors.
Host string
// Global errno value set during the fault.
Errno error
}
)
// withPrefix returns prefix suffixed with errno description if available.
func (e *Error) withPrefix(prefix string) string {
if e.Errno == nil {
return prefix
}
return prefix + ": " + e.Errno.Error()
}
const (
// RSuccess is returned on a successful call.
RSuccess Res = C.HAKUREI_WAYLAND_SUCCESS
// RConnect is returned if wl_display_connect_to_fd failed. The global errno is set.
RConnect Res = C.HAKUREI_WAYLAND_CONNECT
// RListener is returned if wl_registry_add_listener failed. The global errno is set.
RListener Res = C.HAKUREI_WAYLAND_LISTENER
// RRoundtrip is returned if wl_display_roundtrip failed. The global errno is set.
RRoundtrip Res = C.HAKUREI_WAYLAND_ROUNDTRIP
// RNotAvail is returned if compositor does not implement wp_security_context_v1.
RNotAvail Res = C.HAKUREI_WAYLAND_NOT_AVAIL
// RSocket is returned if socket failed. The global errno is set.
RSocket Res = C.HAKUREI_WAYLAND_SOCKET
// RBind is returned if bind failed. The global errno is set.
RBind Res = C.HAKUREI_WAYLAND_BIND
// RListen is returned if listen failed. The global errno is set.
RListen Res = C.HAKUREI_WAYLAND_LISTEN
// RCreate is returned if ensuring pathname availability failed. Returned by [New].
RCreate Res = C.HAKUREI_WAYLAND_CREAT
// RHostSocket is returned if socket failed for host server. Returned by [New].
RHostSocket Res = C.HAKUREI_WAYLAND_HOST_SOCKET
// RHostConnect is returned if connect failed for host server. Returned by [New].
RHostConnect Res = C.HAKUREI_WAYLAND_HOST_CONNECT
)
func (e *Error) Unwrap() error { return e.Errno }
func (e *Error) Message() string { return e.Error() }
func (e *Error) Error() string {
switch e.Cause {
case RSuccess:
if e.Errno == nil {
return "success"
}
return e.Errno.Error()
case RConnect:
return e.withPrefix("wl_display_connect_to_fd failed")
case RListener:
return e.withPrefix("wl_registry_add_listener failed")
case RRoundtrip:
return e.withPrefix("wl_display_roundtrip failed")
case RNotAvail:
return "compositor does not implement security_context_v1"
case RSocket:
if e.Errno == nil {
return "socket operation failed"
}
return "socket: " + e.Errno.Error()
case RBind:
return e.withPrefix("cannot bind " + e.Path)
case RListen:
return e.withPrefix("cannot listen on " + e.Path)
case RCreate:
if e.Errno == nil {
return "cannot ensure wayland pathname socket"
}
return e.Errno.Error()
case RHostSocket:
return e.withPrefix("socket")
case RHostConnect:
return e.withPrefix("cannot connect to " + e.Host)
default:
return e.withPrefix("impossible outcome") /* not reached */
}
}
// securityContextBind calls hakurei_security_context_bind.
//
// A non-nil error has concrete type [Error].
func securityContextBind(
socketPath string,
serverFd int,
appID, instanceID string,
closeFd int,
) error {
if hasNull(socketPath) || hasNull(appID) || hasNull(instanceID) {
return &Error{Cause: RBind, Path: socketPath, Errno: errors.New("argument contains NUL character")}
}
if !C.hakurei_is_valid_size_sun_path(C.size_t(len(socketPath))) {
return &Error{Cause: RBind, Path: socketPath, Errno: errors.New("socket pathname too long")}
}
var e Error
e.Cause, e.Errno = C.hakurei_security_context_bind(
C.CString(socketPath),
C.int(serverFd),
C.CString(appID),
C.CString(instanceID),
C.int(closeFd),
)
if e.Cause == RSuccess {
return nil
}
e.Path = socketPath
return &e
}
// hasNull returns whether s contains the NUL character.
func hasNull(s string) bool { return strings.IndexByte(s, 0) > -1 }

View File

@ -1,137 +0,0 @@
package wayland
import (
"errors"
"os"
"reflect"
"syscall"
"testing"
"hakurei.app/container/stub"
)
func TestError(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
err Error
want string
}{
{"success", Error{
Cause: RSuccess,
}, "success"},
{"success errno", Error{
Cause: RSuccess,
Errno: stub.UniqueError(0),
}, "unique error 0 injected by the test suite"},
{"wl_display_connect_to_fd", Error{
Cause: RConnect,
Errno: stub.UniqueError(1),
}, "wl_display_connect_to_fd failed: unique error 1 injected by the test suite"},
{"wl_registry_add_listener", Error{
Cause: RListener,
Errno: stub.UniqueError(2),
}, "wl_registry_add_listener failed: unique error 2 injected by the test suite"},
{"wl_display_roundtrip", Error{
Cause: RRoundtrip,
Errno: stub.UniqueError(3),
}, "wl_display_roundtrip failed: unique error 3 injected by the test suite"},
{"not available", Error{
Cause: RNotAvail,
}, "compositor does not implement security_context_v1"},
{"not available errno", Error{
Cause: RNotAvail,
Errno: syscall.EAGAIN,
}, "compositor does not implement security_context_v1"},
{"socket", Error{
Cause: RSocket,
Errno: stub.UniqueError(4),
}, "socket: unique error 4 injected by the test suite"},
{"bind", Error{
Cause: RBind,
Path: "/hakurei.0/18783d07791f2460dbbcffb76c24c9e6/wayland",
Errno: stub.UniqueError(5),
}, "cannot bind /hakurei.0/18783d07791f2460dbbcffb76c24c9e6/wayland: unique error 5 injected by the test suite"},
{"listen", Error{
Cause: RListen,
Path: "/hakurei.0/18783d07791f2460dbbcffb76c24c9e6/wayland",
Errno: stub.UniqueError(6),
}, "cannot listen on /hakurei.0/18783d07791f2460dbbcffb76c24c9e6/wayland: unique error 6 injected by the test suite"},
{"socket invalid", Error{
Cause: RSocket,
}, "socket operation failed"},
{"create", Error{
Cause: RCreate,
}, "cannot ensure wayland pathname socket"},
{"create path", Error{
Cause: RCreate,
Errno: &os.PathError{Op: "create", Path: "/proc/nonexistent", Err: syscall.EEXIST},
}, "create /proc/nonexistent: file exists"},
{"host socket", Error{
Cause: RHostSocket,
Errno: stub.UniqueError(7),
}, "socket: unique error 7 injected by the test suite"},
{"host connect", Error{
Cause: RHostConnect,
Host: "/run/user/1971/wayland-1",
Errno: stub.UniqueError(8),
}, "cannot connect to /run/user/1971/wayland-1: unique error 8 injected by the test suite"},
{"invalid", Error{
Cause: 0xbad,
}, "impossible outcome"},
{"invalid errno", Error{
Cause: 0xbad,
Errno: stub.UniqueError(9),
}, "impossible outcome: unique error 9 injected by the test suite"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := tc.err.Message(); got != tc.want {
t.Errorf("Message: %q, want %q", got, tc.want)
}
})
}
}
func TestSecurityContextBindValidate(t *testing.T) {
t.Parallel()
t.Run("NUL", func(t *testing.T) {
t.Parallel()
want := &Error{Cause: RBind, Path: "\x00", Errno: errors.New("argument contains NUL character")}
if got := securityContextBind("\x00", -1, "\x00", "\x00", -1); !reflect.DeepEqual(got, want) {
t.Fatalf("securityContextBind: error = %#v, want %#v", got, want)
}
})
t.Run("long", func(t *testing.T) {
t.Parallel()
// 256 bytes
const oversizedPath = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
want := &Error{Cause: RBind, Path: oversizedPath, Errno: errors.New("socket pathname too long")}
if got := securityContextBind(oversizedPath, -1, "", "", -1); !reflect.DeepEqual(got, want) {
t.Fatalf("securityContextBind: error = %#v, want %#v", got, want)
}
})
}

27
ldd/error.go Normal file
View File

@ -0,0 +1,27 @@
package ldd
import (
"errors"
"fmt"
)
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", string(e))
}

View File

@ -3,7 +3,6 @@ package ldd
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"io" "io"
"os" "os"
"os/exec" "os/exec"
@ -17,26 +16,17 @@ import (
"hakurei.app/message" "hakurei.app/message"
) )
const ( var (
// msgStaticSuffix is the suffix of message printed to stderr by musl on a statically linked program. msgStatic = []byte("Not a valid dynamic program")
msgStaticSuffix = ": Not a valid dynamic program" msgStaticGlibc = []byte("not a dynamic executable")
// msgStaticGlibc is a substring of the message printed to stderr by glibc on a statically linked program.
msgStaticGlibc = "not a dynamic executable"
// lddName is the file name of ldd(1) passed to exec.LookPath.
lddName = "ldd"
// lddTimeout is the maximum duration ldd(1) is allowed to ran for before it is terminated.
lddTimeout = 4 * time.Second
) )
// Resolve runs ldd(1) in a strict sandbox and connects its stdout to a [Decoder]. func Exec(ctx context.Context, msg message.Msg, p string) ([]*Entry, error) {
// const (
// The returned error has concrete type lddName = "ldd"
// [exec.Error] or [check.AbsoluteError] for fault during lookup of ldd(1), lddTimeout = 4 * time.Second
// [os.SyscallError] for fault creating the stdout pipe, )
// [container.StartError] for fault during either stage of container setup.
// Otherwise, it passes through the return values of [Decoder.Decode].
func Resolve(ctx context.Context, msg message.Msg, pathname *check.Absolute) ([]*Entry, error) {
c, cancel := context.WithTimeout(ctx, lddTimeout) c, cancel := context.WithTimeout(ctx, lddTimeout)
defer cancel() defer cancel()
@ -47,24 +37,18 @@ func Resolve(ctx context.Context, msg message.Msg, pathname *check.Absolute) ([]
return nil, err return nil, err
} }
z := container.NewCommand(c, msg, toolPath, lddName, pathname.String()) z := container.NewCommand(c, msg, toolPath, lddName, p)
z.Hostname = "hakurei-" + lddName z.Hostname = "hakurei-" + lddName
z.SeccompFlags |= seccomp.AllowMultiarch z.SeccompFlags |= seccomp.AllowMultiarch
z.SeccompPresets |= std.PresetStrict z.SeccompPresets |= std.PresetStrict
stderr := new(bytes.Buffer) stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
z.Stdout = stdout
z.Stderr = stderr z.Stderr = stderr
z. z.
Bind(fhs.AbsRoot, fhs.AbsRoot, 0). Bind(fhs.AbsRoot, fhs.AbsRoot, 0).
Proc(fhs.AbsProc). Proc(fhs.AbsProc).
Dev(fhs.AbsDev, false) Dev(fhs.AbsDev, false)
var d *Decoder
if r, err := z.StdoutPipe(); err != nil {
return nil, err
} else {
d = NewDecoder(r)
}
if err := z.Start(); err != nil { if err := z.Start(); err != nil {
return nil, err return nil, err
} }
@ -72,35 +56,15 @@ func Resolve(ctx context.Context, msg message.Msg, pathname *check.Absolute) ([]
if err := z.Serve(); err != nil { if err := z.Serve(); err != nil {
return nil, err return nil, err
} }
entries, decodeErr := d.Decode()
if decodeErr != nil {
// do not cancel on successful decode to avoid racing with ldd(1) termination
cancel()
}
if err := z.Wait(); err != nil { if err := z.Wait(); err != nil {
m := stderr.Bytes() m := stderr.Bytes()
if bytes.Contains(m, []byte(msgStaticSuffix)) || bytes.Contains(m, []byte(msgStaticGlibc)) { if bytes.Contains(m, append([]byte(p+": "), msgStatic...)) ||
bytes.Contains(m, msgStaticGlibc) {
return nil, nil return nil, nil
} }
if decodeErr != nil {
return nil, errors.Join(decodeErr, err)
}
return nil, err return nil, err
} }
return entries, decodeErr
}
// Exec runs ldd(1) in a restrictive [container] and connects it to a [Decoder], returning resulting entries. v := stdout.Bytes()
// return Parse(v)
// Deprecated: this function takes an unchecked pathname string.
// Relative pathnames do not work in the container as working directory information is not sent.
func Exec(ctx context.Context, msg message.Msg, pathname string) ([]*Entry, error) {
if a, err := check.NewAbs(pathname); err != nil {
return nil, err
} else {
return Resolve(ctx, msg, a)
}
} }

View File

@ -1,46 +0,0 @@
package ldd_test
import (
"errors"
"os"
"os/exec"
"testing"
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/ldd"
"hakurei.app/message"
)
func TestExec(t *testing.T) {
t.Parallel()
t.Run("failure", func(t *testing.T) {
t.Parallel()
_, err := ldd.Resolve(t.Context(), nil, check.MustAbs("/proc/nonexistent"))
var exitError *exec.ExitError
if !errors.As(err, &exitError) {
t.Fatalf("Exec: error has incorrect concrete type: %#v", err)
}
const want = 1
if got := exitError.ExitCode(); got != want {
t.Fatalf("Exec: ExitCode = %d, want %d", got, want)
}
})
t.Run("success", func(t *testing.T) {
msg := message.New(nil)
msg.GetLogger().SetPrefix("check: ")
if entries, err := ldd.Resolve(t.Context(), nil, check.MustAbs(container.MustExecutable(msg))); err != nil {
t.Fatalf("Exec: error = %v", err)
} else if testing.Verbose() {
// result cannot be measured here as build information is not known
t.Logf("Exec: %q", entries)
}
})
}
func TestMain(m *testing.M) { container.TryArgv0(nil); os.Exit(m.Run()) }

View File

@ -1,241 +1,66 @@
// Package ldd provides a robust parser for ldd(1) output, and a convenience function // Package ldd retrieves linker information by invoking ldd from glibc or musl and parsing its output.
// for running ldd(1) in a strict sandbox.
//
// Note: despite the additional hardening, great care must be taken when using ldd(1).
// As a general rule, you must never run ldd(1) against a file that you do not wish to
// execute within the same context.
package ldd package ldd
import ( import (
"bufio" "math"
"bytes" "path"
"errors"
"fmt"
"io"
"strconv" "strconv"
"strings" "strings"
"hakurei.app/container/check"
) )
var (
// ErrUnexpectedNewline is returned when encountering an unexpected empty line.
ErrUnexpectedNewline = errors.New("unexpected newline")
// ErrUnexpectedSeparator is returned when encountering an unexpected separator segment.
ErrUnexpectedSeparator = errors.New("unexpected separator")
// ErrBadLocationFormat is returned for an incorrectly formatted [Entry.Location] segment.
ErrBadLocationFormat = errors.New("bad location format")
)
// EntryUnexpectedSegmentsError is returned when encountering
// a line containing unexpected number of segments.
type EntryUnexpectedSegmentsError string
func (e EntryUnexpectedSegmentsError) Error() string {
return fmt.Sprintf("unexpected segments in entry %q", string(e))
}
// An Entry represents one line of ldd(1) output.
type Entry struct { type Entry struct {
// File name of required object. Name string `json:"name,omitempty"`
Name string `json:"name"` Path string `json:"path,omitempty"`
// Absolute pathname of matched object. Only populated for the long variant.
Path *check.Absolute `json:"path,omitempty"`
// Address at which the object is loaded.
Location uint64 `json:"location"` Location uint64 `json:"location"`
} }
const ( func Parse(p []byte) ([]*Entry, error) {
// entrySegmentIndexName is the index of the segment holding [Entry.Name]. payload := strings.Split(strings.TrimSpace(string(p)), "\n")
entrySegmentIndexName = 0 result := make([]*Entry, len(payload))
// entrySegmentIndexPath is the index of the segment holding [Entry.Path],
// present only for a line describing a fully populated [Entry].
entrySegmentIndexPath = 2
// entrySegmentIndexSeparator is the index of the segment containing the magic bytes entrySegmentFullSeparator,
// present only for a line describing a fully populated [Entry].
entrySegmentIndexSeparator = 1
// entrySegmentIndexLocation is the index of the segment holding [Entry.Location]
// for a line describing a fully populated [Entry].
entrySegmentIndexLocation = 3
// entrySegmentIndexLocationShort is the index of the segment holding [Entry.Location]
// for a line describing only [Entry.Name].
entrySegmentIndexLocationShort = 1
// entrySegmentSep is the byte separating segments in an [Entry] line. for i, ent := range payload {
entrySegmentSep = ' ' if len(ent) == 0 {
// entrySegmentFullSeparator is the exact contents of the segment at index entrySegmentIndexSeparator. return nil, ErrUnexpectedNewline
entrySegmentFullSeparator = "=>"
// entrySegmentLocationLengthMin is the minimum possible length of a segment corresponding to [Entry.Location].
entrySegmentLocationLengthMin = 4
// entrySegmentLocationPrefix are magic bytes prefixing a segment corresponding to [Entry.Location].
entrySegmentLocationPrefix = "(0x"
// entrySegmentLocationSuffix is the magic byte suffixing a segment corresponding to [Entry.Location].
entrySegmentLocationSuffix = ')'
)
// decodeLocationSegment decodes and saves the segment corresponding to [Entry.Location].
func (e *Entry) decodeLocationSegment(segment []byte) (err error) {
if len(segment) < entrySegmentLocationLengthMin ||
segment[len(segment)-1] != entrySegmentLocationSuffix ||
string(segment[:len(entrySegmentLocationPrefix)]) != entrySegmentLocationPrefix {
return ErrBadLocationFormat
} }
e.Location, err = strconv.ParseUint(string(segment[3:len(segment)-1]), 16, 64) segment := strings.SplitN(ent, " ", 5)
return
}
// UnmarshalText parses a line of ldd(1) output and saves it to [Entry]. // location index
func (e *Entry) UnmarshalText(data []byte) error { var iL int
var (
segments = bytes.SplitN(data, []byte{entrySegmentSep}, 5)
// segment to pass to decodeLocationSegment
iL int
)
switch len(segments) { switch len(segment) {
case 2: // /lib/ld-musl-x86_64.so.1 (0x7f04d14ef000) case 2: // /lib/ld-musl-x86_64.so.1 (0x7f04d14ef000)
iL = entrySegmentIndexLocationShort iL = 1
e.Name = string(bytes.TrimSpace(segments[entrySegmentIndexName])) result[i] = &Entry{Name: strings.TrimSpace(segment[0])}
case 4: // libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f04d14ef000) case 4: // libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f04d14ef000)
iL = entrySegmentIndexLocation iL = 3
if string(segments[entrySegmentIndexSeparator]) != entrySegmentFullSeparator { if segment[1] != "=>" {
return ErrUnexpectedSeparator return nil, ErrUnexpectedSeparator
} }
if a, err := check.NewAbs(string(segments[entrySegmentIndexPath])); err != nil { if !path.IsAbs(segment[2]) {
return err return nil, ErrPathNotAbsolute
} else { }
e.Path = a result[i] = &Entry{
Name: strings.TrimSpace(segment[0]),
Path: segment[2],
} }
e.Name = string(bytes.TrimSpace(segments[entrySegmentIndexName]))
default: default:
return EntryUnexpectedSegmentsError(data) return nil, EntryUnexpectedSegmentsError(ent)
} }
return e.decodeLocationSegment(segments[iL]) if loc, err := parseLocation(segment[iL]); err != nil {
} return nil, err
// String returns the musl ldd(1) representation of [Entry].
func (e *Entry) String() string {
// nameInvalid is used for a zero-length e.Name
const nameInvalid = "invalid"
var buf strings.Builder
// libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)
l := len(e.Name) + 1
if e.Name == "" {
l += len(nameInvalid)
}
if e.Path != nil {
l += len(entrySegmentFullSeparator) + 1 + len(e.Path.String()) + 1
}
l += len(entrySegmentLocationPrefix) + 1<<4 + 1
buf.Grow(l)
if e.Name != "" {
buf.WriteString(e.Name + " ")
} else { } else {
buf.WriteString(nameInvalid + " ") result[i].Location = loc
} }
if e.Path != nil {
buf.WriteString(entrySegmentFullSeparator + " " + e.Path.String() + " ")
} }
buf.WriteString(entrySegmentLocationPrefix + strconv.FormatUint(e.Location, 16) + string(entrySegmentLocationSuffix))
return buf.String() return result, nil
} }
// Path returns a deduplicated slice of absolute directory paths in entries. func parseLocation(s string) (uint64, error) {
func Path(entries []*Entry) []*check.Absolute { if len(s) < 4 || s[len(s)-1] != ')' || s[:3] != "(0x" {
p := make([]*check.Absolute, 0, len(entries)*2) return math.MaxUint64, ErrBadLocationFormat
for _, entry := range entries {
if entry.Path != nil {
p = append(p, entry.Path.Dir())
} }
if a, err := check.NewAbs(entry.Name); err == nil { return strconv.ParseUint(s[3:len(s)-1], 16, 64)
p = append(p, a.Dir())
}
}
check.SortAbs(p)
return check.CompactAbs(p)
} }
// A Decoder reads and decodes [Entry] values from an input stream.
//
// The zero value is not safe for use.
type Decoder struct {
s *bufio.Scanner
// Whether the current line is not the first line.
notFirst bool
// Whether s has no more tokens.
depleted bool
// Holds onto the first error encountered while parsing.
err error
}
// NewDecoder returns a new decoder that reads from r.
//
// The decoder introduces its own buffering and may read
// data from r beyond the [Entry] values requested.
func NewDecoder(r io.Reader) *Decoder { return &Decoder{s: bufio.NewScanner(r)} }
// Scan advances the [Decoder] to the next [Entry] and
// stores the result in the value pointed to by v.
func (d *Decoder) Scan(v *Entry) bool {
if d.s == nil || d.err != nil || d.depleted {
return false
}
if !d.s.Scan() {
d.depleted = true
return false
}
data := d.s.Bytes()
if len(data) == 0 {
if d.notFirst {
if d.s.Scan() && d.err == nil {
d.err = ErrUnexpectedNewline
}
// trailing newline is allowed (glibc)
return false
}
// leading newline is allowed (musl)
d.notFirst = true
return d.Scan(v)
}
d.notFirst = true
d.err = v.UnmarshalText(data)
return d.err == nil
}
// Err returns the first non-EOF error that was encountered
// by the underlying [bufio.Scanner] or [Entry].
func (d *Decoder) Err() error {
if d.err != nil || d.s == nil {
return d.err
}
return d.s.Err()
}
// Decode reads from the input stream until there are no more entries
// and returns the results in a slice.
func (d *Decoder) Decode() ([]*Entry, error) {
var entries []*Entry
e := new(Entry)
for d.Scan(e) {
entries = append(entries, e)
e = new(Entry)
}
return entries, d.Err()
}
// Parse returns a slice of addresses to [Entry] decoded from p.
func Parse(p []byte) ([]*Entry, error) { return NewDecoder(bytes.NewReader(p)).Decode() }

View File

@ -1,23 +1,14 @@
package ldd_test package ldd_test
import ( import (
"encoding/json" "errors"
"reflect" "reflect"
"strings"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/ldd" "hakurei.app/ldd"
) )
func TestEntryUnexpectedSegmentsError(t *testing.T) { func TestParseError(t *testing.T) {
const want = `unexpected segments in entry "\x00"`
if got := ldd.EntryUnexpectedSegmentsError("\x00").Error(); got != want {
t.Fatalf("Error: %s, want %s", got, want)
}
}
func TestDecodeError(t *testing.T) {
t.Parallel() t.Parallel()
testCases := []struct { testCases := []struct {
@ -29,36 +20,24 @@ func TestDecodeError(t *testing.T) {
libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000) libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)
`, ldd.ErrUnexpectedNewline}, `, ldd.ErrUnexpectedNewline},
{"unexpected separator", ` {"unexpected separator", `
libzstd.so.1 = /usr/lib/libzstd.so.1 (0x7ff71bfd2000) libzstd.so.1 = /usr/lib/libzstd.so.1 (0x7ff71bfd2000)
`, ldd.ErrUnexpectedSeparator}, `, ldd.ErrUnexpectedSeparator},
{"path not absolute", ` {"path not absolute", `
libzstd.so.1 => usr/lib/libzstd.so.1 (0x7ff71bfd2000) libzstd.so.1 => usr/lib/libzstd.so.1 (0x7ff71bfd2000)
`, &check.AbsoluteError{Pathname: "usr/lib/libzstd.so.1"}}, `, ldd.ErrPathNotAbsolute},
{"unexpected segments", ` {"unexpected segments", `
meow libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000) meow libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)
`, ldd.EntryUnexpectedSegmentsError("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", ` {"bad location format", `
libzstd.so.1 => /usr/lib/libzstd.so.1 7ff71bfd2000 libzstd.so.1 => /usr/lib/libzstd.so.1 7ff71bfd2000
`, ldd.ErrBadLocationFormat}, `, ldd.ErrBadLocationFormat},
{"valid", ``, nil},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
t.Parallel() t.Parallel()
if _, err := ldd.Parse([]byte(tc.out)); !errors.Is(err, tc.wantErr) {
d := ldd.NewDecoder(strings.NewReader(tc.out)) t.Errorf("Parse() error = %v, wantErr %v", err, tc.wantErr)
if _, err := d.Decode(); !reflect.DeepEqual(err, tc.wantErr) {
t.Errorf("Decode: error = %v, wantErr %v", err, tc.wantErr)
}
if d.Scan(new(ldd.Entry)) {
t.Fatalf("Scan: unexpected true")
} }
}) })
} }
@ -70,7 +49,6 @@ func TestParse(t *testing.T) {
testCases := []struct { testCases := []struct {
file, out string file, out string
want []*ldd.Entry want []*ldd.Entry
paths []*check.Absolute
}{ }{
{"musl /bin/kmod", ` {"musl /bin/kmod", `
/lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000) /lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)
@ -78,38 +56,30 @@ libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)
liblzma.so.5 => /usr/lib/liblzma.so.5 (0x7ff71bf9a000) liblzma.so.5 => /usr/lib/liblzma.so.5 (0x7ff71bf9a000)
libz.so.1 => /lib/libz.so.1 (0x7ff71bf80000) libz.so.1 => /lib/libz.so.1 (0x7ff71bf80000)
libcrypto.so.3 => /lib/libcrypto.so.3 (0x7ff71ba00000) libcrypto.so.3 => /lib/libcrypto.so.3 (0x7ff71ba00000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)`, []*ldd.Entry{ libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)`,
{"/lib/ld-musl-x86_64.so.1", nil, 0x7ff71c0a4000}, []*ldd.Entry{
{"libzstd.so.1", check.MustAbs("/usr/lib/libzstd.so.1"), 0x7ff71bfd2000}, {"/lib/ld-musl-x86_64.so.1", "", 0x7ff71c0a4000},
{"liblzma.so.5", check.MustAbs("/usr/lib/liblzma.so.5"), 0x7ff71bf9a000}, {"libzstd.so.1", "/usr/lib/libzstd.so.1", 0x7ff71bfd2000},
{"libz.so.1", check.MustAbs("/lib/libz.so.1"), 0x7ff71bf80000}, {"liblzma.so.5", "/usr/lib/liblzma.so.5", 0x7ff71bf9a000},
{"libcrypto.so.3", check.MustAbs("/lib/libcrypto.so.3"), 0x7ff71ba00000}, {"libz.so.1", "/lib/libz.so.1", 0x7ff71bf80000},
{"libc.musl-x86_64.so.1", check.MustAbs("/lib/ld-musl-x86_64.so.1"), 0x7ff71c0a4000}, {"libcrypto.so.3", "/lib/libcrypto.so.3", 0x7ff71ba00000},
}, []*check.Absolute{ {"libc.musl-x86_64.so.1", "/lib/ld-musl-x86_64.so.1", 0x7ff71c0a4000},
check.MustAbs("/lib"),
check.MustAbs("/usr/lib"),
}}, }},
{"glibc /nix/store/rc3n2r3nffpib2gqpxlkjx36frw6n34z-kmod-31/bin/kmod", ` {"glibc /nix/store/rc3n2r3nffpib2gqpxlkjx36frw6n34z-kmod-31/bin/kmod", `
linux-vdso.so.1 (0x00007ffed65be000) linux-vdso.so.1 (0x00007ffed65be000)
libzstd.so.1 => /nix/store/80pxmvb9q43kh9rkjagc4h41vf6dh1y6-zstd-1.5.6/lib/libzstd.so.1 (0x00007f3199cd1000) 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) 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) 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) 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{ /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)`,
{"linux-vdso.so.1", nil, 0x00007ffed65be000}, []*ldd.Entry{
{"libzstd.so.1", check.MustAbs("/nix/store/80pxmvb9q43kh9rkjagc4h41vf6dh1y6-zstd-1.5.6/lib/libzstd.so.1"), 0x00007f3199cd1000}, {"linux-vdso.so.1", "", 0x00007ffed65be000},
{"liblzma.so.5", check.MustAbs("/nix/store/g78jna1i5qhh8gqs4mr64648f0szqgw4-xz-5.4.7/lib/liblzma.so.5"), 0x00007f3199ca2000}, {"libzstd.so.1", "/nix/store/80pxmvb9q43kh9rkjagc4h41vf6dh1y6-zstd-1.5.6/lib/libzstd.so.1", 0x00007f3199cd1000},
{"libc.so.6", check.MustAbs("/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6"), 0x00007f3199ab5000}, {"liblzma.so.5", "/nix/store/g78jna1i5qhh8gqs4mr64648f0szqgw4-xz-5.4.7/lib/liblzma.so.5", 0x00007f3199ca2000},
{"libpthread.so.0", check.MustAbs("/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libpthread.so.0"), 0x00007f3199ab0000}, {"libc.so.6", "/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6", 0x00007f3199ab5000},
{"/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/ld-linux-x86-64.so.2", check.MustAbs("/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib64/ld-linux-x86-64.so.2"), 0x00007f3199da5000}, {"libpthread.so.0", "/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libpthread.so.0", 0x00007f3199ab0000},
}, []*check.Absolute{ {"/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},
check.MustAbs("/nix/store/80pxmvb9q43kh9rkjagc4h41vf6dh1y6-zstd-1.5.6/lib"),
check.MustAbs("/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib"),
check.MustAbs("/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib64"),
check.MustAbs("/nix/store/g78jna1i5qhh8gqs4mr64648f0szqgw4-xz-5.4.7/lib"),
}}, }},
{"glibc /usr/bin/xdg-dbus-proxy", ` {"glibc /usr/bin/xdg-dbus-proxy", `
linux-vdso.so.1 (0x00007725f5772000) linux-vdso.so.1 (0x00007725f5772000)
libglib-2.0.so.0 => /usr/lib/libglib-2.0.so.0 (0x00007725f55d5000) libglib-2.0.so.0 => /usr/lib/libglib-2.0.so.0 (0x00007725f55d5000)
@ -123,129 +93,31 @@ libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)`, []*ldd.Entr
libmount.so.1 => /usr/lib/libmount.so.1 (0x00007725f5076000) libmount.so.1 => /usr/lib/libmount.so.1 (0x00007725f5076000)
libffi.so.8 => /usr/lib/libffi.so.8 (0x00007725f506b000) libffi.so.8 => /usr/lib/libffi.so.8 (0x00007725f506b000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007725f5774000) /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007725f5774000)
libblkid.so.1 => /usr/lib/libblkid.so.1 (0x00007725f5032000)`, []*ldd.Entry{ libblkid.so.1 => /usr/lib/libblkid.so.1 (0x00007725f5032000)`,
{"linux-vdso.so.1", nil, 0x00007725f5772000}, []*ldd.Entry{
{"libglib-2.0.so.0", check.MustAbs("/usr/lib/libglib-2.0.so.0"), 0x00007725f55d5000}, {"linux-vdso.so.1", "", 0x00007725f5772000},
{"libgio-2.0.so.0", check.MustAbs("/usr/lib/libgio-2.0.so.0"), 0x00007725f5406000}, {"libglib-2.0.so.0", "/usr/lib/libglib-2.0.so.0", 0x00007725f55d5000},
{"libgobject-2.0.so.0", check.MustAbs("/usr/lib/libgobject-2.0.so.0"), 0x00007725f53a6000}, {"libgio-2.0.so.0", "/usr/lib/libgio-2.0.so.0", 0x00007725f5406000},
{"libgcc_s.so.1", check.MustAbs("/usr/lib/libgcc_s.so.1"), 0x00007725f5378000}, {"libgobject-2.0.so.0", "/usr/lib/libgobject-2.0.so.0", 0x00007725f53a6000},
{"libc.so.6", check.MustAbs("/usr/lib/libc.so.6"), 0x00007725f5187000}, {"libgcc_s.so.1", "/usr/lib/libgcc_s.so.1", 0x00007725f5378000},
{"libpcre2-8.so.0", check.MustAbs("/usr/lib/libpcre2-8.so.0"), 0x00007725f50e8000}, {"libc.so.6", "/usr/lib/libc.so.6", 0x00007725f5187000},
{"libgmodule-2.0.so.0", check.MustAbs("/usr/lib/libgmodule-2.0.so.0"), 0x00007725f50df000}, {"libpcre2-8.so.0", "/usr/lib/libpcre2-8.so.0", 0x00007725f50e8000},
{"libz.so.1", check.MustAbs("/usr/lib/libz.so.1"), 0x00007725f50c6000}, {"libgmodule-2.0.so.0", "/usr/lib/libgmodule-2.0.so.0", 0x00007725f50df000},
{"libmount.so.1", check.MustAbs("/usr/lib/libmount.so.1"), 0x00007725f5076000}, {"libz.so.1", "/usr/lib/libz.so.1", 0x00007725f50c6000},
{"libffi.so.8", check.MustAbs("/usr/lib/libffi.so.8"), 0x00007725f506b000}, {"libmount.so.1", "/usr/lib/libmount.so.1", 0x00007725f5076000},
{"/lib64/ld-linux-x86-64.so.2", check.MustAbs("/usr/lib64/ld-linux-x86-64.so.2"), 0x00007725f5774000}, {"libffi.so.8", "/usr/lib/libffi.so.8", 0x00007725f506b000},
{"libblkid.so.1", check.MustAbs("/usr/lib/libblkid.so.1"), 0x00007725f5032000}, {"/lib64/ld-linux-x86-64.so.2", "/usr/lib64/ld-linux-x86-64.so.2", 0x00007725f5774000},
}, []*check.Absolute{ {"libblkid.so.1", "/usr/lib/libblkid.so.1", 0x00007725f5032000},
check.MustAbs("/lib64"),
check.MustAbs("/usr/lib"),
check.MustAbs("/usr/lib64"),
}}, }},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.file, func(t *testing.T) { t.Run(tc.file, func(t *testing.T) {
t.Parallel() t.Parallel()
if got, err := ldd.Parse([]byte(tc.out)); err != nil { if got, err := ldd.Parse([]byte(tc.out)); err != nil {
t.Errorf("Parse: error = %v", err) t.Errorf("Parse() error = %v", err)
} else if !reflect.DeepEqual(got, tc.want) { } else if !reflect.DeepEqual(got, tc.want) {
t.Errorf("Parse: \n%s\nwant\n%s", mustMarshalJSON(got), mustMarshalJSON(tc.want)) t.Errorf("Parse() got = %#v, want %#v", got, tc.want)
} else if paths := ldd.Path(got); !reflect.DeepEqual(paths, tc.paths) {
t.Errorf("Paths: %v, want %v", paths, tc.paths)
} }
}) })
} }
} }
func TestString(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
e ldd.Entry
want string
}{
{"ld", ldd.Entry{
Name: "/lib/ld-musl-x86_64.so.1",
Location: 0x7ff71c0a4000,
}, `/lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)`},
{"libzstd", ldd.Entry{
Name: "libzstd.so.1",
Path: check.MustAbs("/usr/lib/libzstd.so.1"),
Location: 0x7ff71bfd2000,
}, `libzstd.so.1 => /usr/lib/libzstd.so.1 (0x7ff71bfd2000)`},
{"liblzma", ldd.Entry{
Name: "liblzma.so.5",
Path: check.MustAbs("/usr/lib/liblzma.so.5"),
Location: 0x7ff71bf9a000,
}, `liblzma.so.5 => /usr/lib/liblzma.so.5 (0x7ff71bf9a000)`},
{"libz", ldd.Entry{
Name: "libz.so.1",
Path: check.MustAbs("/lib/libz.so.1"),
Location: 0x7ff71bf80000,
}, `libz.so.1 => /lib/libz.so.1 (0x7ff71bf80000)`},
{"libcrypto", ldd.Entry{
Name: "libcrypto.so.3",
Path: check.MustAbs("/lib/libcrypto.so.3"),
Location: 0x7ff71ba00000,
}, `libcrypto.so.3 => /lib/libcrypto.so.3 (0x7ff71ba00000)`},
{"libc", ldd.Entry{
Name: "libc.musl-x86_64.so.1",
Path: check.MustAbs("/lib/ld-musl-x86_64.so.1"),
Location: 0x7ff71c0a4000,
}, `libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)`},
{"invalid", ldd.Entry{
Location: 0x7ff71c0a4000,
}, `invalid (0x7ff71c0a4000)`},
{"invalid long", ldd.Entry{
Path: check.MustAbs("/lib/ld-musl-x86_64.so.1"),
Location: 0x7ff71c0a4000,
}, `invalid => /lib/ld-musl-x86_64.so.1 (0x7ff71c0a4000)`},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
t.Run("decode", func(t *testing.T) {
if tc.e.Name == "" {
return
}
t.Parallel()
var got ldd.Entry
if err := got.UnmarshalText([]byte(tc.want)); err != nil {
t.Fatalf("UnmarshalText: error = %v", err)
}
if !reflect.DeepEqual(&got, &tc.e) {
t.Errorf("UnmarshalText: %#v, want %#v", got, tc.e)
}
})
t.Run("encode", func(t *testing.T) {
t.Parallel()
if got := tc.e.String(); got != tc.want {
t.Errorf("String: %s, want %s", got, tc.want)
}
})
})
}
}
// mustMarshalJSON calls [json.Marshal] and returns the resulting data.
func mustMarshalJSON(v any) []byte {
if data, err := json.Marshal(v); err != nil {
panic(err)
} else {
return data
}
}

20
ldd/path.go Normal file
View File

@ -0,0 +1,20 @@
package ldd
import (
"hakurei.app/container/check"
)
// Path returns a deterministic, deduplicated slice of absolute directory paths in entries.
func Path(entries []*Entry) []*check.Absolute {
p := make([]*check.Absolute, 0, len(entries)*2)
for _, entry := range entries {
if a, err := check.NewAbs(entry.Path); err == nil {
p = append(p, a.Dir())
}
if a, err := check.NewAbs(entry.Name); err == nil {
p = append(p, a.Dir())
}
}
check.SortAbs(p)
return check.CompactAbs(p)
}

View File

@ -68,7 +68,7 @@ in
home-manager = home-manager =
let let
privPackages = mapAttrs (username: userid: { privPackages = mapAttrs (username: fid: {
home.packages = foldlAttrs ( home.packages = foldlAttrs (
acc: id: app: acc: id: app:
[ [
@ -199,15 +199,15 @@ in
++ [ ++ [
{ {
type = "bind"; type = "bind";
src = getsubhome userid app.identity; src = getsubhome fid app.identity;
write = true; write = true;
ensure = true; ensure = true;
} }
]; ];
username = getsubname userid app.identity; username = getsubname fid app.identity;
inherit (cfg) shell; inherit (cfg) shell;
home = getsubhome userid app.identity; home = getsubhome fid app.identity;
path = path =
if app.path == null then if app.path == null then
@ -310,15 +310,15 @@ in
users = users =
let let
getuser = userid: appid: { getuser = fid: aid: {
isSystemUser = true; isSystemUser = true;
createHome = true; createHome = true;
description = "Hakurei subordinate user ${toString appid} (u${toString userid})"; description = "Hakurei subordinate user ${toString aid} (u${toString fid})";
group = getsubname userid appid; group = getsubname fid aid;
home = getsubhome userid appid; home = getsubhome fid aid;
uid = getsubuid userid appid; uid = getsubuid fid aid;
}; };
getgroup = userid: appid: { gid = getsubuid userid appid; }; getgroup = fid: aid: { gid = getsubuid fid aid; };
in in
{ {
users = mkMerge ( users = mkMerge (

View File

@ -35,7 +35,7 @@ package
*Default:* *Default:*
` <derivation hakurei-static-x86_64-unknown-linux-musl-0.3.1> ` ` <derivation hakurei-static-x86_64-unknown-linux-musl-0.2.2> `
@ -579,52 +579,6 @@ null or package
## environment\.hakurei\.apps\.\<name>\.shareRuntime
Whether to enable sharing of XDG_RUNTIME_DIR between containers under the same identity\.
*Type:*
boolean
*Default:*
` false `
*Example:*
` true `
## environment\.hakurei\.apps\.\<name>\.shareTmpdir
Whether to enable sharing of TMPDIR between containers under the same identity\.
*Type:*
boolean
*Default:*
` false `
*Example:*
` true `
## environment\.hakurei\.apps\.\<name>\.shareUid ## environment\.hakurei\.apps\.\<name>\.shareUid
@ -805,7 +759,7 @@ package
*Default:* *Default:*
` <derivation hakurei-hsu-0.3.1> ` ` <derivation hakurei-hsu-0.2.2> `

View File

@ -20,11 +20,10 @@
# for passthru.buildInputs # for passthru.buildInputs
go, go,
clang, gcc,
# for check # for check
util-linux, util-linux,
nettools,
glibc, # for ldd glibc, # for ldd
withStatic ? stdenv.hostPlatform.isStatic, withStatic ? stdenv.hostPlatform.isStatic,
@ -32,7 +31,7 @@
buildGoModule rec { buildGoModule rec {
pname = "hakurei"; pname = "hakurei";
version = "0.3.1"; version = "0.3.0";
srcFiltered = builtins.path { srcFiltered = builtins.path {
name = "${pname}-src"; name = "${pname}-src";
@ -66,7 +65,7 @@ buildGoModule rec {
lib.attrsets.foldlAttrs lib.attrsets.foldlAttrs
( (
ldflags: name: value: ldflags: name: value:
ldflags ++ [ "-X hakurei.app/internal/info.${name}=${value}" ] ldflags ++ [ "-X hakurei.app/internal.${name}=${value}" ]
) )
( (
[ "-s -w" ] [ "-s -w" ]
@ -81,13 +80,8 @@ buildGoModule rec {
hsuPath = "/run/wrappers/bin/hsu"; hsuPath = "/run/wrappers/bin/hsu";
}; };
env = {
# use clang instead of gcc
CC = "clang -O3 -Werror";
# nix build environment does not allow acls # nix build environment does not allow acls
GO_TEST_SKIP_ACL = 1; env.GO_TEST_SKIP_ACL = 1;
};
buildInputs = [ buildInputs = [
libffi libffi
@ -104,9 +98,6 @@ buildGoModule rec {
nativeBuildInputs = [ nativeBuildInputs = [
pkg-config pkg-config
makeBinaryWrapper makeBinaryWrapper
# for container example
nettools
]; ];
postInstall = postInstall =
@ -140,7 +131,7 @@ buildGoModule rec {
passthru.targetPkgs = [ passthru.targetPkgs = [
go go
clang gcc
xorg.xorgproto xorg.xorgproto
util-linux util-linux

View File

@ -8,7 +8,7 @@ import (
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/acl" "hakurei.app/system/acl"
) )
// UpdatePerm calls UpdatePermType with the [Process] criteria. // UpdatePerm calls UpdatePermType with the [Process] criteria.

View File

@ -13,7 +13,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"hakurei.app/internal/acl" "hakurei.app/system/acl"
) )
const testFileName = "acl.test" const testFileName = "acl.test"

View File

@ -1,25 +0,0 @@
// Package acl exposes the internal/acl package.
//
// Deprecated: This package will be removed in 0.4.
package acl
import (
_ "unsafe" // for go:linkname
"hakurei.app/internal/acl"
)
type Perm = acl.Perm
const (
Read = acl.Read
Write = acl.Write
Execute = acl.Execute
)
// Update replaces ACL_USER entry with qualifier uid.
//
//go:linkname Update hakurei.app/internal/acl.Update
func Update(name string, uid int, perms ...Perm) error
type Perms = acl.Perms

View File

@ -0,0 +1,90 @@
#include "libacl-helper.h"
#include <acl/libacl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/acl.h>
int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid,
acl_perm_t *perms, size_t plen) {
int ret;
bool v;
int i;
acl_t acl;
acl_entry_t entry;
acl_tag_t tag_type;
void *qualifier_p;
acl_permset_t permset;
ret = -1; /* acl_get_file */
acl = acl_get_file(path_p, ACL_TYPE_ACCESS);
if (acl == NULL)
goto out;
/* prune entries by uid */
for (i = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); i == 1;
i = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) {
ret = -2; /* acl_get_tag_type */
if (acl_get_tag_type(entry, &tag_type) != 0)
goto out;
if (tag_type != ACL_USER)
continue;
ret = -3; /* acl_get_qualifier */
qualifier_p = acl_get_qualifier(entry);
if (qualifier_p == NULL)
goto out;
v = *(uid_t *)qualifier_p == uid;
acl_free(qualifier_p);
if (!v)
continue;
ret = -4; /* acl_delete_entry */
if (acl_delete_entry(acl, entry) != 0)
goto out;
}
if (plen == 0)
goto set;
ret = -5; /* acl_create_entry */
if (acl_create_entry(&acl, &entry) != 0)
goto out;
ret = -6; /* acl_get_permset */
if (acl_get_permset(entry, &permset) != 0)
goto out;
ret = -7; /* acl_add_perm */
for (i = 0; i < plen; i++) {
if (acl_add_perm(permset, perms[i]) != 0)
goto out;
}
ret = -8; /* acl_set_tag_type */
if (acl_set_tag_type(entry, ACL_USER) != 0)
goto out;
ret = -9; /* acl_set_qualifier */
if (acl_set_qualifier(entry, (void *)&uid) != 0)
goto out;
set:
ret = -10; /* acl_calc_mask */
if (acl_calc_mask(&acl) != 0)
goto out;
ret = -11; /* acl_valid */
if (acl_valid(acl) != 0)
goto out;
ret = -12; /* acl_set_file */
if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) == 0)
ret = 0;
out:
free((void *)path_p);
if (acl != NULL)
acl_free((void *)acl);
return ret;
}

Some files were not shown because too many files have changed in this diff Show More