Files
Etienne Perot d9fcdc7714 seccheck: Initialize seccheck.Points only when needed.
This moves the initialization of `seccheck.Points` out of package-level
`init` and instead moves it to an explicit `seccheck.Initialize()` function.

On AMD64, this saves about 614KiB of heap memory that would otherwise always
be live.

PiperOrigin-RevId: 514813332
2023-03-07 12:53:27 -08:00

109 lines
3.1 KiB
Go

// Copyright 2022 The gVisor Authors.
//
// 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 linux
import (
"fmt"
"os"
"reflect"
"runtime"
"strings"
"testing"
"unicode"
"gvisor.dev/gvisor/pkg/sentry/seccheck"
)
func findPoint(name string) (seccheck.PointDesc, bool) {
for _, pt := range seccheck.Points {
if pt.Name == name {
return pt, true
}
}
return seccheck.PointDesc{}, false
}
// TestSeccheckMax catches cases that a new syscall was added but seccheck raw
// syscall numbers (e.g. syscall/sysno/123) have not been updated.
func TestSeccheckMax(t *testing.T) {
max := uintptr(0)
for sysno := range archToTest.Table {
if sysno > max {
max = sysno
}
}
want := fmt.Sprintf("syscall/sysno/%d/enter", max)
if _, ok := findPoint(want); !ok {
t.Errorf("seccheck.PointDesc for syscall %d not found. Update pkg/sentry/seccheck/metadata_amd64.go", max)
}
}
// TestSeccheckSyscalls verifies that all syscalls registered with a point
// callback have the corresponding seccheck metadata created.
func TestSeccheckSyscalls(t *testing.T) {
for sysno, syscall := range archToTest.Table {
if syscall.PointCallback == nil {
continue
}
// For every syscall with a PointCallback, there must be a corresponding
// seccheck.PointDesc created.
funcName := runtime.FuncForPC(reflect.ValueOf(syscall.PointCallback).Pointer()).Name()
if idx := strings.LastIndex(funcName, "."); idx > -1 {
funcName = funcName[idx+1:]
}
t.Run(funcName, func(t *testing.T) {
if !strings.HasPrefix(funcName, "Point") {
t.Errorf("PointCallback function name must start with Point: %q", funcName)
}
funcName = strings.TrimPrefix(funcName, "Point")
if len(funcName) == 0 {
t.Errorf("PointCallback function name invalid: %q", funcName)
}
pointName := strings.ToLower(string(funcName[0]))
for _, c := range funcName[1:] {
if unicode.IsUpper(c) {
pointName += "_"
}
pointName += string(unicode.ToLower(c))
}
for _, flavor := range []struct {
suffix string
typ seccheck.SyscallType
}{
{suffix: "enter", typ: seccheck.SyscallEnter},
{suffix: "exit", typ: seccheck.SyscallExit},
} {
fullName := fmt.Sprintf("syscall/%s/%s", pointName, flavor.suffix)
pt, ok := findPoint(fullName)
if !ok {
t.Fatalf("seccheck.PointDesc %q not found.", fullName)
}
if want := seccheck.GetPointForSyscall(flavor.typ, sysno); want != pt.ID {
t.Errorf("seccheck.Point for syscall %q is wrong, want: %v, got: %v", pointName, want, pt.ID)
}
}
})
}
}
func TestMain(m *testing.M) {
seccheck.Initialize()
os.Exit(m.Run())
}