Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2025 Isovalent */
3
4#include <linux/bpf.h>
5#include <bpf/bpf_helpers.h>
6#include "bpf_misc.h"
7#include "../../../include/linux/filter.h"
8
9#if defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_powerpc)
10
11#define DEFINE_SIMPLE_JUMP_TABLE_PROG(NAME, SRC_REG, OFF, IMM, OUTCOME) \
12 \
13 SEC("socket") \
14 OUTCOME \
15 __naked void jump_table_ ## NAME(void) \
16 { \
17 asm volatile (" \
18 .pushsection .jumptables,\"\",@progbits; \
19 jt0_%=: \
20 .quad ret0_%= - socket; \
21 .quad ret1_%= - socket; \
22 .size jt0_%=, 16; \
23 .global jt0_%=; \
24 .popsection; \
25 \
26 r0 = jt0_%= ll; \
27 r0 += 8; \
28 r0 = *(u64 *)(r0 + 0); \
29 .8byte %[gotox_r0]; \
30 ret0_%=: \
31 r0 = 0; \
32 exit; \
33 ret1_%=: \
34 r0 = 1; \
35 exit; \
36 " : \
37 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, (SRC_REG), (OFF) , (IMM))) \
38 : __clobber_all); \
39 }
40
41/*
42 * The first program which doesn't use reserved fields
43 * loads and works properly. The rest fail to load.
44 */
45DEFINE_SIMPLE_JUMP_TABLE_PROG(ok, BPF_REG_0, 0, 0, __success __retval(1))
46DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_src_reg, BPF_REG_1, 0, 0, __failure __msg("BPF_JA|BPF_X uses reserved fields"))
47DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_off, BPF_REG_0, 1, 0, __failure __msg("BPF_JA|BPF_X uses reserved fields"))
48DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_imm, BPF_REG_0, 0, 1, __failure __msg("BPF_JA|BPF_X uses reserved fields"))
49
50/*
51 * Gotox is forbidden when there is no jump table loaded
52 * which points to the sub-function where the gotox is used
53 */
54SEC("socket")
55__failure __msg("no jump tables found for subprog starting at 0")
56__naked void jump_table_no_jump_table(void)
57{
58 asm volatile (" \
59 .8byte %[gotox_r0]; \
60 r0 = 1; \
61 exit; \
62" : \
63 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0))
64 : __clobber_all);
65}
66
67/*
68 * Incorrect type of the target register, only PTR_TO_INSN allowed
69 */
70SEC("socket")
71__failure __msg("R1 has type scalar, expected PTR_TO_INSN")
72__naked void jump_table_incorrect_dst_reg_type(void)
73{
74 asm volatile (" \
75 .pushsection .jumptables,\"\",@progbits; \
76jt0_%=: \
77 .quad ret0_%= - socket; \
78 .quad ret1_%= - socket; \
79 .size jt0_%=, 16; \
80 .global jt0_%=; \
81 .popsection; \
82 \
83 r0 = jt0_%= ll; \
84 r0 += 8; \
85 r0 = *(u64 *)(r0 + 0); \
86 r1 = 42; \
87 .8byte %[gotox_r1]; \
88 ret0_%=: \
89 r0 = 0; \
90 exit; \
91 ret1_%=: \
92 r0 = 1; \
93 exit; \
94" : \
95 : __imm_insn(gotox_r1, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_1, 0, 0 , 0))
96 : __clobber_all);
97}
98
99#define DEFINE_INVALID_SIZE_PROG(READ_SIZE, OUTCOME) \
100 \
101 SEC("socket") \
102 OUTCOME \
103 __naked void jump_table_invalid_read_size_ ## READ_SIZE(void) \
104 { \
105 asm volatile (" \
106 .pushsection .jumptables,\"\",@progbits; \
107 jt0_%=: \
108 .quad ret0_%= - socket; \
109 .quad ret1_%= - socket; \
110 .size jt0_%=, 16; \
111 .global jt0_%=; \
112 .popsection; \
113 \
114 r0 = jt0_%= ll; \
115 r0 += 8; \
116 r0 = *(" #READ_SIZE " *)(r0 + 0); \
117 .8byte %[gotox_r0]; \
118 ret0_%=: \
119 r0 = 0; \
120 exit; \
121 ret1_%=: \
122 r0 = 1; \
123 exit; \
124 " : \
125 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) \
126 : __clobber_all); \
127 }
128
129DEFINE_INVALID_SIZE_PROG(u32, __failure __msg("Invalid read of 4 bytes from insn_array"))
130DEFINE_INVALID_SIZE_PROG(u16, __failure __msg("Invalid read of 2 bytes from insn_array"))
131DEFINE_INVALID_SIZE_PROG(u8, __failure __msg("Invalid read of 1 bytes from insn_array"))
132
133SEC("socket")
134__failure __msg("misaligned value access off 1+0 size 8")
135__naked void jump_table_misaligned_access(void)
136{
137 asm volatile (" \
138 .pushsection .jumptables,\"\",@progbits; \
139jt0_%=: \
140 .quad ret0_%= - socket; \
141 .quad ret1_%= - socket; \
142 .size jt0_%=, 16; \
143 .global jt0_%=; \
144 .popsection; \
145 \
146 r0 = jt0_%= ll; \
147 r0 += 1; \
148 r0 = *(u64 *)(r0 + 0); \
149 .8byte %[gotox_r0]; \
150 ret0_%=: \
151 r0 = 0; \
152 exit; \
153 ret1_%=: \
154 r0 = 1; \
155 exit; \
156" : \
157 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0))
158 : __clobber_all);
159}
160
161SEC("socket")
162__failure __msg("invalid access to map value, value_size=16 off=24 size=8")
163__naked void jump_table_invalid_mem_acceess_pos(void)
164{
165 asm volatile (" \
166 .pushsection .jumptables,\"\",@progbits; \
167jt0_%=: \
168 .quad ret0_%= - socket; \
169 .quad ret1_%= - socket; \
170 .size jt0_%=, 16; \
171 .global jt0_%=; \
172 .popsection; \
173 \
174 r0 = jt0_%= ll; \
175 r0 += 24; \
176 r0 = *(u64 *)(r0 + 0); \
177 .8byte %[gotox_r0]; \
178 ret0_%=: \
179 r0 = 0; \
180 exit; \
181 ret1_%=: \
182 r0 = 1; \
183 exit; \
184" : \
185 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0))
186 : __clobber_all);
187}
188
189SEC("socket")
190__failure __msg("R0 min value is negative")
191__naked void jump_table_invalid_mem_acceess_neg(void)
192{
193 asm volatile (" \
194 .pushsection .jumptables,\"\",@progbits; \
195jt0_%=: \
196 .quad ret0_%= - socket; \
197 .quad ret1_%= - socket; \
198 .size jt0_%=, 16; \
199 .global jt0_%=; \
200 .popsection; \
201 \
202 r0 = jt0_%= ll; \
203 r0 -= 24; \
204 r0 = *(u64 *)(r0 + 0); \
205 .8byte %[gotox_r0]; \
206 ret0_%=: \
207 r0 = 0; \
208 exit; \
209 ret1_%=: \
210 r0 = 1; \
211 exit; \
212" : \
213 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0))
214 : __clobber_all);
215}
216
217SEC("socket")
218__success __retval(1)
219__naked void jump_table_add_sub_ok(void)
220{
221 asm volatile (" \
222 .pushsection .jumptables,\"\",@progbits; \
223jt0_%=: \
224 .quad ret0_%= - socket; \
225 .quad ret1_%= - socket; \
226 .size jt0_%=, 16; \
227 .global jt0_%=; \
228 .popsection; \
229 \
230 r0 = jt0_%= ll; \
231 r0 -= 24; \
232 r0 += 32; \
233 r0 = *(u64 *)(r0 + 0); \
234 .8byte %[gotox_r0]; \
235 ret0_%=: \
236 r0 = 0; \
237 exit; \
238 ret1_%=: \
239 r0 = 1; \
240 exit; \
241" : \
242 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0))
243 : __clobber_all);
244}
245
246SEC("socket")
247__failure __msg("write into map forbidden, value_size=16 off=8 size=8")
248__naked void jump_table_no_writes(void)
249{
250 asm volatile (" \
251 .pushsection .jumptables,\"\",@progbits; \
252jt0_%=: \
253 .quad ret0_%= - socket; \
254 .quad ret1_%= - socket; \
255 .size jt0_%=, 16; \
256 .global jt0_%=; \
257 .popsection; \
258 \
259 r0 = jt0_%= ll; \
260 r0 += 8; \
261 r1 = 0xbeef; \
262 *(u64 *)(r0 + 0) = r1; \
263 .8byte %[gotox_r0]; \
264 ret0_%=: \
265 r0 = 0; \
266 exit; \
267 ret1_%=: \
268 r0 = 1; \
269 exit; \
270" : \
271 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0))
272 : __clobber_all);
273}
274
275#define DEFINE_JUMP_TABLE_USE_REG(REG) \
276 SEC("socket") \
277 __success __retval(1) \
278 __naked void jump_table_use_reg_r ## REG(void) \
279 { \
280 asm volatile (" \
281 .pushsection .jumptables,\"\",@progbits; \
282 jt0_%=: \
283 .quad ret0_%= - socket; \
284 .quad ret1_%= - socket; \
285 .size jt0_%=, 16; \
286 .global jt0_%=; \
287 .popsection; \
288 \
289 r0 = jt0_%= ll; \
290 r0 += 8; \
291 r" #REG " = *(u64 *)(r0 + 0); \
292 .8byte %[gotox_rX]; \
293 ret0_%=: \
294 r0 = 0; \
295 exit; \
296 ret1_%=: \
297 r0 = 1; \
298 exit; \
299 " : \
300 : __imm_insn(gotox_rX, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_ ## REG, 0, 0 , 0)) \
301 : __clobber_all); \
302 }
303
304DEFINE_JUMP_TABLE_USE_REG(0)
305DEFINE_JUMP_TABLE_USE_REG(1)
306DEFINE_JUMP_TABLE_USE_REG(2)
307DEFINE_JUMP_TABLE_USE_REG(3)
308DEFINE_JUMP_TABLE_USE_REG(4)
309DEFINE_JUMP_TABLE_USE_REG(5)
310DEFINE_JUMP_TABLE_USE_REG(6)
311DEFINE_JUMP_TABLE_USE_REG(7)
312DEFINE_JUMP_TABLE_USE_REG(8)
313DEFINE_JUMP_TABLE_USE_REG(9)
314
315__used static int test_subprog(void)
316{
317 return 0;
318}
319
320SEC("socket")
321__failure __msg("jump table for insn 4 points outside of the subprog [0,10]")
322__naked void jump_table_outside_subprog(void)
323{
324 asm volatile (" \
325 .pushsection .jumptables,\"\",@progbits; \
326jt0_%=: \
327 .quad ret0_%= - socket; \
328 .quad ret1_%= - socket; \
329 .quad ret_out_%= - socket; \
330 .size jt0_%=, 24; \
331 .global jt0_%=; \
332 .popsection; \
333 \
334 r0 = jt0_%= ll; \
335 r0 += 8; \
336 r0 = *(u64 *)(r0 + 0); \
337 .8byte %[gotox_r0]; \
338 ret0_%=: \
339 r0 = 0; \
340 exit; \
341 ret1_%=: \
342 r0 = 1; \
343 call test_subprog; \
344 exit; \
345 ret_out_%=: \
346" : \
347 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0))
348 : __clobber_all);
349}
350
351SEC("socket")
352__success __retval(1)
353__naked void jump_table_contains_non_unique_values(void)
354{
355 asm volatile (" \
356 .pushsection .jumptables,\"\",@progbits; \
357jt0_%=: \
358 .quad ret0_%= - socket; \
359 .quad ret1_%= - socket; \
360 .quad ret0_%= - socket; \
361 .quad ret1_%= - socket; \
362 .quad ret0_%= - socket; \
363 .quad ret1_%= - socket; \
364 .quad ret0_%= - socket; \
365 .quad ret1_%= - socket; \
366 .quad ret0_%= - socket; \
367 .quad ret1_%= - socket; \
368 .size jt0_%=, 80; \
369 .global jt0_%=; \
370 .popsection; \
371 \
372 r0 = jt0_%= ll; \
373 r0 += 8; \
374 r0 = *(u64 *)(r0 + 0); \
375 .8byte %[gotox_r0]; \
376 ret0_%=: \
377 r0 = 0; \
378 exit; \
379 ret1_%=: \
380 r0 = 1; \
381 exit; \
382" : \
383 : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0))
384 : __clobber_all);
385}
386
387#endif /* __TARGET_ARCH_x86 || __TARGET_ARCH_arm64 || __TARGET_ARCH_powerpc*/
388
389char _license[] SEC("license") = "GPL";