Skip to content

Commit e212c97

Browse files
committed
Implement personality(2) for no-op personality bits.
Fixes #10756
1 parent 979bbf2 commit e212c97

File tree

12 files changed

+243
-2
lines changed

12 files changed

+243
-2
lines changed

pkg/abi/linux/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ go_library(
5858
"netlink.go",
5959
"netlink_route.go",
6060
"nf_tables.go",
61+
"personality.go",
6162
"poll.go",
6263
"prctl.go",
6364
"ptrace.go",

pkg/abi/linux/personality.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2024 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package linux
16+
17+
// Personality flags, used by personality(2),
18+
// from include/uapi/linux/personality.h.
19+
const (
20+
SHORT_INODE = 0x1000000
21+
WHOLE_SECONDS = 0x2000000
22+
PER_LINUX = 0x0000
23+
PER_BSD = 0x0006
24+
)
25+
26+
// NOTE: All of the above flags are non-security-sensitive and may be copied
27+
// from parent task to child task. However, this is not the case for all
28+
// personality bits. If adding more, check PER_CLEAR_ON_SETID and ensure that
29+
// these are cleared on suid/sgid execs.

pkg/sentry/kernel/kernel.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,7 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID,
11571157
InitialCgroups: args.InitialCgroups,
11581158
UserCounters: k.GetUserCounters(args.Credentials.RealKUID),
11591159
Origin: args.Origin,
1160+
Personality: linux.PER_LINUX,
11601161
// A task with no parent starts out with no session keyring.
11611162
SessionKeyring: nil,
11621163
}

pkg/sentry/kernel/task.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,9 @@ type Task struct {
608608
// +checklocks:mu
609609
sessionKeyring *auth.Key
610610

611+
// personality is the task's personality(2) bits.
612+
personality atomicbitops.Uint32
613+
611614
// Origin is the origin of the task.
612615
Origin TaskOrigin
613616
}
@@ -869,3 +872,14 @@ func (t *Task) ResetKcov() {
869872
t.kcov = nil
870873
}
871874
}
875+
876+
// Personality returns the task's personality.
877+
func (t *Task) Personality() uint32 {
878+
return t.personality.Load()
879+
}
880+
881+
// SetPersonality sets the task's personality.
882+
// It returns the task's former personality.
883+
func (t *Task) SetPersonality(personality uint32) uint32 {
884+
return t.personality.Swap(personality)
885+
}

pkg/sentry/kernel/task_clone.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
265265
ContainerID: t.ContainerID(),
266266
UserCounters: uc,
267267
SessionKeyring: sessionKeyring,
268+
Personality: t.personality.Load(),
268269
Origin: t.Origin,
269270
}
270271
if args.Flags&linux.CLONE_THREAD == 0 {

pkg/sentry/kernel/task_start.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ type TaskConfig struct {
104104
// It may be nil.
105105
SessionKeyring *auth.Key
106106

107+
// Personality is the personality of the parent task.
108+
Personality uint32
109+
107110
Origin TaskOrigin
108111
}
109112

@@ -174,6 +177,7 @@ func (ts *TaskSet) newTask(ctx context.Context, cfg *TaskConfig) (*Task, error)
174177
cgroups: make(map[Cgroup]struct{}),
175178
userCounters: cfg.UserCounters,
176179
sessionKeyring: cfg.SessionKeyring,
180+
personality: atomicbitops.FromUint32(cfg.Personality),
177181
Origin: cfg.Origin,
178182
}
179183
t.netns = cfg.NetworkNamespace

pkg/sentry/syscalls/linux/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ go_library(
3333
"sys_mount.go",
3434
"sys_mq.go",
3535
"sys_msgqueue.go",
36+
"sys_personality.go",
3637
"sys_pipe.go",
3738
"sys_poll.go",
3839
"sys_prctl.go",

pkg/sentry/syscalls/linux/linux64.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ var AMD64 = &kernel.SyscallTable{
187187
132: syscalls.Supported("utime", Utime),
188188
133: syscalls.Supported("mknod", Mknod),
189189
134: syscalls.Error("uselib", linuxerr.ENOSYS, "Obsolete", nil),
190-
135: syscalls.ErrorWithEvent("personality", linuxerr.EINVAL, "Unable to change personality.", nil),
190+
135: syscalls.PartiallySupported("personality", Personality, "Only setting no-op personality bits and retrieving them are supported.", nil),
191191
136: syscalls.ErrorWithEvent("ustat", linuxerr.ENOSYS, "Needs filesystem support.", nil),
192192
137: syscalls.Supported("statfs", Statfs),
193193
138: syscalls.Supported("fstatfs", Fstatfs),
@@ -523,7 +523,7 @@ var ARM64 = &kernel.SyscallTable{
523523
89: syscalls.CapError("acct", linux.CAP_SYS_PACCT, "", nil),
524524
90: syscalls.Supported("capget", Capget),
525525
91: syscalls.Supported("capset", Capset),
526-
92: syscalls.ErrorWithEvent("personality", linuxerr.EINVAL, "Unable to change personality.", nil),
526+
92: syscalls.PartiallySupported("personality", Personality, "Only setting no-op personality bits and retrieving them are supported.", nil),
527527
93: syscalls.Supported("exit", Exit),
528528
94: syscalls.Supported("exit_group", ExitGroup),
529529
95: syscalls.Supported("waitid", Waitid),
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2024 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package linux
16+
17+
import (
18+
"gvisor.dev/gvisor/pkg/abi/linux"
19+
"gvisor.dev/gvisor/pkg/errors/linuxerr"
20+
"gvisor.dev/gvisor/pkg/sentry/arch"
21+
"gvisor.dev/gvisor/pkg/sentry/kernel"
22+
)
23+
24+
const (
25+
// getPersonality may be passed to `personality(2)` to get the current
26+
// personality bits without modifying them.
27+
getPersonality = 0xffffffff
28+
)
29+
30+
// Personality implements Linux syscall personality(2).
31+
func Personality(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
32+
// allowedPersonalityBits are the personality bits that are allowed to be set.
33+
const allowedPersonalityBits = linux.PER_LINUX | linux.PER_BSD | linux.SHORT_INODE | linux.WHOLE_SECONDS
34+
35+
personality := args[0].Uint()
36+
if personality == getPersonality {
37+
return uintptr(t.Personality()), nil, nil
38+
}
39+
if personality&allowedPersonalityBits != personality {
40+
return 0, nil, linuxerr.EINVAL
41+
}
42+
return uintptr(t.SetPersonality(personality)), nil, nil
43+
}

test/syscalls/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,10 @@ syscall_test(
479479
test = "//test/syscalls/linux:pause_test",
480480
)
481481

482+
syscall_test(
483+
test = "//test/syscalls/linux:personality_test",
484+
)
485+
482486
syscall_test(
483487
size = "medium",
484488
add_hostinet = True,

test/syscalls/linux/BUILD

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,20 @@ cc_binary(
16701670
],
16711671
)
16721672

1673+
cc_binary(
1674+
name = "personality_test",
1675+
testonly = 1,
1676+
srcs = ["personality.cc"],
1677+
linkstatic = 1,
1678+
malloc = "//test/util:errno_safe_allocator",
1679+
deps = select_gtest() + [
1680+
"//test/util:multiprocess_util",
1681+
"//test/util:posix_error",
1682+
"//test/util:test_main",
1683+
"//test/util:test_util",
1684+
],
1685+
)
1686+
16731687
cc_binary(
16741688
name = "ping_socket_test",
16751689
testonly = 1,

test/syscalls/linux/personality.cc

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2024 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <sys/personality.h>
16+
17+
#include "gtest/gtest.h"
18+
#include "test/util/multiprocess_util.h"
19+
#include "test/util/posix_error.h"
20+
#include "test/util/test_util.h"
21+
22+
namespace gvisor {
23+
namespace testing {
24+
25+
namespace {
26+
27+
constexpr uint64_t kGetPersonality = 0xffffffff;
28+
constexpr uint64_t kUndefinedPersonalityBit = 0x00001000;
29+
30+
TEST(PersonalityTest, DefaultPersonality) {
31+
EXPECT_THAT(personality(kGetPersonality), SyscallSucceedsWithValue(PER_LINUX));
32+
}
33+
34+
TEST(PersonalityTest, SetLinux) {
35+
EXPECT_THAT(personality(kGetPersonality), SyscallSucceedsWithValue(PER_LINUX));
36+
EXPECT_THAT(InForkedProcess([&] {
37+
TEST_CHECK(personality(PER_LINUX) == PER_LINUX);
38+
TEST_CHECK(personality(kGetPersonality) == PER_LINUX);
39+
TEST_CHECK(personality(kGetPersonality) == PER_LINUX);
40+
}), IsPosixErrorOkAndHolds(0));
41+
EXPECT_THAT(personality(kGetPersonality), SyscallSucceedsWithValue(PER_LINUX));
42+
}
43+
44+
TEST(PersonalityTest, SetBSD) {
45+
EXPECT_THAT(personality(kGetPersonality), SyscallSucceedsWithValue(PER_LINUX));
46+
EXPECT_THAT(InForkedProcess([&] {
47+
TEST_CHECK(personality(PER_BSD) == PER_LINUX);
48+
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
49+
}), IsPosixErrorOkAndHolds(0));
50+
}
51+
52+
TEST(PersonalityTest, PersonalityIsInheritable) {
53+
EXPECT_THAT(InForkedProcess([&] {
54+
TEST_CHECK(personality(PER_BSD) == PER_LINUX);
55+
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
56+
TEST_CHECK(InForkedProcess([&] {
57+
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
58+
}).ok());
59+
}), IsPosixErrorOkAndHolds(0));
60+
}
61+
62+
TEST(PersonalityTest, ChildPersonalityDoesNotAffectParent) {
63+
EXPECT_THAT(InForkedProcess([&] {
64+
TEST_CHECK(personality(PER_BSD) == PER_LINUX);
65+
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
66+
TEST_CHECK(InForkedProcess([&] {
67+
TEST_CHECK(personality(PER_SOLARIS) == PER_BSD);
68+
TEST_CHECK(personality(kGetPersonality) == PER_SOLARIS);
69+
}).ok());
70+
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
71+
}), IsPosixErrorOkAndHolds(0));
72+
TEST_CHECK(personality(kGetPersonality) == PER_LINUX);
73+
}
74+
75+
TEST(PersonalityTest, SetShortInode) {
76+
EXPECT_THAT(InForkedProcess([&] {
77+
TEST_CHECK(personality(SHORT_INODE) == PER_LINUX);
78+
TEST_CHECK(personality(kGetPersonality) == SHORT_INODE);
79+
}), IsPosixErrorOkAndHolds(0));
80+
}
81+
82+
TEST(PersonalityTest, SetWholeSeconds) {
83+
EXPECT_THAT(InForkedProcess([&] {
84+
TEST_CHECK(personality(WHOLE_SECONDS) == PER_LINUX);
85+
TEST_CHECK(personality(kGetPersonality) == WHOLE_SECONDS);
86+
}), IsPosixErrorOkAndHolds(0));
87+
}
88+
89+
TEST(PersonalityTest, SetMultiple) {
90+
EXPECT_THAT(InForkedProcess([&] {
91+
TEST_CHECK(personality(PER_BSD | SHORT_INODE | WHOLE_SECONDS) == PER_LINUX);
92+
TEST_CHECK(personality(kGetPersonality) == (PER_BSD | SHORT_INODE | WHOLE_SECONDS));
93+
}), IsPosixErrorOkAndHolds(0));
94+
}
95+
96+
TEST(PersonalityTest, SetThenUnset) {
97+
EXPECT_THAT(InForkedProcess([&] {
98+
TEST_CHECK(personality(PER_BSD | SHORT_INODE) == PER_LINUX);
99+
TEST_CHECK(personality(WHOLE_SECONDS | SHORT_INODE) == (PER_BSD | SHORT_INODE));
100+
TEST_CHECK(personality(PER_BSD) == (WHOLE_SECONDS | SHORT_INODE));
101+
TEST_CHECK(personality(kGetPersonality) == PER_BSD);
102+
}), IsPosixErrorOkAndHolds(0));
103+
}
104+
105+
TEST(PersonalityTest, UnsupportedPersonalityBitsAreRejected) {
106+
SKIP_IF(!IsRunningOnGvisor());
107+
EXPECT_THAT(personality(kGetPersonality), SyscallSucceedsWithValue(PER_LINUX));
108+
EXPECT_THAT(personality(MMAP_PAGE_ZERO), SyscallFailsWithErrno(EINVAL));
109+
EXPECT_THAT(personality(ADDR_LIMIT_3GB), SyscallFailsWithErrno(EINVAL));
110+
EXPECT_THAT(personality(ADDR_LIMIT_32BIT), SyscallFailsWithErrno(EINVAL));
111+
EXPECT_THAT(personality(ADDR_NO_RANDOMIZE), SyscallFailsWithErrno(EINVAL));
112+
EXPECT_THAT(personality(ADDR_COMPAT_LAYOUT), SyscallFailsWithErrno(EINVAL));
113+
EXPECT_THAT(personality(READ_IMPLIES_EXEC), SyscallFailsWithErrno(EINVAL));
114+
EXPECT_THAT(personality(STICKY_TIMEOUTS), SyscallFailsWithErrno(EINVAL));
115+
EXPECT_THAT(personality(UNAME26), SyscallFailsWithErrno(EINVAL));
116+
EXPECT_THAT(personality(FDPIC_FUNCPTRS), SyscallFailsWithErrno(EINVAL));
117+
EXPECT_THAT(personality(PER_LINUX32), SyscallFailsWithErrno(EINVAL));
118+
EXPECT_THAT(personality(PER_LINUX32_3GB), SyscallFailsWithErrno(EINVAL));
119+
EXPECT_THAT(personality(PER_LINUX_32BIT), SyscallFailsWithErrno(EINVAL));
120+
EXPECT_THAT(personality(PER_LINUX_FDPIC), SyscallFailsWithErrno(EINVAL));
121+
EXPECT_THAT(personality(PER_RISCOS), SyscallFailsWithErrno(EINVAL));
122+
EXPECT_THAT(personality(PER_SOLARIS), SyscallFailsWithErrno(EINVAL));
123+
EXPECT_THAT(personality(kGetPersonality), SyscallSucceedsWithValue(PER_LINUX));
124+
}
125+
126+
} // namespace
127+
128+
} // namespace testing
129+
} // namespace gvisor

0 commit comments

Comments
 (0)