13 Commits
Author SHA1 Message Date
Etienne PerotandgVisor bot 62175dea49 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 663e69bde4 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 ecbf37d037 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 d4973670c3 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 f098b9b06e 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 71dc79e653 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 addac5f248 Refactor seccomp rules with interfaces rather than disjunctive normal form.
This replaces the `seccomp.Rule` type with the `seccomp.SyscallRule`
interface, which is an abstraction that defines how to match a syscall's
arguments and RIP.

This has the following benefits:

- The code can verify that rules are self-contained, as the
  `SyscallRule.Render` contract specifies that the rule must jump to
  either a "matched" or "not matched" label, and may not fall through.
  It uses `ProgramBuilder`'s support for asserting unreachability to
  enforce this.
- Rules that match everything are more explicit (no more implicit
  "no rules means everything matches" behavior, instead you have to
  explicitly specify `seccomp.MatchAll{}`).
- "OR" behavior is explicit (a disjunctive rule is marked as `seccomp.Or`
  rather than the current implicit meaning of a list of rules).
- Allows the creation of more sophisticated matching rules that don't work
  on a per-argument basis. This change does not do any of that yet, it
  simply refactors existing rules without changing the way they work.
- Decouples rule-specific rendering code from the larger program generation
  code (BST, architecture check, etc.).

Unfortunately there is no easy way to split this change into multiple
sub-changes without introducing additional complexity to support both forms
of expressing rules, so sorry if this is a large change. But note that it
is actually net-negative in line count.

Despite the size of this change, please review it carefully, as this is a
security-sensitive change.

PiperOrigin-RevId: 571459670
2023-10-06 16:19:26 -07:00
Adin ScannellandgVisor bot 1ceb814544 Add default_applicable_licenses rules to packages.
PiperOrigin-RevId: 513581243
2023-03-02 10:50:04 -08:00
Jamie LiuandgVisor bot 1ad3822200 Add go:build directives as required by Go 1.17's gofmt.
PiperOrigin-RevId: 385894869
2021-07-20 16:28:45 -07:00
Rahat MahmoodandgVisor bot e00bd82816 Remove uses of the binary package from the rest of the sentry.
PiperOrigin-RevId: 372020696
2021-05-04 16:41:08 -07:00
Ayush RanjanandgVisor bot e668288faf [op] Replace syscall package usage with golang.org/x/sys/unix in runsc/.
The syscall package has been deprecated in favor of golang.org/x/sys.

Note that syscall is still used in some places because the following don't seem
to have an equivalent in unix package:
- syscall.SysProcIDMap
- syscall.Credential

Updates #214

PiperOrigin-RevId: 361381490
2021-03-06 22:07:07 -08:00
Howard Zhang ae1141778e fix seccomp test for ARM64
As open syscall is not support on ARM64, change syscall
from 'open' to 'openat' in no_match_name_allow

Signed-off-by: Howard Zhang <howard.zhang@arm.com>
2020-09-25 14:49:13 +08:00
Ian LewisandgVisor bot dcd532e2e4 Add support for OCI seccomp filters in the sandbox.
OCI configuration includes support for specifying seccomp filters. In runc,
these filter configurations are converted into seccomp BPF programs and loaded
into the kernel via libseccomp. runsc needs to be a static binary so, for
runsc, we cannot rely on a C library and need to implement the functionality
in Go.

The generator added here implements basic support for taking OCI seccomp
configuration and converting it into a seccomp BPF program with the same
behavior as a program generated by libseccomp.

- New conditional operations were added to pkg/seccomp to support operations
  available in OCI.
- AllowAny and AllowValue were renamed to MatchAny and EqualTo to better reflect
  that syscalls matching the conditionals result in the provided action not
  simply SCMP_RET_ALLOW.
- BuildProgram in pkg/seccomp no longer panics if provided an empty list of
  rules. It now builds a program with the architecture sanity check only.
- ProgramBuilder now allows adding labels that are unused. However, backwards
  jumps are still not permitted.

Fixes #510

PiperOrigin-RevId: 331938697
2020-09-15 23:19:17 -07:00