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  }