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 }