From e69b01c3a8f30967170b3c8f1af7412762068781 Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Mon, 16 Oct 2023 19:14:03 -0700 Subject: [PATCH] Add command-line tool to quickly dump the Sentry BPF program. PiperOrigin-RevId: 573994110 --- runsc/boot/filter/dumpfilter/BUILD | 30 +++++++ runsc/boot/filter/dumpfilter/dumpfilter.go | 80 +++++++++++++++++++ .../boot/filter/dumpfilter/dumpfilter_test.go | 38 +++++++++ .../filter/dumpfilter/dumpfilter_unsafe.go | 28 +++++++ 4 files changed, 176 insertions(+) create mode 100644 runsc/boot/filter/dumpfilter/BUILD create mode 100644 runsc/boot/filter/dumpfilter/dumpfilter.go create mode 100644 runsc/boot/filter/dumpfilter/dumpfilter_test.go create mode 100644 runsc/boot/filter/dumpfilter/dumpfilter_unsafe.go diff --git a/runsc/boot/filter/dumpfilter/BUILD b/runsc/boot/filter/dumpfilter/BUILD new file mode 100644 index 000000000..8b2140bb1 --- /dev/null +++ b/runsc/boot/filter/dumpfilter/BUILD @@ -0,0 +1,30 @@ +load("//tools:defs.bzl", "go_binary", "go_test") + +package( + default_applicable_licenses = ["//:license"], + licenses = ["notice"], +) + +go_binary( + name = "dumpfilter", + srcs = [ + "dumpfilter.go", + "dumpfilter_unsafe.go", + ], + deps = [ + "//pkg/abi/linux", + "//pkg/bpf", + "//pkg/log", + "//pkg/seccomp", + "//pkg/sentry/platform/systrap", + "//runsc/boot/filter", + "//runsc/flag", + ], +) + +go_test( + name = "dumpfilter_test", + srcs = ["dumpfilter_test.go"], + data = [":dumpfilter"], + deps = ["//pkg/test/testutil"], +) diff --git a/runsc/boot/filter/dumpfilter/dumpfilter.go b/runsc/boot/filter/dumpfilter/dumpfilter.go new file mode 100644 index 000000000..06474d746 --- /dev/null +++ b/runsc/boot/filter/dumpfilter/dumpfilter.go @@ -0,0 +1,80 @@ +// Copyright 2023 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. + +// dumpfilter dumps the seccomp-bpf program used by the Sentry. +package main + +import ( + "fmt" + "os" + + "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/bpf" + "gvisor.dev/gvisor/pkg/log" + "gvisor.dev/gvisor/pkg/seccomp" + "gvisor.dev/gvisor/pkg/sentry/platform/systrap" + "gvisor.dev/gvisor/runsc/boot/filter" + "gvisor.dev/gvisor/runsc/flag" +) + +// Flags. +var ( + output = flag.String("output", "fancy", "Output type: 'fancy' (human-readable with line numbers resolved), 'plain' (diffable but still human-readable output), 'bytecode' (dump raw bytecode)") + nvproxy = flag.Bool("nvproxy", false, "Enable nvproxy in filter configuration") +) + +func main() { + flag.Parse() + rules, denyRules := filter.Rules(filter.Options{ + Platform: &systrap.Systrap{}, + NVProxy: *nvproxy, + }) + insns, stats, err := seccomp.BuildProgram([]seccomp.RuleSet{ + { + Rules: denyRules, + Action: linux.SECCOMP_RET_ERRNO, + }, + { + Rules: rules, + Action: linux.SECCOMP_RET_ALLOW, + }, + }, linux.SECCOMP_RET_ERRNO, linux.SECCOMP_RET_ERRNO) + if err != nil { + log.Warningf("%v", err) + os.Exit(1) + } + log.Infof("Size before optimizations: %d", stats.SizeBeforeOptimizations) + log.Infof("Size after optimizations: %d", stats.SizeAfterOptimizations) + log.Infof("Build duration: %v", stats.BuildDuration) + log.Infof("Optimization passes duration: %v", stats.OptimizeDuration) + log.Infof("Total duration: %v", stats.BuildDuration+stats.OptimizeDuration) + switch *output { + case "fancy": + dump, err := bpf.DecodeInstructions(insns) + if err != nil { + log.Warningf("%v", err) + os.Exit(1) + } + fmt.Print(dump) + case "plain": + for _, ins := range insns { + fmt.Println(ins.String()) + } + case "bytecode": + if _, err := os.Stdout.WriteString(InstructionsToBytecode(insns)); err != nil { + log.Warningf("cannot write bytecode to stdout: %v", err) + os.Exit(1) + } + } +} diff --git a/runsc/boot/filter/dumpfilter/dumpfilter_test.go b/runsc/boot/filter/dumpfilter/dumpfilter_test.go new file mode 100644 index 000000000..c9dbe4b41 --- /dev/null +++ b/runsc/boot/filter/dumpfilter/dumpfilter_test.go @@ -0,0 +1,38 @@ +// Copyright 2023 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 dumpfilter_test + +import ( + "os/exec" + "strings" + "testing" + + "gvisor.dev/gvisor/pkg/test/testutil" +) + +// TestDumpFilter tests that the `dumpfilter` program works. +func TestDumpFilter(t *testing.T) { + binPath, err := testutil.FindFile("runsc/boot/filter/dumpfilter/dumpfilter") + if err != nil { + t.Fatalf("cannot locate dumpfilter: %v", err) + } + output, err := exec.Command(binPath).CombinedOutput() + for _, line := range strings.Split(string(output), "\n") { + t.Log(line) + } + if err != nil { + t.Errorf("program failed: %v", err) + } +} diff --git a/runsc/boot/filter/dumpfilter/dumpfilter_unsafe.go b/runsc/boot/filter/dumpfilter/dumpfilter_unsafe.go new file mode 100644 index 000000000..06d947367 --- /dev/null +++ b/runsc/boot/filter/dumpfilter/dumpfilter_unsafe.go @@ -0,0 +1,28 @@ +// Copyright 2023 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. + +// dumpfilter dumps the seccomp-bpf program used by the Sentry. +package main + +import ( + "unsafe" + + "gvisor.dev/gvisor/pkg/bpf" +) + +// InstructionsToBytecode returns raw the BPF bytecode for the given program. +func InstructionsToBytecode(insns []bpf.Instruction) string { + bytePointer := (*byte)(unsafe.Pointer(&insns[0])) + return unsafe.String(bytePointer, len(insns)*int(unsafe.Sizeof(bpf.Instruction{}))) +}