Live data from Hacker News

eBPF Summit Day 1 Recap

cilium.io

21–26 of 26 posts

Re: eBPF Summit Day 1 Recap

#21

Earlier quoted context omitted.

When there's no loop bound it cannot prove termination, see halting problem. Goal is to avoid getting an infinite loop and then freezing the kernel of course.

Right but I guess the point I'm getting at is that termination seems neither necessary nor sufficient to me. A loop (or nested loops...) that goes up to 2^63 may as well be infinite, so on the face of it it's not obvious why boundedness gets you anywhere by itself—you'd need to prove something stronger anyway. Conversely, it's not impossible to have loops that nevertheless (provably) terminate within N instructions.…

Forget 2^63, and forget nested loops. Think instead a loop with maybe 2000 iterations --- that seems like the frontier of what the verifier will let you do right now. The verifier's budget is 1MM cycles, for the whole program, including each tick of every loop in it. That is to say that the verifier is literally going to execute your program symbolically, and give up after that many cycles, or at any loop where it can't easily see that the induction variable is bounded by a program constant.

The idea here is that without any kind of loop, it's quite tricky to do some basic packet processing. For instance, if you want to clamp the MSS of a TCP connection, you have to grovel through TCP options, which do not occur at fixed offsets or in a particular order; you want to write a "for" loop over the (inherently limited) range of bytes at the computed offset of the TCP options. You can trivially bound that loop by the MTU of the link your program is loaded on (and also the limited possible size of TCP options), the loop won't iterate that many times, and it won't do much inside the loop.

But it's very much not the case that bounded loops give you general-purpose programming, like to implement your own data structures. BPF programs rely on kernel helpers and userland programs that maintain maps and read perf to do that stuff.

Re: eBPF Summit Day 1 Recap

#22

Earlier quoted context omitted.

Loops must be bounded, that means, the verifier must be able to see that the loop will eventually terminate based on the condition. The verifier will simulate all iterations of the loop and as such it is limited by the verifier complexity, that is, it'll do analysis of up to 1 million walked insns for the entire program until the verifier rejects it.

Okay thanks! So what I don't get is, what's the point of bounding loops then? If it's already simulating the program up to 1M instructions and rejecting it if simulation doesn't prove termination (bounded model checking?), then can't it still do that when there's no loop bound? Because I kind of expected the algorithm would say "hey this loop is bounded up to 4K, this nested one is up to 3K, therefore I can't prove t…

A shot in the dark but maybe the disconnect here is: BPF statically verifies your program at load time, but a loaded BPF program is executed many, many times after that, with varied inputs. It can't be verified every time it is run, only the first time it's loaded.

Re: eBPF Summit Day 1 Recap

#23
post #20

Earlier quoted context omitted.

Ah I see, thanks for running it! Yeah so it's not this particular loop that's interesting (there's probably always going to be some simple-looking loop a solver can't prove—and I'm sure we could come up with simpler examples), but rather, the interesting question is whether it can figure out anything that doesn't map directly to bounded for/while/do-while loops. It's interesting because: 1. If the answer is no, then…

The verifier is extremely fussy. Different forms of the exact same loop (as far as the programmer is concerned) will get different results, and my experience is that I spend time rewriting the same loop in different ways just to get programs to pass. A bona-fide non-unrolled loop in a BPF program right now is a special thing that takes extra time to implement, and you're probably not going to use them casually. The a…

Thanks! Yeah that makes sense; it sounds like it's just the terminology then.

Re: eBPF Summit Day 1 Recap

#24
post #7
post #5

Earlier quoted context omitted.

A possibly lighter weight alternative is libbpf-rs [0]. libbpf-rs is designed to take advantage of BPF's Compile-Once-Run-Everywhere functionality where you can ship a pre-compiled object file to production instead of an entire compiler toolchain. Disclaimer: I wrote libbpf-rs. [0]: https://github.com/libbpf/libbpf-rs

Do people generally do that? Is this an artifact of people using iovisor/bcc and Python or whatever? We just build .o files with clang and ship 'em.

Yes. If you ship any eBPF programs that utilize kprobes and/or read kernel structures that change between kernel versions, you have portability problems. See https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf... for a good overview of the problem that CO-RE intends to solve.

Re: eBPF Summit Day 1 Recap

#26
post #7

Earlier quoted context omitted.

Do people generally do that? Is this an artifact of people using iovisor/bcc and Python or whatever? We just build .o files with clang and ship 'em.

Yes. If you ship any eBPF programs that utilize kprobes and/or read kernel structures that change between kernel versions, you have portability problems. See https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf... for a good overview of the problem that CO-RE intends to solve.

Ah! That makes so much sense. It hadn't occurred; when we rolled out BPF at Fly, the first thing we did was commit to standardize our kernels. After all, there's not much CO-RE can do about your BPF compiler not having tail calls, or not allowing bounded loops. But we're almost entirely XDP; I can see kernel structs being a much bigger problem for people doing observability work.
Post reply on HN