summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/limits/limits.go
blob: b0571739f1f8589dc56b1e6fb53da56ae8779300 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package limits provides resource limits.
package limits

import (
	"sync"
	"syscall"
)

// LimitType defines a type of resource limit.
type LimitType int

// Set of constants defining the different types of resource limits.
const (
	CPU LimitType = iota
	FileSize
	Data
	Stack
	Core
	Rss
	ProcessCount
	NumberOfFiles
	MemoryLocked
	AS
	Locks
	SignalsPending
	MessageQueueBytes
	Nice
	RealTimePriority
	Rttime
)

// Infinity is a constant representing a resource with no limit.
const Infinity = ^uint64(0)

// Limit specifies a system limit.
//
// +stateify savable
type Limit struct {
	// Cur specifies the current limit.
	Cur uint64
	// Max specifies the maximum settable limit.
	Max uint64
}

// LimitSet represents the Limits that correspond to each LimitType.
//
// +stateify savable
type LimitSet struct {
	mu   sync.Mutex `state:"nosave"`
	data map[LimitType]Limit
}

// NewLimitSet creates a new, empty LimitSet.
func NewLimitSet() *LimitSet {
	return &LimitSet{
		data: make(map[LimitType]Limit),
	}
}

// GetCopy returns a clone of the LimitSet.
func (l *LimitSet) GetCopy() *LimitSet {
	l.mu.Lock()
	defer l.mu.Unlock()
	copyData := make(map[LimitType]Limit)
	for k, v := range l.data {
		copyData[k] = v
	}
	return &LimitSet{
		data: copyData,
	}
}

// Get returns the resource limit associated with LimitType t.
// If no limit is provided, it defaults to an infinite limit.Infinity.
func (l *LimitSet) Get(t LimitType) Limit {
	l.mu.Lock()
	defer l.mu.Unlock()
	s, ok := l.data[t]
	if !ok {
		return Limit{Cur: Infinity, Max: Infinity}
	}
	return s
}

// GetCapped returns the current value for the limit, capped as specified.
func (l *LimitSet) GetCapped(t LimitType, max uint64) uint64 {
	s := l.Get(t)
	if s.Cur == Infinity || s.Cur > max {
		return max
	}
	return s.Cur
}

// SetUnchecked assigns value v to resource of LimitType t.
func (l *LimitSet) SetUnchecked(t LimitType, v Limit) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.data[t] = v
}

// Set assigns value v to resource of LimitType t and returns the old value.
// privileged should be true only when either the caller has CAP_SYS_RESOURCE
// or when creating limits for a new kernel.
func (l *LimitSet) Set(t LimitType, v Limit, privileged bool) (Limit, error) {
	l.mu.Lock()
	defer l.mu.Unlock()

	// If a limit is already set, make sure the new limit doesn't
	// exceed the previous max limit.
	if _, ok := l.data[t]; ok {
		// Unprivileged users can only lower their hard limits.
		if l.data[t].Max < v.Max && !privileged {
			return Limit{}, syscall.EPERM
		}
		if v.Cur > v.Max {
			return Limit{}, syscall.EINVAL
		}
	}
	old := l.data[t]
	l.data[t] = v
	return old, nil
}