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