Search code examples
ebpf

eBPF as non-root can't freeze map .rodata when trying to make a hello world program


Sorry if this is a rather stupid issue, but I'm very unfamiliar with eBPF and need to do some non-root eBPF testing.

So I've been trying to get hello world working on non-root eBPF. I can't seem to find any sort of guides on this online so I used the basic hello world example guide which is for privileged users.

However, I can't seem to get it to work. I always run into the following error libbpf: Error freezing map(hello.rodata) as read-only: Operation not permitted libbpf: map 'hello.rodata': failed to create: Operation not permitted(-1) libbpf: failed to load object 'hello.bpf.o' bpf_object__load: Operation not permitted.

I don't use any ebpf maps it is purely the map that is automatically used by ebpf. I'm doing a err = bpf_object__load(obj); call to load the ebpf program causing the crash.

I should add that the eBPF program has SEC("tracepoint/syscalls/sys_enter_execve"). I can't seem to find anything online on what a non-root user can/can't do so I'm unsure if you can set a tracepoint ebpf program.

Also this error happens when I do ./hello but it does work properly when I run as root i.e. sudo ./hello.

Any help would be appreciated!

Thanks!

I've tried doing different ways of loading the bpf program. I've also spent hours looking online for any sort of guide on how to make an eBPF hello world for non-root users but I can't seem to find anything. So if you have any links to non-root eBPF guides that would also be really appreciated :)

I should also add that unprivileged eBPF is enabled.

[edit] Added in my code

Code to load ebpf program.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/resource.h>

#include <bpf/libbpf.h>
#include <bpf/bpf.h>

// #include "hello.skel.h"


void read_trace_pipe(void)
{
    int trace_fd;

    trace_fd = open("/sys/kernel/debug/tracing/trace_pipe", O_RDONLY, 0);
    if (trace_fd < 0)
        return;

    while (1) {
        static char buf[4096];
        ssize_t sz;

        sz = read(trace_fd, buf, sizeof(buf) - 1);
        if (sz > 0) {
            buf[sz] = 0;
            puts(buf);
        }
    }
}

int main(void)
{
    // struct hello_bpf *obj;
    struct bpf_object *obj;
    int err = 0;

    struct rlimit rlim = {
        .rlim_cur = 512UL << 20,
        .rlim_max = 512UL << 20,
    };

    err = setrlimit(RLIMIT_MEMLOCK, &rlim);
    if (err) {
        fprintf(stderr, "failed to change rlimit\n");
        return 1;
    }

    // obj = hello_bpf__open();
    // if (!obj) {
    //  fprintf(stderr, "failed to open and/or load BPF object\n");
    //  return 1;
    // }

    // err = hello_bpf__load(obj);
    // if (err) {
    //  fprintf(stderr, "failed to load BPF object %d\n", err);
    //  goto cleanup;
    // }

    // err = hello_bpf__attach(obj);
    // if (err) {
    //  fprintf(stderr, "failed to attach BPF programs\n");
    //  goto cleanup;
    // }

    struct bpf_program *prog;
    int prog_fd;

    obj = bpf_object__open_file("hello.bpf.o", NULL);
    if (!obj) {
        fprintf(stderr, "Failed to open eBPF object file\n");
        goto cleanup;
    }

    // Load BPF program
    err = bpf_object__load(obj);
    if (err) {
        perror("bpf_object__load");
        return 1;
    }

    prog = bpf_object__find_program_by_name(obj, "tracepoint__syscalls__sys_enter_execve");
    if (!prog) {
      fprintf(stderr, "bpf_object__find_program_by_title failed\n");
      goto cleanup;
    }

    // prog_fd = bpf_program__fd(prog);
    // if (prog_fd < 0) {
    //     fprintf(stderr, "Failed to get file descriptor for eBPF program\n");
    //     goto cleanup;
    // }

    bpf_program__attach(prog);

    read_trace_pipe();

cleanup:
    // hello_bpf__destroy(obj);
    return err != 0;
}

EBPF program:

#include "vmlinux.h"
#include <bpf_helpers.h>

SEC("tracepoint/syscalls/sys_enter_execve")
int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter *ctx)
{
    bpf_printk("My Hello world!\n");
    return 0;
}

char LICENSE[] SEC("license") = "GPL";

Solution

  • There are several ways to create the constant string "My Hello world!\n" in BPF and it seems your loader is choosing to use a frozen map to hold the string. That can probably be worked around as I've seen bcc use a simple literal to hold strings instead of a map.

    However, bpf_printk is not available without elevated privileges. So even if you could avoid the map for the constant string, you still wouldn't be able to load this program without privileges.

    Specifically, bpf_printk requires CAP_SYS_ADMIN or CAP_BPF and CAP_PERFMON: https://elixir.bootlin.com/linux/v6.7-rc8/source/kernel/bpf/helpers.c#L1792.

    In general, unprivileged BPF programs are very limited. As you've noticed, few program types are available, but also few helpers, and the verifier imposes various stricter limits (ex. program size, program complexity, speculative execution).