github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/linux/sys_rlimit.go (about)

     1  // Copyright 2018 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  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    19  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    20  	"github.com/SagerNet/gvisor/pkg/hostarch"
    21  	"github.com/SagerNet/gvisor/pkg/marshal"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/arch"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/limits"
    25  	"github.com/SagerNet/gvisor/pkg/syserror"
    26  )
    27  
    28  // rlimit describes an implementation of 'struct rlimit', which may vary from
    29  // system-to-system.
    30  type rlimit interface {
    31  	marshal.Marshallable
    32  
    33  	// toLimit converts an rlimit to a limits.Limit.
    34  	toLimit() *limits.Limit
    35  
    36  	// fromLimit converts a limits.Limit to an rlimit.
    37  	fromLimit(lim limits.Limit)
    38  }
    39  
    40  // newRlimit returns the appropriate rlimit type for 'struct rlimit' on this system.
    41  func newRlimit(t *kernel.Task) (rlimit, error) {
    42  	switch t.Arch().Width() {
    43  	case 8:
    44  		// On 64-bit system, struct rlimit and struct rlimit64 are identical.
    45  		return &rlimit64{}, nil
    46  	default:
    47  		return nil, syserror.ENOSYS
    48  	}
    49  }
    50  
    51  // +marshal
    52  type rlimit64 struct {
    53  	Cur uint64
    54  	Max uint64
    55  }
    56  
    57  func (r *rlimit64) toLimit() *limits.Limit {
    58  	return &limits.Limit{
    59  		Cur: limits.FromLinux(r.Cur),
    60  		Max: limits.FromLinux(r.Max),
    61  	}
    62  }
    63  
    64  func (r *rlimit64) fromLimit(lim limits.Limit) {
    65  	*r = rlimit64{
    66  		Cur: limits.ToLinux(lim.Cur),
    67  		Max: limits.ToLinux(lim.Max),
    68  	}
    69  }
    70  
    71  func (r *rlimit64) copyIn(t *kernel.Task, addr hostarch.Addr) error {
    72  	_, err := r.CopyIn(t, addr)
    73  	return err
    74  }
    75  
    76  func (r *rlimit64) copyOut(t *kernel.Task, addr hostarch.Addr) error {
    77  	_, err := r.CopyOut(t, addr)
    78  	return err
    79  }
    80  
    81  func makeRlimit64(lim limits.Limit) *rlimit64 {
    82  	return &rlimit64{Cur: lim.Cur, Max: lim.Max}
    83  }
    84  
    85  // setableLimits is the set of supported setable limits.
    86  var setableLimits = map[limits.LimitType]struct{}{
    87  	limits.NumberOfFiles: {},
    88  	limits.AS:            {},
    89  	limits.CPU:           {},
    90  	limits.Data:          {},
    91  	limits.FileSize:      {},
    92  	limits.MemoryLocked:  {},
    93  	limits.Stack:         {},
    94  	// RSS can be set, but it's not enforced because Linux doesn't enforce it
    95  	// either: "This limit has effect only in Linux 2.4.x, x < 30"
    96  	limits.Rss: {},
    97  	// These are not enforced, but we include them here to avoid returning
    98  	// EPERM, since some apps expect them to succeed.
    99  	limits.Core:         {},
   100  	limits.ProcessCount: {},
   101  }
   102  
   103  func prlimit64(t *kernel.Task, resource limits.LimitType, newLim *limits.Limit) (limits.Limit, error) {
   104  	if newLim == nil {
   105  		return t.ThreadGroup().Limits().Get(resource), nil
   106  	}
   107  
   108  	if _, ok := setableLimits[resource]; !ok {
   109  		return limits.Limit{}, linuxerr.EPERM
   110  	}
   111  
   112  	// "A privileged process (under Linux: one with the CAP_SYS_RESOURCE
   113  	// capability in the initial user namespace) may make arbitrary changes
   114  	// to either limit value."
   115  	privileged := t.HasCapabilityIn(linux.CAP_SYS_RESOURCE, t.Kernel().RootUserNamespace())
   116  
   117  	oldLim, err := t.ThreadGroup().Limits().Set(resource, *newLim, privileged)
   118  	if err != nil {
   119  		return limits.Limit{}, err
   120  	}
   121  
   122  	if resource == limits.CPU {
   123  		t.NotifyRlimitCPUUpdated()
   124  	}
   125  	return oldLim, nil
   126  }
   127  
   128  // Getrlimit implements linux syscall getrlimit(2).
   129  func Getrlimit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   130  	resource, ok := limits.FromLinuxResource[int(args[0].Int())]
   131  	if !ok {
   132  		// Return err; unknown limit.
   133  		return 0, nil, linuxerr.EINVAL
   134  	}
   135  	addr := args[1].Pointer()
   136  	rlim, err := newRlimit(t)
   137  	if err != nil {
   138  		return 0, nil, err
   139  	}
   140  	lim, err := prlimit64(t, resource, nil)
   141  	if err != nil {
   142  		return 0, nil, err
   143  	}
   144  	rlim.fromLimit(lim)
   145  	_, err = rlim.CopyOut(t, addr)
   146  	return 0, nil, err
   147  }
   148  
   149  // Setrlimit implements linux syscall setrlimit(2).
   150  func Setrlimit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   151  	resource, ok := limits.FromLinuxResource[int(args[0].Int())]
   152  	if !ok {
   153  		// Return err; unknown limit.
   154  		return 0, nil, linuxerr.EINVAL
   155  	}
   156  	addr := args[1].Pointer()
   157  	rlim, err := newRlimit(t)
   158  	if err != nil {
   159  		return 0, nil, err
   160  	}
   161  	if _, err := rlim.CopyIn(t, addr); err != nil {
   162  		return 0, nil, syserror.EFAULT
   163  	}
   164  	_, err = prlimit64(t, resource, rlim.toLimit())
   165  	return 0, nil, err
   166  }
   167  
   168  // Prlimit64 implements linux syscall prlimit64(2).
   169  func Prlimit64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
   170  	tid := kernel.ThreadID(args[0].Int())
   171  	resource, ok := limits.FromLinuxResource[int(args[1].Int())]
   172  	if !ok {
   173  		// Return err; unknown limit.
   174  		return 0, nil, linuxerr.EINVAL
   175  	}
   176  	newRlimAddr := args[2].Pointer()
   177  	oldRlimAddr := args[3].Pointer()
   178  
   179  	var newLim *limits.Limit
   180  	if newRlimAddr != 0 {
   181  		var nrl rlimit64
   182  		if err := nrl.copyIn(t, newRlimAddr); err != nil {
   183  			return 0, nil, syserror.EFAULT
   184  		}
   185  		newLim = nrl.toLimit()
   186  	}
   187  
   188  	if tid < 0 {
   189  		return 0, nil, linuxerr.EINVAL
   190  	}
   191  	ot := t
   192  	if tid > 0 {
   193  		if ot = t.PIDNamespace().TaskWithID(tid); ot == nil {
   194  			return 0, nil, syserror.ESRCH
   195  		}
   196  	}
   197  
   198  	// "To set or get the resources of a process other than itself, the caller
   199  	// must have the CAP_SYS_RESOURCE capability, or the real, effective, and
   200  	// saved set user IDs of the target process must match the real user ID of
   201  	// the caller and the real, effective, and saved set group IDs of the
   202  	// target process must match the real group ID of the caller."
   203  	if ot != t && !t.HasCapabilityIn(linux.CAP_SYS_RESOURCE, t.PIDNamespace().UserNamespace()) {
   204  		cred, tcred := t.Credentials(), ot.Credentials()
   205  		if cred.RealKUID != tcred.RealKUID ||
   206  			cred.RealKUID != tcred.EffectiveKUID ||
   207  			cred.RealKUID != tcred.SavedKUID ||
   208  			cred.RealKGID != tcred.RealKGID ||
   209  			cred.RealKGID != tcred.EffectiveKGID ||
   210  			cred.RealKGID != tcred.SavedKGID {
   211  			return 0, nil, linuxerr.EPERM
   212  		}
   213  	}
   214  
   215  	oldLim, err := prlimit64(ot, resource, newLim)
   216  	if err != nil {
   217  		return 0, nil, err
   218  	}
   219  
   220  	if oldRlimAddr != 0 {
   221  		if err := makeRlimit64(oldLim).copyOut(t, oldRlimAddr); err != nil {
   222  			return 0, nil, syserror.EFAULT
   223  		}
   224  	}
   225  
   226  	return 0, nil, nil
   227  }