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