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