github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/boot/limits.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 boot 16 17 import ( 18 "fmt" 19 20 specs "github.com/opencontainers/runtime-spec/specs-go" 21 "golang.org/x/sys/unix" 22 "github.com/SagerNet/gvisor/pkg/log" 23 "github.com/SagerNet/gvisor/pkg/sentry/limits" 24 "github.com/SagerNet/gvisor/pkg/sync" 25 ) 26 27 // Mapping from linux resource names to limits.LimitType. 28 var fromLinuxResource = map[string]limits.LimitType{ 29 "RLIMIT_AS": limits.AS, 30 "RLIMIT_CORE": limits.Core, 31 "RLIMIT_CPU": limits.CPU, 32 "RLIMIT_DATA": limits.Data, 33 "RLIMIT_FSIZE": limits.FileSize, 34 "RLIMIT_LOCKS": limits.Locks, 35 "RLIMIT_MEMLOCK": limits.MemoryLocked, 36 "RLIMIT_MSGQUEUE": limits.MessageQueueBytes, 37 "RLIMIT_NICE": limits.Nice, 38 "RLIMIT_NOFILE": limits.NumberOfFiles, 39 "RLIMIT_NPROC": limits.ProcessCount, 40 "RLIMIT_RSS": limits.Rss, 41 "RLIMIT_RTPRIO": limits.RealTimePriority, 42 "RLIMIT_RTTIME": limits.Rttime, 43 "RLIMIT_SIGPENDING": limits.SignalsPending, 44 "RLIMIT_STACK": limits.Stack, 45 } 46 47 func findName(lt limits.LimitType) string { 48 for k, v := range fromLinuxResource { 49 if v == lt { 50 return k 51 } 52 } 53 return "unknown" 54 } 55 56 var defaults defs 57 58 type defs struct { 59 mu sync.Mutex 60 set *limits.LimitSet 61 err error 62 } 63 64 func (d *defs) get() (*limits.LimitSet, error) { 65 d.mu.Lock() 66 defer d.mu.Unlock() 67 68 if d.err != nil { 69 return nil, d.err 70 } 71 if d.set == nil { 72 if err := d.initDefaults(); err != nil { 73 d.err = err 74 return nil, err 75 } 76 } 77 return d.set, nil 78 } 79 80 func (d *defs) initDefaults() error { 81 ls, err := limits.NewLinuxLimitSet() 82 if err != nil { 83 return err 84 } 85 86 // Set default limits based on what containers get by default, ex: 87 // $ docker run --rm debian prlimit 88 ls.SetUnchecked(limits.AS, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 89 ls.SetUnchecked(limits.Core, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 90 ls.SetUnchecked(limits.CPU, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 91 ls.SetUnchecked(limits.Data, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 92 ls.SetUnchecked(limits.FileSize, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 93 ls.SetUnchecked(limits.Locks, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 94 ls.SetUnchecked(limits.MemoryLocked, limits.Limit{Cur: 65536, Max: 65536}) 95 ls.SetUnchecked(limits.MessageQueueBytes, limits.Limit{Cur: 819200, Max: 819200}) 96 ls.SetUnchecked(limits.Nice, limits.Limit{Cur: 0, Max: 0}) 97 ls.SetUnchecked(limits.NumberOfFiles, limits.Limit{Cur: 1048576, Max: 1048576}) 98 ls.SetUnchecked(limits.ProcessCount, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 99 ls.SetUnchecked(limits.Rss, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 100 ls.SetUnchecked(limits.RealTimePriority, limits.Limit{Cur: 0, Max: 0}) 101 ls.SetUnchecked(limits.Rttime, limits.Limit{Cur: limits.Infinity, Max: limits.Infinity}) 102 ls.SetUnchecked(limits.SignalsPending, limits.Limit{Cur: 0, Max: 0}) 103 ls.SetUnchecked(limits.Stack, limits.Limit{Cur: 8388608, Max: limits.Infinity}) 104 105 // Read host limits that directly affect the sandbox and adjust the defaults 106 // based on them. 107 for _, res := range []int{unix.RLIMIT_FSIZE, unix.RLIMIT_NOFILE} { 108 var hl unix.Rlimit 109 if err := unix.Getrlimit(res, &hl); err != nil { 110 return err 111 } 112 113 lt, ok := limits.FromLinuxResource[res] 114 if !ok { 115 return fmt.Errorf("unknown rlimit type %v", res) 116 } 117 hostLimit := limits.Limit{ 118 Cur: limits.FromLinux(hl.Cur), 119 Max: limits.FromLinux(hl.Max), 120 } 121 122 defaultLimit := ls.Get(lt) 123 if hostLimit.Cur != limits.Infinity && hostLimit.Cur < defaultLimit.Cur { 124 log.Warningf("Host limit is lower than recommended, resource: %q, host: %d, recommended: %d", findName(lt), hostLimit.Cur, defaultLimit.Cur) 125 } 126 if hostLimit.Cur != defaultLimit.Cur || hostLimit.Max != defaultLimit.Max { 127 log.Infof("Setting limit from host, resource: %q {soft: %d, hard: %d}", findName(lt), hostLimit.Cur, hostLimit.Max) 128 ls.SetUnchecked(lt, hostLimit) 129 } 130 } 131 132 d.set = ls 133 return nil 134 } 135 136 func createLimitSet(spec *specs.Spec) (*limits.LimitSet, error) { 137 ls, err := defaults.get() 138 if err != nil { 139 return nil, err 140 } 141 142 // Then apply overwrites on top of defaults. 143 for _, rl := range spec.Process.Rlimits { 144 lt, ok := fromLinuxResource[rl.Type] 145 if !ok { 146 return nil, fmt.Errorf("unknown resource %q", rl.Type) 147 } 148 ls.SetUnchecked(lt, limits.Limit{ 149 Cur: rl.Soft, 150 Max: rl.Hard, 151 }) 152 } 153 return ls, nil 154 }