// 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()) }