3

I try to get into XDP, for this I have this very small program:

// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include "bpf/bpf_helpers.h"
#include "xdpsock.h"

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, MAX_SOCKS);
    __uint(key_size, sizeof(int));
    __uint(value_size, sizeof(int));
} xsks_map SEC(".maps");

SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx) {

    return XDP_DROP;
}

But if I try to load it into a virtual interface veth-basic02, I get this error:

$ sudo ip -force link set dev veth-basic02 xdp object xdpsock_kern.o section xdp_sock

Prog section 'xdp_sock' rejected: Operation not permitted (1)! - Type: 6 - Instructions: 2 (0 over limit) - License:

Verifier analysis:

Error fetching program/map!

Kernel-Version: 5.3.0-28-generic

This is the Makefile I am using:

OBJS = xdpsock_kern.o

LLC ?= llc
CLANG ?= clang
INC_FLAGS = -nostdinc -isystem `$(CLANG) -print-file-name=include`
EXTRA_CFLAGS ?= -O2 -emit-llvm

# In case up-to-date headers are not installed locally in /usr/include,
# use source build.

linuxhdrs ?= /usr/src/linux-headers-5.1.0-050100

LINUXINCLUDE =  -I$(linuxhdrs)/arch/x86/include/uapi \
                -I$(linuxhdrs)/arch/x86/include/generated/uapi \
                -I$(linuxhdrs)/include/generated/uapi \
                -I$(linuxhdrs)/include/uapi \
                -I$(linuxhdrs)/include  \
                -I/bpf

prefix ?= /usr/local

INSTALLPATH = $(prefix)/lib/bpf

install_PROGRAM = install
install_DIR = install -dv

all:    $(OBJS)

.PHONY: clean

clean:
    rm -f $(OBJS)

INC_FLAGS = -nostdinc -isystem `$(CLANG) -print-file-name=include`

$(OBJS):  %.o:%.c
    $(CLANG) $(INC_FLAGS) \
                -D__KERNEL__ -D__ASM_SYSREG_H \
                -Wno-unused-value -Wno-pointer-sign \
                -Wno-compare-distinct-pointer-types \
                -Wno-gnu-variable-sized-type-not-at-end \
                -Wno-address-of-packed-member -Wno-tautological-compare \
                -Wno-unknown-warning-option \
                -I../include $(LINUXINCLUDE) \
                $(EXTRA_CFLAGS) -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@

install: $(OBJS)
    $(install_DIR) -d $(INSTALLPATH) ; \
    $(install_PROGRAM) $^ -t $(INSTALLPATH)

uninstall: $(OBJS)
    rm -rf $(INSTALLPATH)

Lockdown:

$ dmesg | grep Lockdown
[    1.283355] Lockdown: swapper/0: Hibernation is restricted; see man kernel_lockdown.7
[   11.313219] Lockdown: systemd: /dev/mem,kmem,port is restricted; see man kernel_lockdown.7
[   11.337794] Lockdown: systemd: BPF is restricted; see man kernel_lockdown.7
[   17.147844] Lockdown: Xorg: ioperm is restricted; see man kernel_lockdown.7

Edit: echo 1 > /proc/sys/kernel/sysrq + echo x > /proc/sysrq-trigger + Alt+SysRq+x indeed solves the problem - I can finally load the XDP-Program! Funny easter egg though. Thank you @Qeole!

binaryBigInt
  • 1,526
  • 2
  • 18
  • 44
  • Which kernel version? – pchaigno Feb 20 '20 at 14:44
  • @pchaigno It is `5.3.0-28-generic` – binaryBigInt Feb 20 '20 at 14:45
  • Are you able to load other BPF programs at all? (Asking because `bpf()` syscall might be disabled under certain conditions, in particular Secure Boot/Lockdown on some distros) – Qeole Feb 20 '20 at 14:50
  • @Qeole What kind of other BPF programs? I think it does not get much simples as mine – binaryBigInt Feb 20 '20 at 14:54
  • 1
    True for simplicity. I was speaking of programs attached to other hooks (TC, XDP, kprobes, any available). Like running something with `bcc` or `bpftrace`, if you have any installed. Or probing your system with `bpftool`. Or creating a BPF map. Anything that does a successful call to `bpf()` (which you can check with `strace -e bpf `). – Qeole Feb 20 '20 at 15:20
  • 1
    Or just by seeing your kernel version, would you be on Ubuntu 19.10 by any chance? With EFI and Secure boot activated? If so, can you read something about BPF when doing `dmesg | grep Lockdown` (details [here](https://gehrcke.de/2019/09/running-an-ebpf-program-may-require-lifting-the-kernel-lockdown/)). Might not be that, I'm just checking. – Qeole Feb 20 '20 at 15:24
  • @Qeole I added the output of `dmesg | grep Lockdown` to the original post. So what does that mean? – binaryBigInt Feb 20 '20 at 15:33
  • 1
    I think you are hitting [this change](https://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/disco/commit/?id=2a68c6): `bpf()` syscall used for e.g. loading BPF programs is deactivated on your system when Lockdown (security module trying to prevent kernel image modifications) is on. Workarounds: disable lockdown temporarily (I think there is a sysrq key sequence, procfs seems blocked now for that) or permanently (disable secure boot in BIOS) (maybe search for security implications). [Could be fixed](https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1863234) in the future. – Qeole Feb 20 '20 at 15:48
  • Thank you so much for your help. I am not working on my own computer so I am not sure whether I am allowed to disable Secure Boot. But to temporarily disable Lockdown I tried these commands from your link above: `echo 1 > /proc/sys/kernel/sysrq` `echo x > /proc/sysrq-trigger` but they didn't help :( – binaryBigInt Feb 20 '20 at 15:58
  • 1
    Yeah, Ubuntu even [blocked writing to `/proc/sysrq-trigger`](https://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/eoan/commit/?id=2124110d2db61f45a90cf20914505481bd9b111a) for disabling lockdown :/. Maybe it still works with keyboard [SysRq key](https://en.wikipedia.org/wiki/Magic_SysRq_key)? I have not tried it. Another workaround: Upgrade to kernel 5.4+, it has [lighter restrictions on `bpf()` usage](https://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/focal/commit/?id=9d1f8be5cf42b497a3bddf1d523f2bb142e9318c). – Qeole Feb 20 '20 at 16:07

1 Answers1

8

eBPF: Operation not permitted

There are several possible causes for a permission error (-EPERM returned by bpf(), which you can observe with strace -e bpf <command>) when working with eBPF. But no so many. Usually, they fall under one of the following items:

  • User does not have the required capabilities (CAP_SYS_ADMIN, CAP_NET_ADMIN, ... typically depending on the types of the programs being used). This is usually solved by running as root, who has all necessary capabilities. In your case you run with sudo, so you are covered.

  • Creating the BPF object (new map, or loading a program) would exceed the limit for the amount of memory that can be locked in the kernel by the user. This is usually solved (for root) by using ulimit -l <something_big> in the terminal, or setrlimit() in a C program. Very unlikely in your case, your program is very small and you did not mention having a lot of BPF objects loaded on your system.

  • There are a few more possibilies, like trying to write on maps that are “frozen” or read-only etc., or trying to use function calls for non-root users. These are usually for more advanced use cases and should not be hit with a program as simple as yours.

Lockdown, Secure Boot, EFI and (unfortunate) backports for bpf() restrictions

But the problem that you seem to be hitting could be related to something else. “Lockdown” is a security module that was merged into Linux 5.5 kernel. It aims at preventing users to modify the running Linux image. It turns out that several distributions decided to backport Lockdown to their kernels, and sometimes they picked patches that predated the final version that was merged to mainline Linux.

Ubuntu and Fedora, for example, have a bunch of custom patches to backport that feature to the kernels used in Disco/19.04 and Eoan/19.10 (kernel 5.3 for the latter, I don't remember for Disco). It includes a patch that completely disables the bpf() system call when Lockdown is activated, meaning that creating maps or loading BPF programs is not possible. Also, they enabled Lockdown by default when Secure Boot is activated, which, I think, is the default for machines booting with EFI.

See also this blog post: a good way to check if Lockdown is affecting your BPF usage is to try and load minimal programs, or to run dmesg | grep Lockdown to see if it says something like:

Lockdown: systemd: BPF is restricted; see man kernel_lockdown.7

So for Ubuntu 19.04 and 19.10, for example, you have to disable Lockdown to work with eBPF. This may be done with a physical stroke of the SysRq key + x (I have not tested), but NOT by writing to /proc/sysrq-trigger (Ubuntu disabled it for this operation). Alternatively, you can disable Secure Boot (in the BIOS or with mokutil, search for the relevant options on the Internet, and do not forget to check the security implications).

Note that Linux kernel 5.4 or newest has the mainline restrictions for bpf(), which do not deactivate the system call, so Focal/20.04 and newest will not be affected. Upgrading to a new kernel might thus be another workaround. I filed a ticket a few days ago to ask for this change to be backported (instead of deactivating bpf()) and the work is in progress, so by the time new readers look at the answer Lockdown impact on eBPF might well be mitigated (Edit: Should be fixed on Ubuntu 19.10 with kernel 5.3.0-43). Not sure how other distros handle this. And it will still have strong implications for tracing with eBPF, though.

Qeole
  • 8,284
  • 1
  • 24
  • 52
  • Thank you so much. After further investigation of the Linux source (especially `syscall.c` line `2823`) I came to a similar conclusion, because `CAP_SYS_ADMIN` is true and `sysctl_unprivileged_bpf_disabled` false. This really is a great answer @Qeole – binaryBigInt Feb 21 '20 at 09:19
  • 1
    You're welcome! Just as a note, `sysctl_unprivileged_bpf_disabled()` has very little effect here. If false you could use `bpf()` as non-root, but for the program type you try to load you would still get a `-EPERM` [a few instructions later](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/bpf/syscall.c/?h=v5.4#n1664) :). – Qeole Feb 21 '20 at 09:45