From 58913f00845903aa2068f6cb0778baa563d58d14 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Mon, 19 Oct 2026 15:09:33 +0900 Subject: container/sysctl: remove calls to logger This behaviour is counterintuitive, the responses are cached, and only the msg object of the first call is used. This change also improves error message formatting of these functions. Signed-off-by: Ophestra --- container/container.go | 4 +-- container/dispatcher.go | 16 +++++----- container/dispatcher_test.go | 3 +- container/init.go | 4 +-- container/sysctl.go | 71 +++++++++++++++++++++++++----------------- container/sysctl_deprecated.go | 19 +++++++++++ 6 files changed, 75 insertions(+), 42 deletions(-) create mode 100644 container/sysctl_deprecated.go (limited to 'container') diff --git a/container/container.go b/container/container.go index 3571d04a..6929c652 100644 --- a/container/container.go +++ b/container/container.go @@ -231,10 +231,10 @@ func (p *Container) Start() error { // map to overflow id to work around ownership checks if p.Uid < 1 { - p.Uid = OverflowUid(p.msg) + p.Uid = OverflowUID() } if p.Gid < 1 { - p.Gid = OverflowGid(p.msg) + p.Gid = OverflowGID() } if !p.RetainSession { diff --git a/container/dispatcher.go b/container/dispatcher.go index 4636a469..ba5e9c10 100644 --- a/container/dispatcher.go +++ b/container/dispatcher.go @@ -37,15 +37,15 @@ type syscallDispatcher interface { // lockOSThread provides [runtime.LockOSThread]. lockOSThread() - // setPtracer provides [SetPtracer]. + // setPtracer provides [ext.SetPtracer]. setPtracer(pid uintptr) error - // setDumpable provides [SetDumpable]. + // setDumpable provides [ext.SetDumpable]. setDumpable(dumpable uintptr) error - // setNoNewPrivs provides [SetNoNewPrivs]. + // setNoNewPrivs provides setNoNewPrivs. setNoNewPrivs() error - // lastcap provides [LastCap]. - lastcap(msg message.Msg) uintptr + // lastcap provides lastcap. + lastcap() uintptr // capset provides capset. capset(hdrp *capHeader, datap *[2]capData) error // capBoundingSetDrop provides capBoundingSetDrop. @@ -54,9 +54,9 @@ type syscallDispatcher interface { capAmbientClearAll() error // capAmbientRaise provides capAmbientRaise. capAmbientRaise(cap uintptr) error - // isatty provides [Isatty]. + // isatty provides [ext.Isatty]. isatty(fd int) bool - // receive provides [Receive]. + // receive provides [params.Receive]. receive(key string, e any, fdp *int) (closeFunc func() error, err error) // bindMount provides procPaths.bindMount. @@ -152,7 +152,7 @@ func (direct) setPtracer(pid uintptr) error { return ext.SetPtracer(pid) } func (direct) setDumpable(dumpable uintptr) error { return ext.SetDumpable(dumpable) } func (direct) setNoNewPrivs() error { return setNoNewPrivs() } -func (direct) lastcap(msg message.Msg) uintptr { return LastCap(msg) } +func (direct) lastcap() uintptr { return lastcap() } func (direct) capset(hdrp *capHeader, datap *[2]capData) error { return capset(hdrp, datap) } func (direct) capBoundingSetDrop(cap uintptr) error { return capBoundingSetDrop(cap) } func (direct) capAmbientClearAll() error { return capAmbientClearAll() } diff --git a/container/dispatcher_test.go b/container/dispatcher_test.go index 242af1bf..9276a2c5 100644 --- a/container/dispatcher_test.go +++ b/container/dispatcher_test.go @@ -352,9 +352,8 @@ func (k *kstub) setDumpable(dumpable uintptr) error { } func (k *kstub) setNoNewPrivs() error { k.Helper(); return k.Expects("setNoNewPrivs").Err } -func (k *kstub) lastcap(msg message.Msg) uintptr { +func (k *kstub) lastcap() uintptr { k.Helper() - k.checkMsg(msg) return k.Expects("lastcap").Ret.(uintptr) } diff --git a/container/init.go b/container/init.go index 07fff357..03501646 100644 --- a/container/init.go +++ b/container/init.go @@ -226,7 +226,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) { } // cache sysctl before pivot_root - lastcap := k.lastcap(msg) + lc := k.lastcap() if err := k.mount(zeroString, fhs.Root, zeroString, MS_SILENT|MS_SLAVE|MS_REC, zeroString); err != nil { k.fatalf(msg, "cannot make / rslave: %v", optionalErrorUnwrap(err)) @@ -399,7 +399,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) { if err := k.capAmbientClearAll(); err != nil { k.fatalf(msg, "cannot clear the ambient capability set: %v", err) } - for i := range lastcap + 1 { + for i := range lc + 1 { if slices.Contains(keepCaps, i) { continue } diff --git a/container/sysctl.go b/container/sysctl.go index e4b7af23..2cf47258 100644 --- a/container/sysctl.go +++ b/container/sysctl.go @@ -2,14 +2,32 @@ package container import ( "bytes" + "fmt" "os" "strconv" "sync" "hakurei.app/fhs" - "hakurei.app/message" ) +// mustSysctlInt reads a sysctl path via proc, interprets it as a decimal +// representation of an integer, and terminates the program if the read was +// unsuccessful or the response is invalid. +func mustSysctlInt(path string) (v int) { + p, err := os.ReadFile(fhs.ProcSys + path) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + if v, err = strconv.Atoi(string(bytes.TrimSpace(p))); err != nil { + fmt.Printf("invalid sysctl response %q\n", string(p)) + os.Exit(1) + } + return +} + +// cached sysctl responses for populateSysctl var ( kernelOverflowuid int kernelOverflowgid int @@ -18,34 +36,31 @@ var ( sysctlOnce sync.Once ) -const ( - kernelOverflowuidPath = fhs.ProcSys + "kernel/overflowuid" - kernelOverflowgidPath = fhs.ProcSys + "kernel/overflowgid" - kernelCapLastCapPath = fhs.ProcSys + "kernel/cap_last_cap" -) - -func mustReadSysctl(msg message.Msg) { +// populateSysctl populates cached sysctl responses guarded by sysctlOnce. +func populateSysctl() { sysctlOnce.Do(func() { - if v, err := os.ReadFile(kernelOverflowuidPath); err != nil { - msg.GetLogger().Fatalf("cannot read %q: %v", kernelOverflowuidPath, err) - } else if kernelOverflowuid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil { - msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelOverflowuidPath, err) - } - - if v, err := os.ReadFile(kernelOverflowgidPath); err != nil { - msg.GetLogger().Fatalf("cannot read %q: %v", kernelOverflowgidPath, err) - } else if kernelOverflowgid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil { - msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelOverflowgidPath, err) - } - - if v, err := os.ReadFile(kernelCapLastCapPath); err != nil { - msg.GetLogger().Fatalf("cannot read %q: %v", kernelCapLastCapPath, err) - } else if kernelCapLastCap, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil { - msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelCapLastCapPath, err) - } + kernelOverflowuid = mustSysctlInt("kernel/overflowuid") + kernelOverflowgid = mustSysctlInt("kernel/overflowgid") + kernelCapLastCap = mustSysctlInt("kernel/cap_last_cap") }) } -func OverflowUid(msg message.Msg) int { mustReadSysctl(msg); return kernelOverflowuid } -func OverflowGid(msg message.Msg) int { mustReadSysctl(msg); return kernelOverflowgid } -func LastCap(msg message.Msg) uintptr { mustReadSysctl(msg); return uintptr(kernelCapLastCap) } +// OverflowUID returns the fixed uid returned by legacy 16-bit UID system calls. +// This value is also returned for unmapped UIDs in a user namespace. +func OverflowUID() int { + populateSysctl() + return kernelOverflowuid +} + +// OverflowGID is like [OverflowUID], but for GID instead. +func OverflowGID() int { + populateSysctl() + return kernelOverflowgid +} + +// lastcap returns the highest valid capability of the running kernel. Exports +// CAP_LAST_CAP from the kernel. +func lastcap() uintptr { + populateSysctl() + return uintptr(kernelCapLastCap) +} diff --git a/container/sysctl_deprecated.go b/container/sysctl_deprecated.go new file mode 100644 index 00000000..3c76be2c --- /dev/null +++ b/container/sysctl_deprecated.go @@ -0,0 +1,19 @@ +package container + +import "hakurei.app/message" + +// OverflowUid is the old function signature of [OverflowUID]. +// +// Deprecated: This function will be removed in 0.5. +func OverflowUid(_ message.Msg) int { return OverflowUID() } + +// OverflowGid is the old function signature of [OverflowGID]. +// +// Deprecated: This function will be removed in 0.5. +func OverflowGid(_ message.Msg) int { return OverflowGID() } + +// LastCap returns the highest valid capability of the running kernel. Exports +// CAP_LAST_CAP from the kernel. +// +// Deprecated: This function will be unexported in 0.5. +func LastCap(_ message.Msg) uintptr { return lastcap() } -- cgit v1.3.1