summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/kernel/fd_map_test.go
blob: 9e76f0a2d3868a2fc30f4911518e3e954816385d (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 kernel

import (
	"testing"

	"gvisor.googlesource.com/gvisor/pkg/sentry/fs/filetest"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
	"gvisor.googlesource.com/gvisor/pkg/sentry/limits"
)

const (
	// maxFD is the maximum FD to try to create in the map.
	// This number of open files has been seen in the wild.
	maxFD = 2 * 1024
)

func newTestFDMap() *FDMap {
	return &FDMap{
		files: make(map[kdefs.FD]descriptor),
	}
}

// TestFDMapMany allocates maxFD FDs, i.e. maxes out the FDMap,
// until there is no room, then makes sure that NewFDAt works
// and also that if we remove one and add one that works too.
func TestFDMapMany(t *testing.T) {
	file := filetest.NewTestFile(t)
	limitSet := limits.NewLimitSet()
	limitSet.Set(limits.NumberOfFiles, limits.Limit{maxFD, maxFD}, true /* privileged */)

	f := newTestFDMap()
	for i := 0; i < maxFD; i++ {
		if _, err := f.NewFDFrom(0, file, FDFlags{}, limitSet); err != nil {
			t.Fatalf("Allocated %v FDs but wanted to allocate %v", i, maxFD)
		}
	}

	if _, err := f.NewFDFrom(0, file, FDFlags{}, limitSet); err == nil {
		t.Fatalf("f.NewFDFrom(0, r) in full map: got nil, wanted error")
	}

	if err := f.NewFDAt(1, file, FDFlags{}, limitSet); err != nil {
		t.Fatalf("f.NewFDAt(1, r, FDFlags{}): got %v, wanted nil", err)
	}
}

// TestFDMap does a set of simple tests to make sure simple adds,
// removes, GetRefs, and DecRefs work. The ordering is just weird
// enough that a table-driven approach seemed clumsy.
func TestFDMap(t *testing.T) {
	file := filetest.NewTestFile(t)
	limitSet := limits.NewLimitSet()
	limitSet.Set(limits.NumberOfFiles, limits.Limit{1, maxFD}, true /* privileged */)

	f := newTestFDMap()
	if _, err := f.NewFDFrom(0, file, FDFlags{}, limitSet); err != nil {
		t.Fatalf("Adding an FD to an empty 1-size map: got %v, want nil", err)
	}

	if _, err := f.NewFDFrom(0, file, FDFlags{}, limitSet); err == nil {
		t.Fatalf("Adding an FD to a filled 1-size map: got nil, wanted an error")
	}

	largeLimit := limits.Limit{maxFD, maxFD}
	limitSet.Set(limits.NumberOfFiles, largeLimit, true /* privileged */)

	if fd, err := f.NewFDFrom(0, file, FDFlags{}, limitSet); err != nil {
		t.Fatalf("Adding an FD to a resized map: got %v, want nil", err)
	} else if fd != kdefs.FD(1) {
		t.Fatalf("Added an FD to a resized map: got %v, want 1", fd)
	}

	if err := f.NewFDAt(1, file, FDFlags{}, limitSet); err != nil {
		t.Fatalf("Replacing FD 1 via f.NewFDAt(1, r, FDFlags{}): got %v, wanted nil", err)
	}

	if err := f.NewFDAt(maxFD+1, file, FDFlags{}, limitSet); err == nil {
		t.Fatalf("Using an FD that was too large via f.NewFDAt(%v, r, FDFlags{}): got nil, wanted an error", maxFD+1)
	}

	if ref := f.GetFile(1); ref == nil {
		t.Fatalf("f.GetFile(1): got nil, wanted %v", file)
	}

	if ref := f.GetFile(2); ref != nil {
		t.Fatalf("f.GetFile(2): got a %v, wanted nil", ref)
	}

	ref, ok := f.Remove(1)
	if !ok {
		t.Fatalf("f.Remove(1) for an existing FD: failed, want success")
	}
	ref.DecRef()

	if ref, ok := f.Remove(1); ok {
		ref.DecRef()
		t.Fatalf("r.Remove(1) for a removed FD: got success, want failure")
	}

}

func TestDescriptorFlags(t *testing.T) {
	file := filetest.NewTestFile(t)
	f := newTestFDMap()
	limitSet := limits.NewLimitSet()
	limitSet.Set(limits.NumberOfFiles, limits.Limit{maxFD, maxFD}, true /* privileged */)

	origFlags := FDFlags{CloseOnExec: true}

	if err := f.NewFDAt(2, file, origFlags, limitSet); err != nil {
		t.Fatalf("f.NewFDAt(2, r, FDFlags{}): got %v, wanted nil", err)
	}

	newFile, newFlags := f.GetDescriptor(2)
	if newFile == nil {
		t.Fatalf("f.GetFile(2): got a %v, wanted nil", newFile)
	}

	if newFlags != origFlags {
		t.Fatalf("new File flags %+v don't match original %+v", newFlags, origFlags)
	}
}