xref: /linux/tools/testing/selftests/bpf/progs/stack_arg.c (revision 157317ba662a7c476320fdb334216154eaa8b856)
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