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