1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ 3 4 #include <vmlinux.h> 5 #include <stdbool.h> 6 #include <bpf/bpf_helpers.h> 7 #include "bpf_kfuncs.h" 8 9 #define CLOCK_MONOTONIC 1 10 11 struct timer_elem { 12 struct bpf_timer timer; 13 }; 14 15 struct { 16 __uint(type, BPF_MAP_TYPE_ARRAY); 17 __uint(max_entries, 1); 18 __type(key, int); 19 __type(value, struct timer_elem); 20 } timer_map SEC(".maps"); 21 22 int timer_result; 23 24 #if (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64)) && \ 25 defined(__BPF_FEATURE_STACK_ARGUMENT) 26 27 const volatile bool has_stack_arg = true; 28 29 __noinline static int static_func_many_args(int a, int b, int c, int d, 30 int e, int f, int g, int h, 31 int i, int j) 32 { 33 return a + b + c + d + e + f + g + h + i + j; 34 } 35 36 __noinline int global_calls_many_args(int a, int b, int c) 37 { 38 return static_func_many_args(a, b, c, a + 3, a + 4, a + 5, a + 6, 39 a + 7, a + 8, a + 9); 40 } 41 42 SEC("tc") 43 int test_global_many_args(void) 44 { 45 return global_calls_many_args(1, 2, 3); 46 } 47 48 struct test_data { 49 long x; 50 long y; 51 }; 52 53 /* 1+2+3+4+5+6+7+8+9+10+20 = 75 */ 54 __noinline static long func_with_ptr_stack_arg(long a, long b, long c, long d, 55 long e, long f, long g, long h, 56 long i, struct test_data *p) 57 { 58 return a + b + c + d + e + f + g + h + i + p->x + p->y; 59 } 60 61 __noinline long global_ptr_stack_arg(long a, long b, long c, long d, long e) 62 { 63 struct test_data data = { .x = 10, .y = 20 }; 64 65 return func_with_ptr_stack_arg(a, b, c, d, e, a + 5, a + 6, a + 7, 66 a + 8, &data); 67 } 68 69 SEC("tc") 70 int test_bpf2bpf_ptr_stack_arg(void) 71 { 72 return global_ptr_stack_arg(1, 2, 3, 4, 5); 73 } 74 75 /* 1+2+3+4+5+6+7+10+8+20 = 66 */ 76 __noinline static long func_with_mix_stack_args(long a, long b, long c, long d, 77 long e, long f, long g, 78 struct test_data *p, 79 long h, struct test_data *q) 80 { 81 return a + b + c + d + e + f + g + p->x + h + q->y; 82 } 83 84 __noinline long global_mix_stack_args(long a, long b, long c, long d, long e) 85 { 86 struct test_data p = { .x = 10 }; 87 struct test_data q = { .y = 20 }; 88 89 return func_with_mix_stack_args(a, b, c, d, e, e + 1, e + 2, &p, 90 e + 3, &q); 91 } 92 93 SEC("tc") 94 int test_bpf2bpf_mix_stack_args(void) 95 { 96 return global_mix_stack_args(1, 2, 3, 4, 5); 97 } 98 99 /* 100 * Nesting test: func_outer calls func_inner, both with struct pointer 101 * as stack arg. 102 * 103 * func_inner: (a+1)+...+(i+1) + p->x + p->y 104 * = 2+3+4+5+6+7+8+9+10+10+20 = 84 105 */ 106 __noinline static long func_inner_ptr(long a, long b, long c, long d, 107 long e, long f, long g, long h, 108 long i, struct test_data *p) 109 { 110 return a + b + c + d + e + f + g + h + i + p->x + p->y; 111 } 112 113 __noinline static long func_outer_ptr(long a, long b, long c, long d, 114 long e, long f, long g, long h, 115 long i, struct test_data *p) 116 { 117 return func_inner_ptr(a + 1, b + 1, c + 1, d + 1, e + 1, 118 f + 1, g + 1, h + 1, i + 1, p); 119 } 120 121 __noinline long global_nesting_ptr(long a, long b, long c, long d, long e) 122 { 123 struct test_data data = { .x = 10, .y = 20 }; 124 125 return func_outer_ptr(a, b, c, d, e, a + 5, a + 6, a + 7, a + 8, 126 &data); 127 } 128 129 SEC("tc") 130 int test_bpf2bpf_nesting_stack_arg(void) 131 { 132 return global_nesting_ptr(1, 2, 3, 4, 5); 133 } 134 135 /* 1+2+3+4+5+6+7+8+9+sizeof(pkt_v4) = 45+54 = 99 */ 136 __noinline static long func_with_dynptr(long a, long b, long c, long d, 137 long e, long f, long g, long h, 138 long i, struct bpf_dynptr *ptr) 139 { 140 return a + b + c + d + e + f + g + h + i + bpf_dynptr_size(ptr); 141 } 142 143 __noinline long global_dynptr_stack_arg(void *ctx __arg_ctx, long a, long b, 144 long c, long d) 145 { 146 struct bpf_dynptr ptr; 147 148 bpf_dynptr_from_skb(ctx, 0, &ptr); 149 return func_with_dynptr(a, b, c, d, d + 1, d + 2, d + 3, d + 4, 150 d + 5, &ptr); 151 } 152 153 SEC("tc") 154 int test_bpf2bpf_dynptr_stack_arg(struct __sk_buff *skb) 155 { 156 return global_dynptr_stack_arg(skb, 1, 2, 3, 4); 157 } 158 159 /* foo1: a+b+c+d+e+f+g+h+i+j */ 160 __noinline static int foo1(int a, int b, int c, int d, int e, 161 int f, int g, int h, int i, int j) 162 { 163 return a + b + c + d + e + f + g + h + i + j; 164 } 165 166 /* foo2: a+b+c+d+e+f+g+h+i+j+k+l */ 167 __noinline static int foo2(int a, int b, int c, int d, int e, 168 int f, int g, int h, int i, int j, 169 int k, int l) 170 { 171 return a + b + c + d + e + f + g + h + i + j + k + l; 172 } 173 174 /* global_two_callees calls foo1 (5 stack args) and foo2 (7 stack args). 175 * The outgoing stack arg area is sized for foo2 (the larger callee). 176 * Stores for foo1 are a subset of the area used by foo2. 177 * Result: foo1(1..10) + foo2(1..12) = 55 + 78 = 133 178 * 179 * Pass a-e through so the compiler can't constant-fold the stack args away. 180 */ 181 __noinline int global_two_callees(int a, int b, int c, int d, int e) 182 { 183 int ret; 184 185 ret = foo1(a, b, c, d, e, a + 5, a + 6, a + 7, a + 8, a + 9); 186 ret += foo2(a, b, c, d, e, a + 5, a + 6, a + 7, a + 8, a + 9, 187 a + 10, a + 11); 188 return ret; 189 } 190 191 SEC("tc") 192 int test_two_callees(void) 193 { 194 return global_two_callees(1, 2, 3, 4, 5); 195 } 196 197 const volatile int timer_base = 10; 198 199 static int timer_cb_many_args(void *map, int *key, struct bpf_timer *timer) 200 { 201 int v = timer_base; 202 203 timer_result = static_func_many_args(v, v * 2, v * 3, v * 4, v * 5, 204 v * 6, v * 7, v * 8, v * 9, 205 v * 10); 206 return 0; 207 } 208 209 SEC("tc") 210 int test_async_cb_many_args(void) 211 { 212 struct timer_elem *elem; 213 int key = 0; 214 215 elem = bpf_map_lookup_elem(&timer_map, &key); 216 if (!elem) 217 return -1; 218 219 bpf_timer_init(&elem->timer, &timer_map, CLOCK_MONOTONIC); 220 bpf_timer_set_callback(&elem->timer, timer_cb_many_args); 221 bpf_timer_start(&elem->timer, 1, 0); 222 return 0; 223 } 224 225 #else 226 227 const volatile bool has_stack_arg = false; 228 229 SEC("tc") 230 int test_global_many_args(void) 231 { 232 return 0; 233 } 234 235 SEC("tc") 236 int test_bpf2bpf_ptr_stack_arg(void) 237 { 238 return 0; 239 } 240 241 SEC("tc") 242 int test_bpf2bpf_mix_stack_args(void) 243 { 244 return 0; 245 } 246 247 SEC("tc") 248 int test_bpf2bpf_nesting_stack_arg(void) 249 { 250 return 0; 251 } 252 253 SEC("tc") 254 int test_bpf2bpf_dynptr_stack_arg(struct __sk_buff *skb) 255 { 256 return 0; 257 } 258 259 SEC("tc") 260 int test_two_callees(void) 261 { 262 return 0; 263 } 264 265 SEC("tc") 266 int test_async_cb_many_args(void) 267 { 268 return 0; 269 } 270 271 #endif 272 273 char _license[] SEC("license") = "GPL"; 274