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