From 4600cdde2083c3d4e586c6e94d711c72e6888700 Mon Sep 17 00:00:00 2001 From: flouthoc Date: Tue, 3 Aug 2021 15:27:18 +0530 Subject: [PATCH] linux: Support setting execution domain via linux personality. Signed-off-by: Aditya Rajan --- libcontainer/configs/config.go | 3 +++ libcontainer/configs/config_linux.go | 13 +++++++++++++ libcontainer/container_linux.go | 8 ++++++++ libcontainer/integration/exec_test.go | 15 +++++++++++++++ libcontainer/integration/template_test.go | 11 +++++++++-- libcontainer/specconv/spec_linux.go | 20 ++++++++++++++++++++ libcontainer/system/linux.go | 12 ++++++++++++ 7 files changed, 80 insertions(+), 2 deletions(-) diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index 2dad5c8b575..e3c5eccb83c 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -206,6 +206,9 @@ type Config struct { // RootlessCgroups is set when unlikely to have the full access to cgroups. // When RootlessCgroups is set, cgroups errors are ignored. RootlessCgroups bool `json:"rootless_cgroups,omitempty"` + + // Personality contains configuration for the Linux personality syscall. + Personality *LinuxPersonality `json:"personality,omitempty"` } type ( diff --git a/libcontainer/configs/config_linux.go b/libcontainer/configs/config_linux.go index 8c02848b70a..67eeb205513 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://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/personality.h +const ( + PER_LINUX = 0x0000 + PER_LINUX32 = 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/container_linux.go b/libcontainer/container_linux.go index 19a82bbf6f6..ffa265f5c0f 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -228,6 +228,14 @@ func (c *linuxContainer) Set(config configs.Config) error { } func (c *linuxContainer) Start(process *Process) error { + // configure linux personality before starting the process + if c.Config().Personality != nil { + err := system.SetLinuxPersonality(c.Config().Personality.Domain) + if err != nil { + return err + } + } + c.m.Lock() defer c.m.Unlock() if c.config.Cgroups.Resources.SkipDevices { diff --git a/libcontainer/integration/exec_test.go b/libcontainer/integration/exec_test.go index 28f2e10d2c0..d1a83e83279 100644 --- a/libcontainer/integration/exec_test.go +++ b/libcontainer/integration/exec_test.go @@ -137,6 +137,21 @@ func TestIPCBadPath(t *testing.T) { } } +func TestPersonality(t *testing.T) { + if testing.Short() { + return + } + + config := newTemplateConfig(t, &tParam{personality: configs.PER_LINUX32}) + + out, _, err := runContainer(t, config, "", "/bin/sh", "-c", "uname -a") + ok(t, err) + // output must contain kernel architecture configured as i686 + if !strings.Contains(out.Stdout.String(), "i686") { + t.Fatalf("expected kernel architecture i686 configured via personality") + } +} + func TestRlimit(t *testing.T) { testRlimit(t, false) } diff --git a/libcontainer/integration/template_test.go b/libcontainer/integration/template_test.go index d35904766b9..b9659b37a37 100644 --- a/libcontainer/integration/template_test.go +++ b/libcontainer/integration/template_test.go @@ -22,8 +22,9 @@ var standardEnvironment = []string{ const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV type tParam struct { - userns bool - systemd bool + userns bool + systemd bool + personality int } // newTemplateConfig returns a base template for running a container. @@ -218,6 +219,12 @@ func newTemplateConfig(t *testing.T, p *tParam) *configs.Config { }) } + if p.personality != 0 { + config.Personality = &configs.LinuxPersonality{ + Domain: p.personality, + } + } + if p.systemd { id := strconv.FormatInt(-int64(time.Now().Nanosecond()), 36) config.Cgroups.Name = strings.ReplaceAll(t.Name(), "/", "_") + id diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index 89172316358..5cbeab21827 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -305,6 +305,11 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) { MemBwSchema: spec.Linux.IntelRdt.MemBwSchema, } } + if spec.Linux.Personality != nil { + config.Personality = &configs.LinuxPersonality{ + Domain: getLinuxPersonalityFromStr(string(spec.Linux.Personality.Domain)), + } + } } if spec.Process != nil { config.OomScoreAdj = spec.Process.OOMScoreAdj @@ -391,6 +396,21 @@ func convertSecToUSec(value dbus.Variant) (dbus.Variant, error) { return dbus.MakeVariant(sec), nil } +// getLinuxPersonalityFromStr converts the string domain received from spec to equivalent integer. +// check libcontainer/configs/config_linux.go +// check https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/personality.h +// valid arguments +// LINUX i.e PER_LINUX (since Linux 1.2.0). +// LINUX32 i.e PER_LINUX32 (since Linux 2.2) LINUX32 will set the uname system call to show a 32 bit CPU type, such as i686. +// everything unknown default to PER_LINUX. +func getLinuxPersonalityFromStr(domain string) int { + // defaults to PER_LINUX + if domain == "LINUX32" { + return configs.PER_LINUX32 + } + return configs.PER_LINUX +} + func initSystemdProps(spec *specs.Spec) ([]systemdDbus.Property, error) { const keyPrefix = "org.systemd.property." var sp []systemdDbus.Property diff --git a/libcontainer/system/linux.go b/libcontainer/system/linux.go index b9fd0832d56..3cfadaa8422 100644 --- a/libcontainer/system/linux.go +++ b/libcontainer/system/linux.go @@ -3,7 +3,9 @@ package system import ( + "fmt" "os/exec" + "syscall" "unsafe" "golang.org/x/sys/unix" @@ -108,3 +110,13 @@ func GetSubreaper() (int, error) { return int(i), nil } + +// 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(persona int) error { + _, _, errno := syscall.Syscall(syscall.SYS_PERSONALITY, uintptr(persona), 0, 0) + if errno != 0 { + return fmt.Errorf("syscall: SYS_PERSONALITY failed with error code: %w", errno) + } + return nil +}