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) 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 */ 45 DEFINE_SIMPLE_JUMP_TABLE_PROG(ok, BPF_REG_0, 0, 0, __success __retval(1)) 46 DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_src_reg, BPF_REG_1, 0, 0, __failure __msg("BPF_JA|BPF_X uses reserved fields")) 47 DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_off, BPF_REG_0, 1, 0, __failure __msg("BPF_JA|BPF_X uses reserved fields")) 48 DEFINE_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 */ 54 SEC("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 */ 70 SEC("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; \ 76 jt0_%=: \ 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 129 DEFINE_INVALID_SIZE_PROG(u32, __failure __msg("Invalid read of 4 bytes from insn_array")) 130 DEFINE_INVALID_SIZE_PROG(u16, __failure __msg("Invalid read of 2 bytes from insn_array")) 131 DEFINE_INVALID_SIZE_PROG(u8, __failure __msg("Invalid read of 1 bytes from insn_array")) 132 133 SEC("socket") 134 __failure __msg("misaligned value access off 0+1+0 size 8") 135 __naked void jump_table_misaligned_access(void) 136 { 137 asm volatile (" \ 138 .pushsection .jumptables,\"\",@progbits; \ 139 jt0_%=: \ 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 161 SEC("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; \ 167 jt0_%=: \ 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 189 SEC("socket") 190 __failure __msg("invalid access to map value, value_size=16 off=-24 size=8") 191 __naked void jump_table_invalid_mem_acceess_neg(void) 192 { 193 asm volatile (" \ 194 .pushsection .jumptables,\"\",@progbits; \ 195 jt0_%=: \ 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 217 SEC("socket") 218 __success __retval(1) 219 __naked void jump_table_add_sub_ok(void) 220 { 221 asm volatile (" \ 222 .pushsection .jumptables,\"\",@progbits; \ 223 jt0_%=: \ 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 246 SEC("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; \ 252 jt0_%=: \ 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 304 DEFINE_JUMP_TABLE_USE_REG(0) 305 DEFINE_JUMP_TABLE_USE_REG(1) 306 DEFINE_JUMP_TABLE_USE_REG(2) 307 DEFINE_JUMP_TABLE_USE_REG(3) 308 DEFINE_JUMP_TABLE_USE_REG(4) 309 DEFINE_JUMP_TABLE_USE_REG(5) 310 DEFINE_JUMP_TABLE_USE_REG(6) 311 DEFINE_JUMP_TABLE_USE_REG(7) 312 DEFINE_JUMP_TABLE_USE_REG(8) 313 DEFINE_JUMP_TABLE_USE_REG(9) 314 315 __used static int test_subprog(void) 316 { 317 return 0; 318 } 319 320 SEC("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; \ 326 jt0_%=: \ 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 351 SEC("socket") 352 __success __retval(1) 353 __naked void jump_table_contains_non_unique_values(void) 354 { 355 asm volatile (" \ 356 .pushsection .jumptables,\"\",@progbits; \ 357 jt0_%=: \ 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 */ 388 389 char _license[] SEC("license") = "GPL"; 390