Skip to content

Commit 2a3e16f

Browse files
milantracygvisor-bot
authored andcommitted
Implement /proc/sys/kernel/keys/maxkeys.
PiperOrigin-RevId: 836764402
1 parent fc40e23 commit 2a3e16f

File tree

7 files changed

+121
-5
lines changed

7 files changed

+121
-5
lines changed

pkg/sentry/fsimpl/proc/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ go_library(
7575
"fd_dir_inode_refs.go",
7676
"fd_info_dir_inode_refs.go",
7777
"filesystem.go",
78+
"keys.go",
7879
"proc_impl.go",
7980
"subtasks.go",
8081
"subtasks_inode_refs.go",

pkg/sentry/fsimpl/proc/keys.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2025 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 proc
16+
17+
import (
18+
"bytes"
19+
"fmt"
20+
21+
"gvisor.dev/gvisor/pkg/abi/linux"
22+
"gvisor.dev/gvisor/pkg/atomicbitops"
23+
"gvisor.dev/gvisor/pkg/context"
24+
"gvisor.dev/gvisor/pkg/errors/linuxerr"
25+
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
26+
"gvisor.dev/gvisor/pkg/sentry/kernel"
27+
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
28+
"gvisor.dev/gvisor/pkg/sentry/vfs"
29+
"gvisor.dev/gvisor/pkg/usermem"
30+
)
31+
32+
func (fs *filesystem) newMaxKeySizeFile(ctx context.Context, k *kernel.Kernel, creds *auth.Credentials) kernfs.Inode {
33+
s := &maxKeySize{maxKeys: &k.MaxKeySetSize}
34+
s.Init(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), s, 0644)
35+
return s
36+
}
37+
38+
// maxKeySize implements vfs.WritableDynamicBytesSource for
39+
// /proc/sys/kernel/keys/maxkeys.
40+
//
41+
// +stateify savable
42+
type maxKeySize struct {
43+
kernfs.DynamicBytesFile
44+
45+
// maxKeys is the maximum number of keys allowed in a key set.
46+
maxKeys *atomicbitops.Int32
47+
}
48+
49+
var _ vfs.WritableDynamicBytesSource = (*maxKeySize)(nil)
50+
51+
// Generate implements vfs.DynamicBytesSource.Generate.
52+
func (s *maxKeySize) Generate(ctx context.Context, buf *bytes.Buffer) error {
53+
_, err := fmt.Fprintf(buf, "%d\n", s.maxKeys.Load())
54+
return err
55+
}
56+
57+
// Write implements vfs.WritableDynamicBytesSource.Write.
58+
func (s *maxKeySize) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) {
59+
if offset != 0 {
60+
// Ignore partial writes.
61+
return 0, linuxerr.EINVAL
62+
}
63+
if !auth.CredentialsFromContext(ctx).HasCapability(linux.CAP_SYS_ADMIN) {
64+
return 0, linuxerr.EPERM
65+
}
66+
buf := make([]int32, 1)
67+
n, err := ParseInt32Vec(ctx, src, buf)
68+
if err != nil || n == 0 {
69+
return 0, err
70+
}
71+
72+
if buf[0] <= 0 {
73+
return 0, linuxerr.EINVAL
74+
}
75+
76+
s.maxKeys.Store(buf[0])
77+
return n, nil
78+
}

pkg/sentry/fsimpl/proc/tasks_sys.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ func (fs *filesystem) newSysDir(ctx context.Context, root *auth.Credentials, k *
6666
"yama": fs.newStaticDir(ctx, root, map[string]kernfs.Inode{
6767
"ptrace_scope": fs.newYAMAPtraceScopeFile(ctx, k, root),
6868
}),
69+
"keys": fs.newStaticDir(ctx, root, map[string]kernfs.Inode{
70+
"maxkeys": fs.newMaxKeySizeFile(ctx, k, root),
71+
}),
6972
}),
7073
"fs": fs.newStaticDir(ctx, root, map[string]kernfs.Inode{
7174
"nr_open": fs.newInode(ctx, root, 0644, &atomicInt32File{val: &k.MaxFDLimit, min: 8, max: kernel.MaxFdLimit}),

pkg/sentry/kernel/auth/key.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ const (
6363
// Corresponds to `KEY_MAX_DESC_SIZE` in Linux.
6464
MaxKeyDescSize = 4096
6565

66-
// maxSetSize is the maximum number of a keys in a `Set`.
66+
// MaxSetSize is the default maximum number of keys in a `KeySet`.
6767
// By default, Linux limits this number to 200 per non-root user.
6868
// Here, we limit it to 200 per Set, which is stricter.
69-
maxSetSize = 200
69+
MaxSetSize = 200
7070
)
7171

7272
// Key represents a key in the keyrings subsystem.
@@ -361,13 +361,13 @@ func getNewID() (KeySerial, error) {
361361
}
362362

363363
// Add adds a new Key to the KeySet.
364-
func (s *LockedKeySet) Add(description string, creds *Credentials, perms KeyPermissions) (*Key, error) {
364+
func (s *LockedKeySet) Add(description string, creds *Credentials, perms KeyPermissions, keySizeLimit int) (*Key, error) {
365365
if len(description) >= MaxKeyDescSize {
366366
return nil, linuxerr.EINVAL
367367
}
368368
s.mu.Lock()
369369
defer s.mu.Unlock()
370-
if len(s.keys) >= maxSetSize {
370+
if len(s.keys) >= keySizeLimit {
371371
return nil, linuxerr.EDQUOT
372372
}
373373
newID, err := getNewID()

pkg/sentry/kernel/kernel.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,9 @@ type Kernel struct {
401401

402402
// AllowSUID determines if the SUID/SGID bits are honored during execve.
403403
AllowSUID bool
404+
405+
// MaxKeySetSize is the maximum number of keys in a key set.
406+
MaxKeySetSize atomicbitops.Int32
404407
}
405408

406409
// InitKernelArgs holds arguments to Init.
@@ -581,6 +584,7 @@ func (k *Kernel) Init(args InitKernelArgs) error {
581584

582585
k.cgroupRegistry = newCgroupRegistry()
583586
k.UnixSocketOpts = args.UnixSocketOpts
587+
k.MaxKeySetSize = atomicbitops.FromInt32(auth.MaxSetSize)
584588
return nil
585589
}
586590

pkg/sentry/kernel/task_key.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (t *Task) joinNewSessionKeyringLocked(newKeyDesc string, newKeyPerms auth.K
4848
err := t.UserNamespace().Keys.Do(func(keySet *auth.LockedKeySet) error {
4949
creds := t.Credentials()
5050
var err error
51-
sessionKeyring, err = keySet.Add(newKeyDesc, creds, newKeyPerms)
51+
sessionKeyring, err = keySet.Add(newKeyDesc, creds, newKeyPerms, int(t.Kernel().MaxKeySetSize.Load()))
5252
return err
5353
})
5454
if err != nil {

test/syscalls/linux/proc.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#include "test/util/eventfd_util.h"
7272
#include "test/util/file_descriptor.h"
7373
#include "test/util/fs_util.h"
74+
#include "test/util/linux_capability_util.h"
7475
#include "test/util/memory_util.h"
7576
#include "test/util/mount_util.h"
7677
#include "test/util/multiprocess_util.h"
@@ -3085,6 +3086,35 @@ TEST(ProcFilesystems, OverflowID) {
30853086
EXPECT_EQ(overflowUid, defaultOverflowID);
30863087
}
30873088

3089+
TEST(ProcSysKernelKeysMax, Exists) {
3090+
auto maxkeys =
3091+
ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/keys/maxkeys"));
3092+
int32_t mk;
3093+
ASSERT_TRUE(absl::SimpleAtoi(maxkeys, &mk));
3094+
EXPECT_EQ(mk, 200);
3095+
}
3096+
3097+
TEST(ProcSysKernelKeysMax, InvalidMaxKeysValue) {
3098+
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
3099+
ASSERT_THAT(SetContents("/proc/sys/kernel/keys/maxkeys", "-1"),
3100+
PosixErrorIs(EINVAL));
3101+
auto maxkeys =
3102+
ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/keys/maxkeys"));
3103+
int32_t mk;
3104+
ASSERT_TRUE(absl::SimpleAtoi(maxkeys, &mk));
3105+
EXPECT_EQ(mk, 200);
3106+
}
3107+
3108+
TEST(ProcSysKernelKeysMax, SetMaxKeys) {
3109+
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
3110+
ASSERT_NO_ERRNO(SetContents("/proc/sys/kernel/keys/maxkeys", "100"));
3111+
auto maxkeys =
3112+
ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/keys/maxkeys"));
3113+
int32_t mk;
3114+
ASSERT_TRUE(absl::SimpleAtoi(maxkeys, &mk));
3115+
EXPECT_EQ(mk, 100);
3116+
}
3117+
30883118
} // namespace
30893119
} // namespace testing
30903120
} // namespace gvisor

0 commit comments

Comments
 (0)