Re: [RFC PATCH 2/7] x86/sci: add core implementation for system call isolation

From: Ingo Molnar
Date: Sat Apr 27 2019 - 06:46:27 EST



* Ingo Molnar <mingo@xxxxxxxxxx> wrote:

> * Andy Lutomirski <luto@xxxxxxxxxx> wrote:
>
> > > And no, I'm not arguing for Java or C#, but I am arguing for a saner
> > > version of C.
> >
> > IMO three are three credible choices:
> >
> > 1. C with fairly strong CFI protection. Grsecurity has this (supposedly
> > â thereâs a distinct lack of source code available), and clang is
> > gradually working on it.
> >
> > 2. A safe language for parts of the kernel, e.g. drivers and maybe
> > eventually filesystems. Rust is probably the only credible candidate.
> > Actually creating a decent Rust wrapper around the core kernel
> > facilities would be quite a bit of work. Things like sysfs would be
> > interesting in Rust, since AFAIK few or even no drivers actually get
> > the locking fully correct. This means that naive users of the API
> > cannot port directly to safe Rust, because all the races won't compile
> > :)
> >
> > 3. A sandbox for parts of the kernel, e.g. drivers. The obvious
> > candidates are eBPF and WASM.
> >
> > #2 will give very good performance. #3 gives potentially stronger
> > protection against a sandboxed component corrupting the kernel overall,
> > but it gives much weaker protection against a sandboxed component
> > corrupting itself.
> >
> > In an ideal world, we could do #2 *and* #3. Drivers could, for
> > example, be written in a language like Rust, compiled to WASM, and run
> > in the kernel.
>
> So why not go for #1, which would still outperform #2/#3, right? Do we
> know what it would take, roughly, and how the runtime overhead looks
> like?

BTW., CFI protection is in essence a compiler (or hardware) technique to
detect stack frame or function pointer corruption after the fact.

So I'm wondering whether there's a 4th choice as well, which avoids
control flow corruption *before* it happens:

- A C language runtime that is a subset of current C syntax and
semantics used in the kernel, and which doesn't allow access outside
of existing objects and thus creates a strictly enforced separation
between memory used for data, and memory used for code and control
flow.

- This would involve, at minimum:

- tracking every type and object and its inherent length and valid
access patterns, and never losing track of its type.

- being a lot more organized about initialization, i.e. no
uninitialized variables/fields.

- being a lot more strict about type conversions and pointers in
general.

- ... and a metric ton of other details.

- If such a runtime could co-exist without big complications with
regular C kernel code then we could convert particular pieces of C
code into this safe-C runtime step by step, and would also allow the
compilation of a piece of code as regular C, or into the safe runtime.

- If a particular function can be formally proven to be safe, it can be
compiled as C - otherwise it would be compiled as safe-C.

- ... or something like this.

The advantage would be: data corruption could never be triggered by code
itself, if the compiler and runtime is correct. Return addresses and
stacks wouldn't have to be 'hardened' or 'checked', because they'd never
be corrupted in the first place. WX memory wouldn't be an issue as kernel
code could never jump into generated shell code or ROP gadgets.

The disadvantage: the overhead of managing this, and any loss of
flexibility on the kernel programming side.

Does this make sense, and if yes, does such a project exist already?
(And no, I don't mean Java or C#.)

Or would we in essence end up with a Java runtime, with C syntax?

Thanks,

Ingo