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