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