Hello everybody,
in my XDP eBPF program I'm trying to initialize an array with a brace-enclosed list, however my code is rejected by the verifier.
Here is a simple piece of code to replicate the problem:
#include <linux/bpf.h>
#ifndef __section
# define __section(NAME) \
__attribute__((section(NAME), used))
#endif
#ifndef BPF_FUNC
# define BPF_FUNC(NAME, ...) \
(*NAME)(__VA_ARGS__) = (void *)BPF_FUNC_##NAME
#endif
#ifndef printk
# define printk(fmt, ...) \
({ \
char ____fmt[] = fmt; \
trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
})
#endif
static void BPF_FUNC(trace_printk, const char *fmt, int fmt_size, ...);
__section("prog")
int xdp_prog(struct xdp_md *ctx) {
int i;
int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
#pragma nounroll
for (i = 0; i < 10; i++) {
printk("%d", array[i]);
}
return XDP_PASS;
}
char __license[] __section("license") = "GPL"
This is the error reported by the verifier:
0: (b7) r6 = 0
1: (b7) r7 = 25637
2: (b7) r8 = 0
3: (73) *(u8 *)(r10 -2) = r6
last_idx 3 first_idx 0
regs=40 stack=0 before 2: (b7) r8 = 0
regs=40 stack=0 before 1: (b7) r7 = 25637
regs=40 stack=0 before 0: (b7) r6 = 0
4: (6b) *(u16 *)(r10 -4) = r7
5: (18) r1 = 0x0
7: (0f) r1 += r8
8: (61) r3 = *(u32 *)(r1 +0)
R1 invalid mem access 'inv'
processed 8 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
I tried compiling with clang-6 and clang-9 with optimizaton set to O1 and O2, but I get this error in all cases.
If I initialize the array in another way (e.g. with a loop) the program works correctly.
The eBPF bytecode generated by clang is the following:
.text
.file "test.c"
.section prog,"ax",@progbits
.globl xdp_prog # -- Begin function xdp_prog
.p2align 3
.type xdp_prog,@function
xdp_prog: # @xdp_prog
# %bb.0:
r6 = 0
r7 = 25637
r8 = 0
LBB0_1: # =>This Inner Loop Header: Depth=1
*(u8 *)(r10 - 2) = r6
*(u16 *)(r10 - 4) = r7
r1 = .L__const.xdp_prog.array ll
r1 += r8
r3 = *(u32 *)(r1 + 0)
r1 = r10
r1 += -4
r2 = 3
call 6
r8 += 4
if r8 != 24 goto LBB0_1
# %bb.2:
r0 = 2
exit
.Lfunc_end0:
.size xdp_prog, .Lfunc_end0-xdp_prog
# -- End function
.type .L__const.xdp_prog.array,@object # @__const.xdp_prog.array
.section .rodata,"a",@progbits
.p2align 2
.L__const.xdp_prog.array:
.long 0 # 0x0
.long 1 # 0x1
.long 2 # 0x2
.long 3 # 0x3
.long 4 # 0x4
.long 5 # 0x5
.long 6 # 0x6
.long 7 # 0x7
.long 8 # 0x8
.long 9 # 0x9
.size .L__const.xdp_prog.array, 40
.type .L__const.xdp_prog.____fmt,@object # @__const.xdp_prog.____fmt
.section .rodata.str1.1,"aMS",@progbits,1
.L__const.xdp_prog.____fmt:
.asciz "%d"
.size .L__const.xdp_prog.____fmt, 3
.type __license,@object # @__license
.section license,"aw",@progbits
.globl __license
__license:
.asciz "GPL"
.size __license, 4
.addrsig
.addrsig_sym xdp_prog
.addrsig_sym __license
It seems like the array in the stack is not initialized in the code. With some declarations the code works, for example decalring the array in the following way:
int array[10] = {0, 1, 2, 3};
everything works, the generated bytecode is the following:
.text
.file "test.c"
.section prog,"ax",@progbits
.globl xdp_prog # -- Begin function xdp_prog
.p2align 3
.type xdp_prog,@function
xdp_prog: # @xdp_prog
# %bb.0:
r1 = 2
*(u32 *)(r10 - 36) = r1
r6 = 0
*(u32 *)(r10 - 8) = r6
*(u32 *)(r10 - 12) = r6
*(u32 *)(r10 - 16) = r6
*(u32 *)(r10 - 20) = r6
*(u32 *)(r10 - 24) = r6
*(u32 *)(r10 - 28) = r6
*(u32 *)(r10 - 4) = r6
r1 = 3
*(u32 *)(r10 - 32) = r1
r1 = 1
*(u32 *)(r10 - 40) = r1
*(u8 *)(r10 - 42) = r6
r7 = 25637
*(u16 *)(r10 - 44) = r7
r1 = r10
r1 += -44
r2 = 3
r3 = 1
call 6
r8 = 4
LBB0_1: # =>This Inner Loop Header: Depth=1
r1 = r10
r1 += -40
r1 += r8
r3 = *(u32 *)(r1 + 0)
*(u8 *)(r10 - 42) = r6
*(u16 *)(r10 - 44) = r7
r1 = r10
r1 += -44
r2 = 3
call 6
r8 += 4
if r8 != 24 goto LBB0_1
# %bb.2:
r0 = 2
exit
.Lfunc_end0:
.size xdp_prog, .Lfunc_end0-xdp_prog
# -- End function
.type .L__const.xdp_prog.____fmt,@object # @__const.xdp_prog.____fmt
.section .rodata.str1.1,"aMS",@progbits,1
.L__const.xdp_prog.____fmt:
.asciz "%d"
.size .L__const.xdp_prog.____fmt, 3
.type __license,@object # @__license
.section license,"aw",@progbits
.globl __license
__license:
.asciz "GPL"
.size __license, 4
.addrsig
.addrsig_sym xdp_prog
.addrsig_sym __license
This time the array is correctly initialized into code.
Is this a clang bug?