https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128471

            Bug ID: 128471
           Summary: BPF verifier rejects systemd's userns-restrict.bpf.c
                    (infinite loop detected at insn 48)
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: sjames at gcc dot gnu.org
            Blocks: 121462
  Target Milestone: ---

Created attachment 66091
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=66091&action=edit
bpftool output

The following is a reduced testcase from systemd reported to me by a friend:
```
/* Reduced from systemd's userns-restrict.bpf.c, uid_gid_map_translate().
 *
 * A counted loop (i < 340, early exit at i >= n) over an array whose address
 * the verifier only knows as a scalar, read element-wise with
 * bpf_probe_read_kernel().  With -O2, GCC's IVOPTS replaces the counter by a
 * pointer walk compared against a precomputed end pointer ("if rX != rY"):
 * both are unknown scalars, so the verifier cannot bound the loop. */

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

#define MAX_EXTENTS 340U

struct extent {
        __u32 first;
        __u32 lower_first;
        __u32 count;
};

static __attribute__((noinline))
int translate(const struct extent *array, __u32 n, __u32 id, __u32 *ret) {
        if (!ret)
                return -1;

        for (__u32 i = 0; i < MAX_EXTENTS; i++) {
                struct extent e;

                if (i >= n)
                        break;

                if (bpf_probe_read_kernel(&e, sizeof(e), array + i) != 0)
                        return -1;

                if (id >= e.first && id - e.first < e.count) {
                        *ret = e.lower_first + (id - e.first);
                        return 0;
                }
        }

        *ret = (__u32) -1;
        return 0;
}

SEC("raw_tracepoint/sys_enter")
int repro(struct bpf_raw_tracepoint_args *ctx) {
        const struct extent *array = (const struct extent *) ctx->args[0];
        __u32 n = (__u32) ctx->args[1];
        __u32 id = (__u32) (ctx->args[1] >> 32);
        __u32 out;

        if (translate(array, n, id, &out) < 0)
                return 1;
        return out == (__u32) -1;
}

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

```
$ bpf-unknown-none-gcc -O2 -mcpu=v3 -gbtf -mlittle-endian -c -idirafter
/usr/include repro.bpf.c -o repro-gcc.o
$ sudo bpftool prog load repro-gcc.o /sys/fs/bpf/repro
[...]
44: (07) r1 += -12                    ; frame1: R1=fp-12
45: (85) call bpf_probe_read_kernel#113       ; frame1: R0=scalar()
fp-8=mmmmmmmm fp-16=mmmmpppp
46: (55) if r0 != 0x0 goto pc+125     ; frame1: R0=0
;  @ repro.bpf.c:34
47: (61) r4 = *(u32 *)(r10 -12)
infinite loop detected at insn 48
cur state: frame1: R4=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0;
0xffffffff)) R6=scalar() R7=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0;
0xffffffff)) R8=scalar() R10=fp0 fp-8=mmmmmmmm fp-24=scalar() fp-32=fp[0]-4
old state: frame1: R4=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0;
0xffffffff)) R6=scalar() R7=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0;
0xffffffff)) R8=scalar() R10=fp0 fp-8=mmmmmmmm fp-24=scalar() fp-32=fp[0]-4
processed 193 insns (limit 1000000) max_states_per_insn 1 total_states 18
peak_states 18 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'repro': failed to load: -EINVAL
libbpf: failed to load object 'repro-gcc.o'
Error: failed to load object file
```


Referenced Bugs:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121462
[Bug 121462] [meta-bug] BPF verifier issues

Reply via email to