Skip to content

Commit

Permalink
Implement personality(2) for no-op personality bits.
Browse files Browse the repository at this point in the history
This PR implements the [`personality(2)` syscall](https://www.man7.org/linux/man-pages/man2/personality.2.html) for personality bits that are no-ops on Linux.

Fixes #10756.

FUTURE_COPYBARA_INTEGRATE_REVIEW=#10757 from EtiennePerot:linux-is-not-a-personality-trait 91d3665
PiperOrigin-RevId: 665427818
  • Loading branch information
EtiennePerot authored and gvisor-bot committed Aug 20, 2024
1 parent e0643b8 commit 693258e
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 2 deletions.
1 change: 1 addition & 0 deletions pkg/abi/linux/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ go_library(
"netlink.go",
"netlink_route.go",
"nf_tables.go",
"personality.go",
"poll.go",
"prctl.go",
"ptrace.go",
Expand Down
29 changes: 29 additions & 0 deletions pkg/abi/linux/personality.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package linux

// Personality flags, used by personality(2),
// from include/uapi/linux/personality.h.
const (
SHORT_INODE = 0x1000000
WHOLE_SECONDS = 0x2000000
PER_LINUX = 0x0000
PER_BSD = 0x0006
)

// NOTE: All of the above flags are non-security-sensitive and may be copied
// from parent task to child task. However, this is not the case for all
// personality bits. If adding more, check PER_CLEAR_ON_SETID and ensure that
// these are cleared on suid/sgid execs.
1 change: 1 addition & 0 deletions pkg/sentry/kernel/kernel.go
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,7 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID,
InitialCgroups: args.InitialCgroups,
UserCounters: k.GetUserCounters(args.Credentials.RealKUID),
Origin: args.Origin,
Personality: linux.PER_LINUX,
// A task with no parent starts out with no session keyring.
SessionKeyring: nil,
}
Expand Down
14 changes: 14 additions & 0 deletions pkg/sentry/kernel/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,9 @@ type Task struct {
// +checklocks:mu
sessionKeyring *auth.Key

// personality is the task's personality(2) bits.
personality atomicbitops.Uint32

// Origin is the origin of the task.
Origin TaskOrigin
}
Expand Down Expand Up @@ -869,3 +872,14 @@ func (t *Task) ResetKcov() {
t.kcov = nil
}
}

// Personality returns the task's personality.
func (t *Task) Personality() uint32 {
return t.personality.Load()
}

// SetPersonality sets the task's personality.
// It returns the task's former personality.
func (t *Task) SetPersonality(personality uint32) uint32 {
return t.personality.Swap(personality)
}
1 change: 1 addition & 0 deletions pkg/sentry/kernel/task_clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
ContainerID: t.ContainerID(),
UserCounters: uc,
SessionKeyring: sessionKeyring,
Personality: t.personality.Load(),
Origin: t.Origin,
}
if args.Flags&linux.CLONE_THREAD == 0 {
Expand Down
4 changes: 4 additions & 0 deletions pkg/sentry/kernel/task_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ type TaskConfig struct {
// It may be nil.
SessionKeyring *auth.Key

// Personality is the personality of the parent task.
Personality uint32

Origin TaskOrigin
}

Expand Down Expand Up @@ -174,6 +177,7 @@ func (ts *TaskSet) newTask(ctx context.Context, cfg *TaskConfig) (*Task, error)
cgroups: make(map[Cgroup]struct{}),
userCounters: cfg.UserCounters,
sessionKeyring: cfg.SessionKeyring,
personality: atomicbitops.FromUint32(cfg.Personality),
Origin: cfg.Origin,
}
t.netns = cfg.NetworkNamespace
Expand Down
1 change: 1 addition & 0 deletions pkg/sentry/syscalls/linux/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ go_library(
"sys_mount.go",
"sys_mq.go",
"sys_msgqueue.go",
"sys_personality.go",
"sys_pipe.go",
"sys_poll.go",
"sys_prctl.go",
Expand Down
4 changes: 2 additions & 2 deletions pkg/sentry/syscalls/linux/linux64.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ var AMD64 = &kernel.SyscallTable{
132: syscalls.Supported("utime", Utime),
133: syscalls.Supported("mknod", Mknod),
134: syscalls.Error("uselib", linuxerr.ENOSYS, "Obsolete", nil),
135: syscalls.ErrorWithEvent("personality", linuxerr.EINVAL, "Unable to change personality.", nil),
135: syscalls.PartiallySupported("personality", Personality, "Only setting no-op personality bits and retrieving them are supported.", nil),
136: syscalls.ErrorWithEvent("ustat", linuxerr.ENOSYS, "Needs filesystem support.", nil),
137: syscalls.Supported("statfs", Statfs),
138: syscalls.Supported("fstatfs", Fstatfs),
Expand Down Expand Up @@ -523,7 +523,7 @@ var ARM64 = &kernel.SyscallTable{
89: syscalls.CapError("acct", linux.CAP_SYS_PACCT, "", nil),
90: syscalls.Supported("capget", Capget),
91: syscalls.Supported("capset", Capset),
92: syscalls.ErrorWithEvent("personality", linuxerr.EINVAL, "Unable to change personality.", nil),
92: syscalls.PartiallySupported("personality", Personality, "Only setting no-op personality bits and retrieving them are supported.", nil),
93: syscalls.Supported("exit", Exit),
94: syscalls.Supported("exit_group", ExitGroup),
95: syscalls.Supported("waitid", Waitid),
Expand Down
43 changes: 43 additions & 0 deletions pkg/sentry/syscalls/linux/sys_personality.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package linux

import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
)

const (
// getPersonality may be passed to `personality(2)` to get the current
// personality bits without modifying them.
getPersonality = 0xffffffff
)

// Personality implements Linux syscall personality(2).
func Personality(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
// allowedPersonalityBits are the personality bits that are allowed to be set.
const allowedPersonalityBits = linux.PER_LINUX | linux.PER_BSD | linux.SHORT_INODE | linux.WHOLE_SECONDS

personality := args[0].Uint()
if personality == getPersonality {
return uintptr(t.Personality()), nil, nil
}
if personality&allowedPersonalityBits != personality {
return 0, nil, linuxerr.EINVAL
}
return uintptr(t.SetPersonality(personality)), nil, nil
}
4 changes: 4 additions & 0 deletions test/syscalls/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,10 @@ syscall_test(
test = "//test/syscalls/linux:pause_test",
)

syscall_test(
test = "//test/syscalls/linux:personality_test",
)

syscall_test(
size = "medium",
add_hostinet = True,
Expand Down
14 changes: 14 additions & 0 deletions test/syscalls/linux/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,20 @@ cc_binary(
],
)

cc_binary(
name = "personality_test",
testonly = 1,
srcs = ["personality.cc"],
linkstatic = 1,
malloc = "//test/util:errno_safe_allocator",
deps = select_gtest() + [
"//test/util:multiprocess_util",
"//test/util:posix_error",
"//test/util:test_main",
"//test/util:test_util",
],
)

cc_binary(
name = "ping_socket_test",
testonly = 1,
Expand Down
163 changes: 163 additions & 0 deletions test/syscalls/linux/personality.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <sys/personality.h>

#include "gtest/gtest.h"
#include "test/util/multiprocess_util.h"
#include "test/util/posix_error.h"
#include "test/util/test_util.h"

namespace gvisor {
namespace testing {

namespace {

constexpr uint64_t kGetPersonality = 0xffffffff;
constexpr uint64_t kUndefinedPersonalityBit = 0x00001000;

TEST(PersonalityTest, DefaultPersonality) {
EXPECT_THAT(personality(kGetPersonality),
SyscallSucceedsWithValue(PER_LINUX));
}

TEST(PersonalityTest, SetLinux) {
EXPECT_THAT(personality(kGetPersonality),
SyscallSucceedsWithValue(PER_LINUX));
EXPECT_THAT(InForkedProcess([&] {
TEST_CHECK(personality(PER_LINUX) == PER_LINUX);
TEST_CHECK(personality(kGetPersonality) == PER_LINUX);
TEST_CHECK(personality(kGetPersonality) == PER_LINUX);
}),
IsPosixErrorOkAndHolds(0));
EXPECT_THAT(personality(kGetPersonality),
SyscallSucceedsWithValue(PER_LINUX));
}

TEST(PersonalityTest, SetBSD) {
EXPECT_THAT(personality(kGetPersonality),
SyscallSucceedsWithValue(PER_LINUX));
EXPECT_THAT(InForkedProcess([&] {
TEST_CHECK(personality(PER_BSD) == PER_LINUX);
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
}),
IsPosixErrorOkAndHolds(0));
}

TEST(PersonalityTest, PersonalityIsInheritable) {
EXPECT_THAT(
InForkedProcess([&] {
TEST_CHECK(personality(PER_BSD) == PER_LINUX);
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
TEST_CHECK(
InForkedProcess([&] {
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
TEST_CHECK(InForkedProcess([&] {
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
}).ok());
TEST_CHECK(personality(PER_SOLARIS) == PER_BSD);
TEST_CHECK(InForkedProcess([&] {
TEST_CHECK(personality(kGetPersonality) ==
PER_SOLARIS);
}).ok());
TEST_CHECK(personality(PER_BSD) == PER_SOLARIS);
TEST_CHECK(InForkedProcess([&] {
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
}).ok());
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
}).ok());
}),
IsPosixErrorOkAndHolds(0));
}

TEST(PersonalityTest, ChildPersonalityDoesNotAffectParent) {
EXPECT_THAT(
InForkedProcess([&] {
TEST_CHECK(personality(PER_BSD) == PER_LINUX);
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
TEST_CHECK(InForkedProcess([&] {
TEST_CHECK(personality(PER_SOLARIS) == PER_BSD);
TEST_CHECK(personality(kGetPersonality) == PER_SOLARIS);
}).ok());
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
}),
IsPosixErrorOkAndHolds(0));
TEST_CHECK(personality(kGetPersonality) == PER_LINUX);
}

TEST(PersonalityTest, SetShortInode) {
EXPECT_THAT(InForkedProcess([&] {
TEST_CHECK(personality(SHORT_INODE) == PER_LINUX);
TEST_CHECK(personality(kGetPersonality) == SHORT_INODE);
}),
IsPosixErrorOkAndHolds(0));
}

TEST(PersonalityTest, SetWholeSeconds) {
EXPECT_THAT(InForkedProcess([&] {
TEST_CHECK(personality(WHOLE_SECONDS) == PER_LINUX);
TEST_CHECK(personality(kGetPersonality) == WHOLE_SECONDS);
}),
IsPosixErrorOkAndHolds(0));
}

TEST(PersonalityTest, SetMultiple) {
EXPECT_THAT(InForkedProcess([&] {
TEST_CHECK(personality(PER_BSD | SHORT_INODE | WHOLE_SECONDS) ==
PER_LINUX);
TEST_CHECK(personality(kGetPersonality) ==
(PER_BSD | SHORT_INODE | WHOLE_SECONDS));
}),
IsPosixErrorOkAndHolds(0));
}

TEST(PersonalityTest, SetThenUnset) {
EXPECT_THAT(
InForkedProcess([&] {
TEST_CHECK(personality(PER_BSD | SHORT_INODE) == PER_LINUX);
TEST_CHECK(personality(WHOLE_SECONDS | SHORT_INODE) ==
(PER_BSD | SHORT_INODE));
TEST_CHECK(personality(PER_BSD) == (WHOLE_SECONDS | SHORT_INODE));
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
}),
IsPosixErrorOkAndHolds(0));
}

TEST(PersonalityTest, UnsupportedPersonalityBitsAreRejected) {
SKIP_IF(!IsRunningOnGvisor());
EXPECT_THAT(personality(kGetPersonality),
SyscallSucceedsWithValue(PER_LINUX));
EXPECT_THAT(personality(MMAP_PAGE_ZERO), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(ADDR_LIMIT_3GB), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(ADDR_LIMIT_32BIT), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(ADDR_NO_RANDOMIZE), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(ADDR_COMPAT_LAYOUT), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(READ_IMPLIES_EXEC), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(STICKY_TIMEOUTS), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(UNAME26), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(FDPIC_FUNCPTRS), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(PER_LINUX32), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(PER_LINUX32_3GB), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(PER_LINUX_32BIT), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(PER_LINUX_FDPIC), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(PER_RISCOS), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(PER_SOLARIS), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(personality(kGetPersonality),
SyscallSucceedsWithValue(PER_LINUX));
}

} // namespace

} // namespace testing
} // namespace gvisor

0 comments on commit 693258e

Please sign in to comment.