Live data from Hacker News

eBPF Verification Is Untenable

twitter.com

31–40 of 116 posts

Re: eBPF Verification Is Untenable

#31
post #4

I hope no one tries to use the rust "safety" guarantees for security guards. They are designed to prevent bugs not intentional abuse. If perfect without bugs they theoretically might be usable for security guards, but it's not where priorities lies when it comes to bug fixes and design. And people mistaking rust safety + no unsafe lint for "security against evil code" could be long term quite an issue for rust in var…

I agree -- relying on Safe Rust's "guarantees" for security purposes is very likely to be problematic. To make the reasons concrete: for the last 4 years rustc has had a bug that allows writing transmute (arbitrary type conversion) without the use of unsafe: https://zyedidia.github.io/blog/posts/5-safe-transmute/ . This is one of the 77 current open unsoundness bugs on the Rust issue tracker. To make this tenable you…

I am a bit skeptical this is a workable approach long term, but there is a project based on an attempt to enumerate all of Rust's soundness holes and use Rust's compiler infrastructure to detect and forbid them. They think that by erring on the side of forbidding valid code this is feasible. https://news.ycombinator.com/item?id=35501065

Re: eBPF Verification Is Untenable

#32

Hm. Doesn’t look viable to me. I’m not against language-based security, proof-carrying code, and all that, but I have less than perfect confidence that the Rust compiler currently is or will soon be sound enough to be secure against actively hostile code—AFAIU the language designers haven’t even written down their core calculus, let alone proven it sound. Putting the entirety of the Rust compiler (including, at least…

OK then! Back to C it is I guess. More seriously, we're talking about the Linux kernel here: it's written in C, and there's some momentum to write new code in Rust. You're asking for the moon, but you may have to settle for a picture of it.

I’m not talking about writing parts of the kernel in Rust. I‘m not even talking about using Rust inside the eBPF implementation specifically. In either case that’s replacing C with Rust, and if that’s what you want, sure, knock yourself out. In the spirit of full disclosure, I’ll admit to not being a fan, but it’s still entirely plausible and one can seriously argue it’d be an improvement.

But what TFA talks about amounts to replacing the eBPF verifier with (a blessed userspace version of) the Rust typechecker—dragging the rest of the compiler along for the ride—and that just feels like a downgrade in almost every respect. It’s humongous, it requires strange contortions due to not fitting in the kernel, it implements a comparatively very complicated spec, that spec is not written down, etc. The eBPF machine is not perfect, especially (as the authors point out) when you account for the “helpers“, but it mostly avoids these downsides. It’s not the moon—it’s already there.

Re: eBPF Verification Is Untenable

#33

I haven't been following the eBPF situation for a while, but... how did it come to this? I thought the point of BPF (sans 'e' anyway) was that it was pretty much secure by construction, or at minimum was simple enough to fully verify in polynomial time. So these eBPF vulnerabilities sound like a completely invented, unnecessary class of problems.

Because devs latched into their interpretation of eBPF’s promises and found ways to make them happen via the way any problem in computer science gets solved: indirection. This is human nature (just look at the stack of BIOS/EFI/OS execution rings that pre-empt each other to provide more features at lower hardware levels). Responsible operators should avoid these hacks where possible.

Re: eBPF Verification Is Untenable

#34

Hm. Doesn’t look viable to me. I’m not against language-based security, proof-carrying code, and all that, but I have less than perfect confidence that the Rust compiler currently is or will soon be sound enough to be secure against actively hostile code—AFAIU the language designers haven’t even written down their core calculus, let alone proven it sound. Putting the entirety of the Rust compiler (including, at least…

OK then! Back to C it is I guess. More seriously, we're talking about the Linux kernel here: it's written in C, and there's some momentum to write new code in Rust. You're asking for the moon, but you may have to settle for a picture of it.

You can write ebpf programs in rust. Bpftrace generates programs from an awk like language, you can make the program however you want. Solana does it with rust, I don’t know what it gains you though given the verifier protecting you from most of the pitfalls of C.

Re: eBPF Verification Is Untenable

#35
Somewhat tangentially related, if anyone is interested in writing eBPF programs in Rust, check out aya-rs (https://aya-rs.dev/).

Rustc supports eBPF bytecode as a target, and aya-rs avoids using clang/llvm. So you can use rust to write eBPF code in both user and kernel space.

This is a different beast from the usual rust though - lots of `unsafe`s.

Re: eBPF Verification Is Untenable

#36
post #3

This is weird. 1. Instead of having the kernel verify the program about to be installed at installation time, they rely on a trusted compiler and having the kernel perform signature validation. This means that the kernel is relying on a userspace component to enforce kernel-level safety guarantees, adds another level of coupling (via key infrastructure) between the kernel and a particular version of the Rust compiler…

> 1. Instead of having the kernel verify the program about to be installed at installation time, they rely on a trusted compiler and having the kernel perform signature validation. This means that the kernel is relying on a userspace component to enforce kernel-level safety guarantees, adds another level of coupling (via key infrastructure) between the kernel and a particular version of the Rust compiler, and if some…

Driver Verifier? That’s not intended to prove the code under test secure, only to hopefully show that it’s not complete crap in well-known ways. Even a signed driver is still trusted code and requires administrator privileges to install. I guess the closest Linux counterpart would be a distro maintainer running a hardware vendor’s out-of-tree module under KASAN and, if it passes, signing the package with their PGP key.

But none of that is intended or able to check the module (resp. driver) is not gimmeroot.ko (resp. gimmesystem.dll)—that’s left to humans inspecting the source (resp. thoughts and prayers[1]). On the other hand, the eBPF VM absolutely is intended to be able to load anything any unprivileged user throws at it and emerge unscathed.

It’s not precisely essential that a kernel have this capability, but if one is to have it, restricting the allowable code to a predetermined vendor-approved set defeats most of the point. (The authors propose that a userspace compiler running on the user’s computer be allowed to extend it, as I understood them.)

[1] https://www.zdnet.com/article/these-hackers-used-microsoft-s...

Re: eBPF Verification Is Untenable

#37
Put extensions in a Wasm sandbox. The type system has been proven sound to the highest level of assurance possible with today's technology, mechanized at least twice, once in Coq and once in Isabelle. The algorithm is efficiently implementable and there are approaching a dozen production Wasm engines, some of which have tiers with proven safety guarantees. There is even an interpreter written in a proof assistant that has been proven fully functionally correct.

Re: eBPF Verification Is Untenable

#39

Earlier quoted context omitted.

Your point 1 is the elephant herd in the room. If I were a paranoid person, I would think it’s by design - build in a way to compromise a system retroactively.

That makes no sense

You should read Ken Thompson's "Reflections on trusting trust". Outsourcing security to a tool which you have to blindly trust, and can't verify is very, very dangerous.

Re: eBPF Verification Is Untenable

#40
eBPF verification was always a laugh from the very beginning design stages, if you ask me, because as this paper demonstrates, it was never going to be enough. Anyone with a modicum of security or PLT experience could have told you this when evaluating the design and history. Like, if I had to be completely honest, the very fact the security/robustness model started on principles like "fixed number of loop iterations" or "no backedge jumps" (among several others) in the verifier was a pretty good sign that this was always going to be a source of continuous vulnerabilities. It makes me think people are flying blind. If you're not systematically fixing these issues in the very design stages of the system, and using duct tape, you're just going to patch every single thing one by one as it happens, and then how is that any different from today?

The basic idea is simple. You have the verifier, and the TCB. The verifier has to reject invalid programs, so the TCB does not have its integrity compromised by the program. The verifier is small, so it can be audited. That's nice -- until you back out and realize the TCB is "the entire linux kernel and everything inside of it and all of the surface area API between it and the BPF Virtual Machine" and it dawns on you that at that point the principle of "system integrity being maintained" relies very little on the verifier and actually a whole lot on Linux being functionally correct. Which is where you started at in the first place. The goal of eBPF after all isn't just to burn CPU cycles and return an integer code. It has to interact with the system. Having the TCB functionally be "every line of code we're trying to protect" is the Windows 3.1 of integrity models.

Now, this might also be OK and quantifiable to some extent. Except for the other fact that the guiding design principle in Linux is to pretty much grow without bound, without end, rewrite code left and right, and the eBPF subsystem itself has been endlessly tacking on features left and right for what -- years now?

If you take away any of these three things (flawed design basis, ridiculously large TCB, endless and boundless growth) and modify or remove one of them, the picture looks much better. Solid basis? You can maybe handle the other two if you're careful and on top of things, big hand waive. Very small TCB? Great, you can put significantly more trust in the verifier, freeing you from the need to worry about every line of code. No endless growth? Then you have a target you can monitor and maybe improve on e.g. reduce trends downward over time. But the combination of all three of these things means that the end result is "greater than the sum of the parts" so to speak and it will always be a matter of pushing the boulder up the hill every day, all so it can fall back down again.

That said, eBPF is really useful. I get a ton of value out of it. The verifier does allow you to have greater trust in running things in the kernel. In this case, doing something is quite literally 1,000% better than doing nothing in this if you ask me, at least for most intents and purposes. So making it safer and more robust is worthwhile. But it was pretty easy to see this sort of stuff from a long way out, IMO.

Post reply on HN