Allow threads with CAP_SYS_RESOURCE to raise hard rlimits.

PiperOrigin-RevId: 242919489
Change-Id: Ie3267b3bcd8a54b54bc16a6556369a19e843376f
This commit is contained in:
Kevin Krakauer
2019-04-10 12:36:45 -07:00
committed by Shentubot
parent 0a0619216e
commit f7aff0aaa4
7 changed files with 48 additions and 25 deletions
+4 -4
View File
@@ -40,7 +40,7 @@ func newTestFDMap() *FDMap {
func TestFDMapMany(t *testing.T) {
file := filetest.NewTestFile(t)
limitSet := limits.NewLimitSet()
limitSet.Set(limits.NumberOfFiles, limits.Limit{maxFD, maxFD})
limitSet.Set(limits.NumberOfFiles, limits.Limit{maxFD, maxFD}, true /* privileged */)
f := newTestFDMap()
for i := 0; i < maxFD; i++ {
@@ -64,7 +64,7 @@ func TestFDMapMany(t *testing.T) {
func TestFDMap(t *testing.T) {
file := filetest.NewTestFile(t)
limitSet := limits.NewLimitSet()
limitSet.Set(limits.NumberOfFiles, limits.Limit{1, maxFD})
limitSet.Set(limits.NumberOfFiles, limits.Limit{1, maxFD}, true /* privileged */)
f := newTestFDMap()
if _, err := f.NewFDFrom(0, file, FDFlags{}, limitSet); err != nil {
@@ -76,7 +76,7 @@ func TestFDMap(t *testing.T) {
}
largeLimit := limits.Limit{maxFD, maxFD}
limitSet.Set(limits.NumberOfFiles, largeLimit)
limitSet.Set(limits.NumberOfFiles, largeLimit, true /* privileged */)
if fd, err := f.NewFDFrom(0, file, FDFlags{}, limitSet); err != nil {
t.Fatalf("Adding an FD to a resized map: got %v, want nil", err)
@@ -117,7 +117,7 @@ func TestDescriptorFlags(t *testing.T) {
file := filetest.NewTestFile(t)
f := newTestFDMap()
limitSet := limits.NewLimitSet()
limitSet.Set(limits.NumberOfFiles, limits.Limit{maxFD, maxFD})
limitSet.Set(limits.NumberOfFiles, limits.Limit{maxFD, maxFD}, true /* privileged */)
origFlags := FDFlags{CloseOnExec: true}
+6 -2
View File
@@ -113,13 +113,17 @@ func (l *LimitSet) SetUnchecked(t LimitType, v Limit) {
}
// Set assigns value v to resource of LimitType t and returns the old value.
func (l *LimitSet) Set(t LimitType, v Limit) (Limit, error) {
// privileged should be true only when either the caller has CAP_SYS_RESOURCE
// or when creating limits for a new kernel.
func (l *LimitSet) Set(t LimitType, v Limit, privileged bool) (Limit, error) {
l.mu.Lock()
defer l.mu.Unlock()
// If a limit is already set, make sure the new limit doesn't
// exceed the previous max limit.
if _, ok := l.data[t]; ok {
if l.data[t].Max < v.Max {
// Unprivileged users can only lower their hard limits.
if l.data[t].Max < v.Max && !privileged {
return Limit{}, syscall.EPERM
}
if v.Cur > v.Max {
+18 -12
View File
@@ -20,18 +20,24 @@ import (
)
func TestSet(t *testing.T) {
testCases := []struct {
limit Limit
privileged bool
expectedErr error
}{
{limit: Limit{Cur: 50, Max: 50}, privileged: false, expectedErr: nil},
{limit: Limit{Cur: 20, Max: 50}, privileged: false, expectedErr: nil},
{limit: Limit{Cur: 20, Max: 60}, privileged: false, expectedErr: syscall.EPERM},
{limit: Limit{Cur: 60, Max: 50}, privileged: false, expectedErr: syscall.EINVAL},
{limit: Limit{Cur: 11, Max: 10}, privileged: false, expectedErr: syscall.EINVAL},
{limit: Limit{Cur: 20, Max: 60}, privileged: true, expectedErr: nil},
}
ls := NewLimitSet()
ls.Set(1, Limit{Cur: 50, Max: 50})
if _, err := ls.Set(1, Limit{Cur: 20, Max: 50}); err != nil {
t.Fatalf("Tried to lower Limit to valid new value: got %v, wanted nil", err)
}
if _, err := ls.Set(1, Limit{Cur: 20, Max: 60}); err != syscall.EPERM {
t.Fatalf("Tried to raise limit.Max to invalid higher value: got %v, wanted syscall.EPERM", err)
}
if _, err := ls.Set(1, Limit{Cur: 60, Max: 50}); err != syscall.EINVAL {
t.Fatalf("Tried to raise limit.Cur to invalid higher value: got %v, wanted syscall.EINVAL", err)
}
if _, err := ls.Set(1, Limit{Cur: 11, Max: 10}); err != syscall.EINVAL {
t.Fatalf("Tried to set new limit with Cur > Max: got %v, wanted syscall.EINVAL", err)
for _, tc := range testCases {
if _, err := ls.Set(1, tc.limit, tc.privileged); err != tc.expectedErr {
t.Fatalf("Tried to set Limit to %+v and privilege %t: got %v, wanted %v", tc.limit, tc.privileged, err, tc.expectedErr)
}
}
}
+1 -1
View File
@@ -95,6 +95,6 @@ func NewLinuxDistroLimitSet() (*LimitSet, error) {
// 1,048,576 ought to be enough for anyone.
l := ls.Get(ProcessCount)
l.Cur = 1 << 20
ls.Set(ProcessCount, l)
ls.Set(ProcessCount, l, true /* privileged */)
return ls, nil
}
+1 -1
View File
@@ -70,7 +70,7 @@ func TestUsageASUpdates(t *testing.T) {
func TestBrkDataLimitUpdates(t *testing.T) {
limitSet := limits.NewLimitSet()
limitSet.Set(limits.Data, limits.Limit{}) // zero RLIMIT_DATA
limitSet.Set(limits.Data, limits.Limit{}, true /* privileged */) // zero RLIMIT_DATA
ctx := contexttest.WithLimitSet(contexttest.Context(t), limitSet)
mm := testMemoryManager(ctx)
+7 -1
View File
@@ -106,7 +106,13 @@ func prlimit64(t *kernel.Task, resource limits.LimitType, newLim *limits.Limit)
if _, ok := setableLimits[resource]; !ok {
return limits.Limit{}, syserror.EPERM
}
oldLim, err := t.ThreadGroup().Limits().Set(resource, *newLim)
// "A privileged process (under Linux: one with the CAP_SYS_RESOURCE
// capability in the initial user namespace) may make arbitrary changes
// to either limit value."
privileged := t.HasCapabilityIn(linux.CAP_SYS_RESOURCE, t.Kernel().RootUserNamespace())
oldLim, err := t.ThreadGroup().Limits().Set(resource, *newLim, privileged)
if err != nil {
return limits.Limit{}, err
}
+11 -4
View File
@@ -25,15 +25,12 @@ namespace {
TEST(RlimitTest, SetRlimitHigher) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE)));
SKIP_IF(!IsRunningOnGvisor());
struct rlimit rl = {};
EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
// TODO: Even with CAP_SYS_RESOURCE, gVisor does not allow
// setting a higher rlimit.
rl.rlim_max++;
EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallFailsWithErrno(EPERM));
EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
}
TEST(RlimitTest, UnprivilegedSetRlimit) {
@@ -56,6 +53,16 @@ TEST(RlimitTest, UnprivilegedSetRlimit) {
EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallFailsWithErrno(EPERM));
}
TEST(RlimitTest, SetSoftRlimitAboveHard) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE)));
struct rlimit rl = {};
EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
rl.rlim_cur = rl.rlim_max + 1;
EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallFailsWithErrno(EINVAL));
}
} // namespace
} // namespace testing