13 Commits
Author SHA1 Message Date
Konstantin BogomolovandgVisor bot 4dfcd5e1ae nogo: Fix more analyzer findings.
PiperOrigin-RevId: 875246522
2026-02-25 11:07:15 -08:00
Etienne PerotandgVisor bot 8d1cd3dbae seccomp: Refactor to allow arbitrary number of RuleSets.
This is meant to make #12557 possible to implement, i.e.
making it possible for the seccomp configuration to specify actions
other than deny/allow in the main `runsc` configuration. This change
doesn't do that but it adds the structures to make that possible.

PiperOrigin-RevId: 868246148
2026-02-10 11:41:43 -08:00
Etienne PerotandgVisor bot 53a1d77925 Plumb seccomp program options through the Sentry filter and Platform.
This adds a `HottestSyscalls` function to the `Platform` interface, as the
platform determines which syscalls are most often called by the Sentry.

This is used by the Sentry seccomp filter code to build the final list of
hot syscalls.

PiperOrigin-RevId: 582833363
2023-11-15 15:57:53 -08:00
Etienne PerotandgVisor bot 23236bb61d Separate out rule optimizers from main syscall rendering.
This does an optimization pass over `RuleSet`s prior to rendering anything,
rather than optimizing at the last minute for each syscall.

PiperOrigin-RevId: 581671748
2023-11-12 00:47:32 -08:00
Etienne PerotandgVisor bot 920774eef9 seccomp.BuildProgram: Add ProgramOptions struct.
This is just a refactoring, but the intention of this `struct` is to add
other useful options for the program build, such as the list of expected
"hottest" syscalls by frequency.

The interface is a bit awkward, because of the need for two entry points
(one which didn't have any way to set the default actions), and because the
zero value of `linux.BPFAction` is a valid (and common) "default action":
killing the program (`linux.SECCOMP_RET_KILL_THREAD`).
We also can't use `*linux.BPFAction`, as `linux.SECCOMP_RET_*` are constants.
So the struct fields use functions that "resolve" to an action.
This reads fairly well in the call sites (`DefaultAction: Return(action)`),
at the cost of slightly convoluted logic in `seccomp.go`.

PiperOrigin-RevId: 581477348
2023-11-11 00:44:40 -08:00
Etienne PerotandgVisor bot 54a976dce3 bpf: Do not use interfaces to define byte ordering in BPF evaluation.
This speeds up BPF evaluation by avoiding interface call overhead.

```
            │   initial   │         hey_look_no_interfaces         │         even_fewer_interfaces          │
            │   sec/op    │   sec/op     vs base                   │   sec/op     vs base                   │
Interpreter   26.31n ± 0%   21.87n ± 0%  -16.88% (p=0.000 n=21+20)   14.26n ± 0%  -45.80% (p=0.000 n=21+20)
```

PiperOrigin-RevId: 576591719
2023-10-25 11:32:34 -07:00
Etienne PerotandgVisor bot beba05ce32 BPF: Add "instrumented execution" mode.
This creates a duplicate BPF interpreter which keeps track of the
instructions and input bytes it accesses.

Having such a mode is useful for the following reasons:

  - For seccomp-bpf programs that the Sentry enforces from the sandboxed
    application, this allows it to check whether the program is
    deterministic based solely on the syscall number. For such syscalls,
    we can cache the result of executing the program, and never run the
    interpreter during syscall execution. (This optimization is not yet
    implemented, but this change enables it to be.)
  - For verification of correctness, coverage-based fuzzing is necessary.
    I plan to implement a fuzz-based test which verifies that the
    optimized and unoptimized versions of the same seccomp-bpf programs
    have the same behavior, and ensuring that the corpus achieves full
    coverage is necessary to ensure a good degree of correctness.

However, this mode is slower than it needs to when evaluating the
program in an application syscall (which is a very hot path), so this
has to live in a separate (sadly duplicated) function. The tests are
expanded to verify the for instrumented output, but also verify that
the "fast" version (aka the previous implementation) behaves the exact
same way, so this prevents the risk that code from one will drift away
behavior-wise.

To illustrate how much slower (not to mention it does allocations):

```
                        │ with_binary_order_removed │
                        │          sec/op           │
Interpreter                             14.26n ± 0%
InstrumentedInterpreter                 98.37n ± 3%
```

PiperOrigin-RevId: 576562506
2023-10-25 10:03:26 -07:00
Etienne PerotandgVisor bot 80ef8a68d9 bpf: Remove Input interface and simply use bytes as the input.
This removes the interface indirection from BPF evaluation, which is
a very hot path (runs for every application syscall for seccomp'd containers)
and simplifies the code in general.

```
            │   initial   │         hey_look_no_interfaces         │
            │   sec/op    │   sec/op     vs base                   │
Interpreter   26.31n ± 0%   21.12n ± 0%  -19.73% (p=0.000 n=21+20)
```

PiperOrigin-RevId: 576375947
2023-10-24 20:47:59 -07:00
Etienne PerotandgVisor bot 3968ccab4a secbench: Read stdout before waiting for process.
Go's `command.Cmd.Wait` closes stdout, so this can race with the stdout
reader goroutine.

PiperOrigin-RevId: 572983626
2023-10-12 12:42:43 -07:00
Etienne PerotandgVisor bot 56c21d81cd seccomp: Make SyscallRules map type opaque.
This wraps the `map[uintptry]SyscallRule` into an unexported field of a struct
so that it cannot be accessed directly.

This is helpful for the `runsc` and `fsgofer` seccomp filters which are quite
complex and built across multiple files and multiple functions, where it is
not always clear which order they are executed in. By forcing mutations to be
more explicit about their intent (especially "merge with this new rule" vs
"override what happens for this syscall with this new rule"), we can crash if
that intent isn't what's actually happening.

PiperOrigin-RevId: 572361619
2023-10-10 14:11:01 -07:00
Etienne PerotandgVisor bot a0e426ea1c secbench: Benchmark optimization duration and compression ratio.
Current values for the Sentry filters:

```
              │   current   │
              │  build-sec  │
SentrySystrap   13.73m ± 0%
SentryKVM       16.36m ± 0%

              │      current      │
              │ compression-ratio │
SentrySystrap          2.165 ± 0%
SentryKVM              2.132 ± 0%

              │   current   │
              │  gen-instr  │
SentrySystrap   1.288k ± 0%
SentryKVM       1.373k ± 0%

              │  current   │
              │ opt-instr  │
SentrySystrap   595.0 ± 0%
SentryKVM       644.0 ± 0%

              │   current   │
              │   opt-sec   │
SentrySystrap   819.0µ ± 2%
SentryKVM       897.0µ ± 1%
```

PiperOrigin-RevId: 572089103
2023-10-09 17:53:22 -07:00
Etienne PerotandgVisor bot aa8a460e02 bpf: Replace most uses of linux.BPFInstruction with bpf.Instruction.
`bpf.Instruction` is the same type as `linux.BPFInstruction`, except that it
uses the BPF instruction-to-string decoder to give a nice human-readable
stringification.

PiperOrigin-RevId: 570499020
2023-10-03 14:34:53 -07:00
Etienne PerotandgVisor bot e0844201e4 Add library to benchmark gVisor's seccomp-bpf filters.
The first step to optimizing something is to measure it.

This creates a `secbench` library which can benchmark the time it takes to
run an application "Profile", which is a weighted-random set of syscall
sequences, under a given `seccomp-bpf` filter.

The `secbench` library runs two subprocesses which run the same set of system
calls using this application profile. One of the subprocesses runs with the
seccomp-bpf filter, and the other one runs without it. The library computes
the difference, and reports this duration as the time it takes to run the
seccomp-bpf filter for that system call. It also reports the overall time it
takes to run the weighted-random set of system calls, useful as a general
measure of the overhead that the application will see.

PiperOrigin-RevId: 567704927
2023-09-22 13:38:04 -07:00