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