1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include "bpf_misc.h" 7 8 #if (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64)) && \ 9 defined(__BPF_FEATURE_STACK_ARGUMENT) 10 11 __noinline __used __naked 12 static int subprog_bad_order_6args(int a, int b, int c, int d, int e, int f) 13 { 14 asm volatile ( 15 "*(u64 *)(r11 - 8) = r1;" 16 "r0 = *(u64 *)(r11 + 8);" 17 "exit;" 18 ::: __clobber_all 19 ); 20 } 21 22 SEC("tc") 23 __description("stack_arg: r11 load after r11 store") 24 __failure 25 __msg("r11 load must be before any r11 store or call insn") 26 __btf_func_path("btf__verifier_stack_arg_order.bpf.o") 27 __naked void stack_arg_load_after_store(void) 28 { 29 asm volatile ( 30 "r1 = 1;" 31 "r2 = 2;" 32 "r3 = 3;" 33 "r4 = 4;" 34 "r5 = 5;" 35 "*(u64 *)(r11 - 8) = 6;" 36 "call subprog_bad_order_6args;" 37 "exit;" 38 ::: __clobber_all 39 ); 40 } 41 42 __noinline __used __naked 43 static int subprog_call_before_load_6args(int a, int b, int c, int d, int e, 44 int f) 45 { 46 asm volatile ( 47 "call %[bpf_get_prandom_u32];" 48 "r0 = *(u64 *)(r11 + 8);" 49 "exit;" 50 :: __imm(bpf_get_prandom_u32) 51 : __clobber_all 52 ); 53 } 54 55 SEC("tc") 56 __description("stack_arg: r11 load after a call") 57 __failure 58 __msg("r11 load must be before any r11 store or call insn") 59 __btf_func_path("btf__verifier_stack_arg_order.bpf.o") 60 __naked void stack_arg_load_after_call(void) 61 { 62 asm volatile ( 63 "r1 = 1;" 64 "r2 = 2;" 65 "r3 = 3;" 66 "r4 = 4;" 67 "r5 = 5;" 68 "*(u64 *)(r11 - 8) = 6;" 69 "call subprog_call_before_load_6args;" 70 "exit;" 71 ::: __clobber_all 72 ); 73 } 74 75 __noinline __used __naked 76 static int subprog_pruning_call_before_load_6args(int a, int b, int c, int d, 77 int e, int f) 78 { 79 asm volatile ( 80 "if r1 s> 0 goto l0_%=;" 81 "goto l1_%=;" 82 "l0_%=:" 83 "call %[bpf_get_prandom_u32];" 84 "l1_%=:" 85 "r0 = *(u64 *)(r11 + 8);" 86 "exit;" 87 :: __imm(bpf_get_prandom_u32) 88 : __clobber_all 89 ); 90 } 91 92 SEC("tc") 93 __description("stack_arg: pruning keeps r11 load ordering") 94 __failure 95 __flag(BPF_F_TEST_STATE_FREQ) 96 __msg("r11 load must be before any r11 store or call insn") 97 __btf_func_path("btf__verifier_stack_arg_order.bpf.o") 98 __naked void stack_arg_pruning_load_after_call(void) 99 { 100 asm volatile ( 101 "call %[bpf_get_prandom_u32];" 102 "r1 = r0;" 103 "r2 = 2;" 104 "r3 = 3;" 105 "r4 = 4;" 106 "r5 = 5;" 107 "*(u64 *)(r11 - 8) = 6;" 108 "call subprog_pruning_call_before_load_6args;" 109 "exit;" 110 :: __imm(bpf_get_prandom_u32) 111 : __clobber_all 112 ); 113 } 114 115 /* 116 * "bad_ptr": the first arg is 'long *', which is not a recognized pointer 117 * type for static subprogs (not ctx, dynptr, or tagged). btf_prepare_func_args() 118 * sets arg_cnt = 7 / stack_arg_cnt = 2, then fails with -EINVAL. The subprog 119 * is marked unreliable but the call still proceeds for static subprogs. 120 */ 121 __noinline __used __naked 122 static void subprog_bad_ptr_7args(long *a, int b, int c, int d, int e, int f, int g) 123 { 124 asm volatile ( 125 "r0 = *(u64 *)(r11 + 8);" 126 "r1 = *(u64 *)(r11 + 16);" 127 "exit;" 128 ::: __clobber_all 129 ); 130 } 131 132 SEC("tc") 133 __description("stack_arg: read without caller write") 134 __failure 135 __msg("callee expects 7 args, stack arg1 is not initialized") 136 __btf_func_path("btf__verifier_stack_arg_order.bpf.o") 137 __naked void stack_arg_read_without_write_1(void) 138 { 139 asm volatile ( 140 "r1 = 0;" 141 "r2 = 0;" 142 "r3 = 0;" 143 "r4 = 0;" 144 "r5 = 0;" 145 "call subprog_bad_ptr_7args;" 146 "exit;" 147 ::: __clobber_all 148 ); 149 } 150 151 SEC("tc") 152 __description("stack_arg: read with not-initialized caller write") 153 __failure 154 __msg("R0 !read_ok") 155 __btf_func_path("btf__verifier_stack_arg_order.bpf.o") 156 __naked void stack_arg_read_without_write_2(void) 157 { 158 asm volatile ( 159 "r1 = 0;" 160 "r2 = 0;" 161 "r3 = 0;" 162 "r4 = 0;" 163 "r5 = 0;" 164 "*(u64 *)(r11 - 8) = 0;" 165 "*(u64 *)(r11 - 16) = 0;" 166 "call subprog_bad_ptr_7args;" 167 "call subprog_bad_ptr_7args;" 168 "exit;" 169 ::: __clobber_all 170 ); 171 } 172 173 #else 174 175 SEC("socket") 176 __description("stack_arg order is not supported by compiler or jit, use a dummy test") 177 __success 178 int dummy_test(void) 179 { 180 return 0; 181 } 182 183 #endif 184 185 char _license[] SEC("license") = "GPL"; 186