Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'bpf: Support <8-byte scalar spill and refill'

Martin KaFai says:

====================

The verifier currently does not save the reg state when
spilling <8byte bounded scalar to the stack. The bpf program
will be incorrectly rejected when this scalar is refilled to
the reg and then used to offset into a packet header.
The later patch has a simplified bpf prog from a real use case
to demonstrate this case. The current work around is
to reparse the packet again such that this offset scalar
is close to where the packet data will be accessed to
avoid the spill. Thus, the header is parsed twice.

The llvm patch [1] will align the <8bytes spill to
the 8-byte stack address. This set is to make the necessary
changes in verifier to support <8byte scalar spill and refill.

[1] https://reviews.llvm.org/D109073

v2:
- Changed the xdpwall selftest in patch 3 to trigger a u32
spill at a non 8-byte aligned stack address. The v1 has
simplified the real example too much such that it only
triggers a u32 spill but does not spill at a non
8-byte aligned stack address.
- Changed README.rst in patch 3 to explain the llvm dependency
for the xdpwall test.
====================

Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+626 -27
+72 -27
kernel/bpf/verifier.c
··· 612 612 return btf_name_by_offset(btf, btf_type_by_id(btf, id)->name_off); 613 613 } 614 614 615 + /* The reg state of a pointer or a bounded scalar was saved when 616 + * it was spilled to the stack. 617 + */ 618 + static bool is_spilled_reg(const struct bpf_stack_state *stack) 619 + { 620 + return stack->slot_type[BPF_REG_SIZE - 1] == STACK_SPILL; 621 + } 622 + 623 + static void scrub_spilled_slot(u8 *stype) 624 + { 625 + if (*stype != STACK_INVALID) 626 + *stype = STACK_MISC; 627 + } 628 + 615 629 static void print_verifier_state(struct bpf_verifier_env *env, 616 630 const struct bpf_func_state *state) 617 631 { ··· 731 717 continue; 732 718 verbose(env, " fp%d", (-i - 1) * BPF_REG_SIZE); 733 719 print_liveness(env, state->stack[i].spilled_ptr.live); 734 - if (state->stack[i].slot_type[0] == STACK_SPILL) { 720 + if (is_spilled_reg(&state->stack[i])) { 735 721 reg = &state->stack[i].spilled_ptr; 736 722 t = reg->type; 737 723 verbose(env, "=%s", reg_type_str[t]); ··· 2387 2373 reg->precise = true; 2388 2374 } 2389 2375 for (j = 0; j < func->allocated_stack / BPF_REG_SIZE; j++) { 2390 - if (func->stack[j].slot_type[0] != STACK_SPILL) 2376 + if (!is_spilled_reg(&func->stack[j])) 2391 2377 continue; 2392 2378 reg = &func->stack[j].spilled_ptr; 2393 2379 if (reg->type != SCALAR_VALUE) ··· 2429 2415 } 2430 2416 2431 2417 while (spi >= 0) { 2432 - if (func->stack[spi].slot_type[0] != STACK_SPILL) { 2418 + if (!is_spilled_reg(&func->stack[spi])) { 2433 2419 stack_mask = 0; 2434 2420 break; 2435 2421 } ··· 2528 2514 return 0; 2529 2515 } 2530 2516 2531 - if (func->stack[i].slot_type[0] != STACK_SPILL) { 2517 + if (!is_spilled_reg(&func->stack[i])) { 2532 2518 stack_mask &= ~(1ull << i); 2533 2519 continue; 2534 2520 } ··· 2640 2626 } 2641 2627 2642 2628 static void save_register_state(struct bpf_func_state *state, 2643 - int spi, struct bpf_reg_state *reg) 2629 + int spi, struct bpf_reg_state *reg, 2630 + int size) 2644 2631 { 2645 2632 int i; 2646 2633 2647 2634 state->stack[spi].spilled_ptr = *reg; 2648 - state->stack[spi].spilled_ptr.live |= REG_LIVE_WRITTEN; 2635 + if (size == BPF_REG_SIZE) 2636 + state->stack[spi].spilled_ptr.live |= REG_LIVE_WRITTEN; 2649 2637 2650 - for (i = 0; i < BPF_REG_SIZE; i++) 2651 - state->stack[spi].slot_type[i] = STACK_SPILL; 2638 + for (i = BPF_REG_SIZE; i > BPF_REG_SIZE - size; i--) 2639 + state->stack[spi].slot_type[i - 1] = STACK_SPILL; 2640 + 2641 + /* size < 8 bytes spill */ 2642 + for (; i; i--) 2643 + scrub_spilled_slot(&state->stack[spi].slot_type[i - 1]); 2652 2644 } 2653 2645 2654 2646 /* check_stack_{read,write}_fixed_off functions track spill/fill of registers, ··· 2701 2681 env->insn_aux_data[insn_idx].sanitize_stack_spill = true; 2702 2682 } 2703 2683 2704 - if (reg && size == BPF_REG_SIZE && register_is_bounded(reg) && 2684 + if (reg && !(off % BPF_REG_SIZE) && register_is_bounded(reg) && 2705 2685 !register_is_null(reg) && env->bpf_capable) { 2706 2686 if (dst_reg != BPF_REG_FP) { 2707 2687 /* The backtracking logic can only recognize explicit ··· 2714 2694 if (err) 2715 2695 return err; 2716 2696 } 2717 - save_register_state(state, spi, reg); 2697 + save_register_state(state, spi, reg, size); 2718 2698 } else if (reg && is_spillable_regtype(reg->type)) { 2719 2699 /* register containing pointer is being spilled into stack */ 2720 2700 if (size != BPF_REG_SIZE) { ··· 2726 2706 verbose(env, "cannot spill pointers to stack into stack frame of the caller\n"); 2727 2707 return -EINVAL; 2728 2708 } 2729 - save_register_state(state, spi, reg); 2709 + save_register_state(state, spi, reg, size); 2730 2710 } else { 2731 2711 u8 type = STACK_MISC; 2732 2712 2733 2713 /* regular write of data into stack destroys any spilled ptr */ 2734 2714 state->stack[spi].spilled_ptr.type = NOT_INIT; 2735 2715 /* Mark slots as STACK_MISC if they belonged to spilled ptr. */ 2736 - if (state->stack[spi].slot_type[0] == STACK_SPILL) 2716 + if (is_spilled_reg(&state->stack[spi])) 2737 2717 for (i = 0; i < BPF_REG_SIZE; i++) 2738 - state->stack[spi].slot_type[i] = STACK_MISC; 2718 + scrub_spilled_slot(&state->stack[spi].slot_type[i]); 2739 2719 2740 2720 /* only mark the slot as written if all 8 bytes were written 2741 2721 * otherwise read propagation may incorrectly stop too soon ··· 2938 2918 struct bpf_func_state *state = vstate->frame[vstate->curframe]; 2939 2919 int i, slot = -off - 1, spi = slot / BPF_REG_SIZE; 2940 2920 struct bpf_reg_state *reg; 2941 - u8 *stype; 2921 + u8 *stype, type; 2942 2922 2943 2923 stype = reg_state->stack[spi].slot_type; 2944 2924 reg = &reg_state->stack[spi].spilled_ptr; 2945 2925 2946 - if (stype[0] == STACK_SPILL) { 2926 + if (is_spilled_reg(&reg_state->stack[spi])) { 2947 2927 if (size != BPF_REG_SIZE) { 2928 + u8 scalar_size = 0; 2929 + 2948 2930 if (reg->type != SCALAR_VALUE) { 2949 2931 verbose_linfo(env, env->insn_idx, "; "); 2950 2932 verbose(env, "invalid size of register fill\n"); 2951 2933 return -EACCES; 2952 2934 } 2953 - if (dst_regno >= 0) { 2954 - mark_reg_unknown(env, state->regs, dst_regno); 2955 - state->regs[dst_regno].live |= REG_LIVE_WRITTEN; 2956 - } 2935 + 2957 2936 mark_reg_read(env, reg, reg->parent, REG_LIVE_READ64); 2937 + if (dst_regno < 0) 2938 + return 0; 2939 + 2940 + for (i = BPF_REG_SIZE; i > 0 && stype[i - 1] == STACK_SPILL; i--) 2941 + scalar_size++; 2942 + 2943 + if (!(off % BPF_REG_SIZE) && size == scalar_size) { 2944 + /* The earlier check_reg_arg() has decided the 2945 + * subreg_def for this insn. Save it first. 2946 + */ 2947 + s32 subreg_def = state->regs[dst_regno].subreg_def; 2948 + 2949 + state->regs[dst_regno] = *reg; 2950 + state->regs[dst_regno].subreg_def = subreg_def; 2951 + } else { 2952 + for (i = 0; i < size; i++) { 2953 + type = stype[(slot - i) % BPF_REG_SIZE]; 2954 + if (type == STACK_SPILL) 2955 + continue; 2956 + if (type == STACK_MISC) 2957 + continue; 2958 + verbose(env, "invalid read from stack off %d+%d size %d\n", 2959 + off, i, size); 2960 + return -EACCES; 2961 + } 2962 + mark_reg_unknown(env, state->regs, dst_regno); 2963 + } 2964 + state->regs[dst_regno].live |= REG_LIVE_WRITTEN; 2958 2965 return 0; 2959 2966 } 2960 2967 for (i = 1; i < BPF_REG_SIZE; i++) { ··· 3012 2965 } 3013 2966 mark_reg_read(env, reg, reg->parent, REG_LIVE_READ64); 3014 2967 } else { 3015 - u8 type; 3016 - 3017 2968 for (i = 0; i < size; i++) { 3018 2969 type = stype[(slot - i) % BPF_REG_SIZE]; 3019 2970 if (type == STACK_MISC) ··· 4559 4514 goto mark; 4560 4515 } 4561 4516 4562 - if (state->stack[spi].slot_type[0] == STACK_SPILL && 4517 + if (is_spilled_reg(&state->stack[spi]) && 4563 4518 state->stack[spi].spilled_ptr.type == PTR_TO_BTF_ID) 4564 4519 goto mark; 4565 4520 4566 - if (state->stack[spi].slot_type[0] == STACK_SPILL && 4521 + if (is_spilled_reg(&state->stack[spi]) && 4567 4522 (state->stack[spi].spilled_ptr.type == SCALAR_VALUE || 4568 4523 env->allow_ptr_leaks)) { 4569 4524 if (clobber) { 4570 4525 __mark_reg_unknown(env, &state->stack[spi].spilled_ptr); 4571 4526 for (j = 0; j < BPF_REG_SIZE; j++) 4572 - state->stack[spi].slot_type[j] = STACK_MISC; 4527 + scrub_spilled_slot(&state->stack[spi].slot_type[j]); 4573 4528 } 4574 4529 goto mark; 4575 4530 } ··· 10401 10356 * return false to continue verification of this path 10402 10357 */ 10403 10358 return false; 10404 - if (i % BPF_REG_SIZE) 10359 + if (i % BPF_REG_SIZE != BPF_REG_SIZE - 1) 10405 10360 continue; 10406 - if (old->stack[spi].slot_type[0] != STACK_SPILL) 10361 + if (!is_spilled_reg(&old->stack[spi])) 10407 10362 continue; 10408 10363 if (!regsafe(env, &old->stack[spi].spilled_ptr, 10409 10364 &cur->stack[spi].spilled_ptr, idmap)) ··· 10610 10565 } 10611 10566 10612 10567 for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) { 10613 - if (state->stack[i].slot_type[0] != STACK_SPILL) 10568 + if (!is_spilled_reg(&state->stack[i])) 10614 10569 continue; 10615 10570 state_reg = &state->stack[i].spilled_ptr; 10616 10571 if (state_reg->type != SCALAR_VALUE ||
+13
tools/testing/selftests/bpf/README.rst
··· 242 242 .. Links 243 243 .. _clang reloc patch: https://reviews.llvm.org/D102712 244 244 .. _kernel llvm reloc: /Documentation/bpf/llvm_reloc.rst 245 + 246 + Clang dependencies for the u32 spill test (xdpwall) 247 + =================================================== 248 + The xdpwall selftest requires a change in `Clang 14`__. 249 + 250 + Without it, the xdpwall selftest will fail and the error message 251 + from running test_progs will look like: 252 + 253 + .. code-block:: console 254 + 255 + test_xdpwall:FAIL:Does LLVM have https://reviews.llvm.org/D109073? unexpected error: -4007 256 + 257 + __ https://reviews.llvm.org/D109073
+15
tools/testing/selftests/bpf/prog_tests/xdpwall.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2021 Facebook */ 3 + 4 + #include "test_progs.h" 5 + #include "xdpwall.skel.h" 6 + 7 + void test_xdpwall(void) 8 + { 9 + struct xdpwall *skel; 10 + 11 + skel = xdpwall__open_and_load(); 12 + ASSERT_OK_PTR(skel, "Does LLMV have https://reviews.llvm.org/D109073?"); 13 + 14 + xdpwall__destroy(skel); 15 + }
+365
tools/testing/selftests/bpf/progs/xdpwall.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2021 Facebook */ 3 + #include <stdbool.h> 4 + #include <stdint.h> 5 + #include <linux/stddef.h> 6 + #include <linux/if_ether.h> 7 + #include <linux/in.h> 8 + #include <linux/in6.h> 9 + #include <linux/ip.h> 10 + #include <linux/ipv6.h> 11 + #include <linux/tcp.h> 12 + #include <linux/udp.h> 13 + #include <linux/bpf.h> 14 + #include <linux/types.h> 15 + #include <bpf/bpf_endian.h> 16 + #include <bpf/bpf_helpers.h> 17 + 18 + enum pkt_parse_err { 19 + NO_ERR, 20 + BAD_IP6_HDR, 21 + BAD_IP4GUE_HDR, 22 + BAD_IP6GUE_HDR, 23 + }; 24 + 25 + enum pkt_flag { 26 + TUNNEL = 0x1, 27 + TCP_SYN = 0x2, 28 + QUIC_INITIAL_FLAG = 0x4, 29 + TCP_ACK = 0x8, 30 + TCP_RST = 0x10 31 + }; 32 + 33 + struct v4_lpm_key { 34 + __u32 prefixlen; 35 + __u32 src; 36 + }; 37 + 38 + struct v4_lpm_val { 39 + struct v4_lpm_key key; 40 + __u8 val; 41 + }; 42 + 43 + struct { 44 + __uint(type, BPF_MAP_TYPE_HASH); 45 + __uint(max_entries, 16); 46 + __type(key, struct in6_addr); 47 + __type(value, bool); 48 + } v6_addr_map SEC(".maps"); 49 + 50 + struct { 51 + __uint(type, BPF_MAP_TYPE_HASH); 52 + __uint(max_entries, 16); 53 + __type(key, __u32); 54 + __type(value, bool); 55 + } v4_addr_map SEC(".maps"); 56 + 57 + struct { 58 + __uint(type, BPF_MAP_TYPE_LPM_TRIE); 59 + __uint(max_entries, 16); 60 + __uint(key_size, sizeof(struct v4_lpm_key)); 61 + __uint(value_size, sizeof(struct v4_lpm_val)); 62 + __uint(map_flags, BPF_F_NO_PREALLOC); 63 + } v4_lpm_val_map SEC(".maps"); 64 + 65 + struct { 66 + __uint(type, BPF_MAP_TYPE_ARRAY); 67 + __uint(max_entries, 16); 68 + __type(key, int); 69 + __type(value, __u8); 70 + } tcp_port_map SEC(".maps"); 71 + 72 + struct { 73 + __uint(type, BPF_MAP_TYPE_ARRAY); 74 + __uint(max_entries, 16); 75 + __type(key, int); 76 + __type(value, __u16); 77 + } udp_port_map SEC(".maps"); 78 + 79 + enum ip_type { V4 = 1, V6 = 2 }; 80 + 81 + struct fw_match_info { 82 + __u8 v4_src_ip_match; 83 + __u8 v6_src_ip_match; 84 + __u8 v4_src_prefix_match; 85 + __u8 v4_dst_prefix_match; 86 + __u8 tcp_dp_match; 87 + __u16 udp_sp_match; 88 + __u16 udp_dp_match; 89 + bool is_tcp; 90 + bool is_tcp_syn; 91 + }; 92 + 93 + struct pkt_info { 94 + enum ip_type type; 95 + union { 96 + struct iphdr *ipv4; 97 + struct ipv6hdr *ipv6; 98 + } ip; 99 + int sport; 100 + int dport; 101 + __u16 trans_hdr_offset; 102 + __u8 proto; 103 + __u8 flags; 104 + }; 105 + 106 + static __always_inline struct ethhdr *parse_ethhdr(void *data, void *data_end) 107 + { 108 + struct ethhdr *eth = data; 109 + 110 + if (eth + 1 > data_end) 111 + return NULL; 112 + 113 + return eth; 114 + } 115 + 116 + static __always_inline __u8 filter_ipv6_addr(const struct in6_addr *ipv6addr) 117 + { 118 + __u8 *leaf; 119 + 120 + leaf = bpf_map_lookup_elem(&v6_addr_map, ipv6addr); 121 + 122 + return leaf ? *leaf : 0; 123 + } 124 + 125 + static __always_inline __u8 filter_ipv4_addr(const __u32 ipaddr) 126 + { 127 + __u8 *leaf; 128 + 129 + leaf = bpf_map_lookup_elem(&v4_addr_map, &ipaddr); 130 + 131 + return leaf ? *leaf : 0; 132 + } 133 + 134 + static __always_inline __u8 filter_ipv4_lpm(const __u32 ipaddr) 135 + { 136 + struct v4_lpm_key v4_key = {}; 137 + struct v4_lpm_val *lpm_val; 138 + 139 + v4_key.src = ipaddr; 140 + v4_key.prefixlen = 32; 141 + 142 + lpm_val = bpf_map_lookup_elem(&v4_lpm_val_map, &v4_key); 143 + 144 + return lpm_val ? lpm_val->val : 0; 145 + } 146 + 147 + 148 + static __always_inline void 149 + filter_src_dst_ip(struct pkt_info* info, struct fw_match_info* match_info) 150 + { 151 + if (info->type == V6) { 152 + match_info->v6_src_ip_match = 153 + filter_ipv6_addr(&info->ip.ipv6->saddr); 154 + } else if (info->type == V4) { 155 + match_info->v4_src_ip_match = 156 + filter_ipv4_addr(info->ip.ipv4->saddr); 157 + match_info->v4_src_prefix_match = 158 + filter_ipv4_lpm(info->ip.ipv4->saddr); 159 + match_info->v4_dst_prefix_match = 160 + filter_ipv4_lpm(info->ip.ipv4->daddr); 161 + } 162 + } 163 + 164 + static __always_inline void * 165 + get_transport_hdr(__u16 offset, void *data, void *data_end) 166 + { 167 + if (offset > 255 || data + offset > data_end) 168 + return NULL; 169 + 170 + return data + offset; 171 + } 172 + 173 + static __always_inline bool tcphdr_only_contains_flag(struct tcphdr *tcp, 174 + __u32 FLAG) 175 + { 176 + return (tcp_flag_word(tcp) & 177 + (TCP_FLAG_ACK | TCP_FLAG_RST | TCP_FLAG_SYN | TCP_FLAG_FIN)) == FLAG; 178 + } 179 + 180 + static __always_inline void set_tcp_flags(struct pkt_info *info, 181 + struct tcphdr *tcp) { 182 + if (tcphdr_only_contains_flag(tcp, TCP_FLAG_SYN)) 183 + info->flags |= TCP_SYN; 184 + else if (tcphdr_only_contains_flag(tcp, TCP_FLAG_ACK)) 185 + info->flags |= TCP_ACK; 186 + else if (tcphdr_only_contains_flag(tcp, TCP_FLAG_RST)) 187 + info->flags |= TCP_RST; 188 + } 189 + 190 + static __always_inline bool 191 + parse_tcp(struct pkt_info *info, void *transport_hdr, void *data_end) 192 + { 193 + struct tcphdr *tcp = transport_hdr; 194 + 195 + if (tcp + 1 > data_end) 196 + return false; 197 + 198 + info->sport = bpf_ntohs(tcp->source); 199 + info->dport = bpf_ntohs(tcp->dest); 200 + set_tcp_flags(info, tcp); 201 + 202 + return true; 203 + } 204 + 205 + static __always_inline bool 206 + parse_udp(struct pkt_info *info, void *transport_hdr, void *data_end) 207 + { 208 + struct udphdr *udp = transport_hdr; 209 + 210 + if (udp + 1 > data_end) 211 + return false; 212 + 213 + info->sport = bpf_ntohs(udp->source); 214 + info->dport = bpf_ntohs(udp->dest); 215 + 216 + return true; 217 + } 218 + 219 + static __always_inline __u8 filter_tcp_port(int port) 220 + { 221 + __u8 *leaf = bpf_map_lookup_elem(&tcp_port_map, &port); 222 + 223 + return leaf ? *leaf : 0; 224 + } 225 + 226 + static __always_inline __u16 filter_udp_port(int port) 227 + { 228 + __u16 *leaf = bpf_map_lookup_elem(&udp_port_map, &port); 229 + 230 + return leaf ? *leaf : 0; 231 + } 232 + 233 + static __always_inline bool 234 + filter_transport_hdr(void *transport_hdr, void *data_end, 235 + struct pkt_info *info, struct fw_match_info *match_info) 236 + { 237 + if (info->proto == IPPROTO_TCP) { 238 + if (!parse_tcp(info, transport_hdr, data_end)) 239 + return false; 240 + 241 + match_info->is_tcp = true; 242 + match_info->is_tcp_syn = (info->flags & TCP_SYN) > 0; 243 + 244 + match_info->tcp_dp_match = filter_tcp_port(info->dport); 245 + } else if (info->proto == IPPROTO_UDP) { 246 + if (!parse_udp(info, transport_hdr, data_end)) 247 + return false; 248 + 249 + match_info->udp_dp_match = filter_udp_port(info->dport); 250 + match_info->udp_sp_match = filter_udp_port(info->sport); 251 + } 252 + 253 + return true; 254 + } 255 + 256 + static __always_inline __u8 257 + parse_gue_v6(struct pkt_info *info, struct ipv6hdr *ip6h, void *data_end) 258 + { 259 + struct udphdr *udp = (struct udphdr *)(ip6h + 1); 260 + void *encap_data = udp + 1; 261 + 262 + if (udp + 1 > data_end) 263 + return BAD_IP6_HDR; 264 + 265 + if (udp->dest != bpf_htons(6666)) 266 + return NO_ERR; 267 + 268 + info->flags |= TUNNEL; 269 + 270 + if (encap_data + 1 > data_end) 271 + return BAD_IP6GUE_HDR; 272 + 273 + if (*(__u8 *)encap_data & 0x30) { 274 + struct ipv6hdr *inner_ip6h = encap_data; 275 + 276 + if (inner_ip6h + 1 > data_end) 277 + return BAD_IP6GUE_HDR; 278 + 279 + info->type = V6; 280 + info->proto = inner_ip6h->nexthdr; 281 + info->ip.ipv6 = inner_ip6h; 282 + info->trans_hdr_offset += sizeof(struct ipv6hdr) + sizeof(struct udphdr); 283 + } else { 284 + struct iphdr *inner_ip4h = encap_data; 285 + 286 + if (inner_ip4h + 1 > data_end) 287 + return BAD_IP6GUE_HDR; 288 + 289 + info->type = V4; 290 + info->proto = inner_ip4h->protocol; 291 + info->ip.ipv4 = inner_ip4h; 292 + info->trans_hdr_offset += sizeof(struct iphdr) + sizeof(struct udphdr); 293 + } 294 + 295 + return NO_ERR; 296 + } 297 + 298 + static __always_inline __u8 parse_ipv6_gue(struct pkt_info *info, 299 + void *data, void *data_end) 300 + { 301 + struct ipv6hdr *ip6h = data + sizeof(struct ethhdr); 302 + 303 + if (ip6h + 1 > data_end) 304 + return BAD_IP6_HDR; 305 + 306 + info->proto = ip6h->nexthdr; 307 + info->ip.ipv6 = ip6h; 308 + info->type = V6; 309 + info->trans_hdr_offset = sizeof(struct ethhdr) + sizeof(struct ipv6hdr); 310 + 311 + if (info->proto == IPPROTO_UDP) 312 + return parse_gue_v6(info, ip6h, data_end); 313 + 314 + return NO_ERR; 315 + } 316 + 317 + SEC("xdp") 318 + int edgewall(struct xdp_md *ctx) 319 + { 320 + void *data_end = (void *)(long)(ctx->data_end); 321 + void *data = (void *)(long)(ctx->data); 322 + struct fw_match_info match_info = {}; 323 + struct pkt_info info = {}; 324 + __u8 parse_err = NO_ERR; 325 + void *transport_hdr; 326 + struct ethhdr *eth; 327 + bool filter_res; 328 + __u32 proto; 329 + 330 + eth = parse_ethhdr(data, data_end); 331 + if (!eth) 332 + return XDP_DROP; 333 + 334 + proto = eth->h_proto; 335 + if (proto != bpf_htons(ETH_P_IPV6)) 336 + return XDP_DROP; 337 + 338 + if (parse_ipv6_gue(&info, data, data_end)) 339 + return XDP_DROP; 340 + 341 + if (info.proto == IPPROTO_ICMPV6) 342 + return XDP_PASS; 343 + 344 + if (info.proto != IPPROTO_TCP && info.proto != IPPROTO_UDP) 345 + return XDP_DROP; 346 + 347 + filter_src_dst_ip(&info, &match_info); 348 + 349 + transport_hdr = get_transport_hdr(info.trans_hdr_offset, data, 350 + data_end); 351 + if (!transport_hdr) 352 + return XDP_DROP; 353 + 354 + filter_res = filter_transport_hdr(transport_hdr, data_end, 355 + &info, &match_info); 356 + if (!filter_res) 357 + return XDP_DROP; 358 + 359 + if (match_info.is_tcp && !match_info.is_tcp_syn) 360 + return XDP_PASS; 361 + 362 + return XDP_DROP; 363 + } 364 + 365 + char LICENSE[] SEC("license") = "GPL";
+161
tools/testing/selftests/bpf/verifier/spill_fill.c
··· 104 104 .result = ACCEPT, 105 105 .retval = POINTER_VALUE, 106 106 }, 107 + { 108 + "Spill and refill a u32 const scalar. Offset to skb->data", 109 + .insns = { 110 + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 111 + offsetof(struct __sk_buff, data)), 112 + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, 113 + offsetof(struct __sk_buff, data_end)), 114 + /* r4 = 20 */ 115 + BPF_MOV32_IMM(BPF_REG_4, 20), 116 + /* *(u32 *)(r10 -8) = r4 */ 117 + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, -8), 118 + /* r4 = *(u32 *)(r10 -8) */ 119 + BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_10, -8), 120 + /* r0 = r2 */ 121 + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), 122 + /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv20 */ 123 + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), 124 + /* if (r0 > r3) R0=pkt,off=20 R2=pkt R3=pkt_end R4=inv20 */ 125 + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), 126 + /* r0 = *(u32 *)r2 R0=pkt,off=20,r=20 R2=pkt,r=20 R3=pkt_end R4=inv20 */ 127 + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), 128 + BPF_MOV64_IMM(BPF_REG_0, 0), 129 + BPF_EXIT_INSN(), 130 + }, 131 + .result = ACCEPT, 132 + .prog_type = BPF_PROG_TYPE_SCHED_CLS, 133 + }, 134 + { 135 + "Spill a u32 const, refill from another half of the uninit u32 from the stack", 136 + .insns = { 137 + /* r4 = 20 */ 138 + BPF_MOV32_IMM(BPF_REG_4, 20), 139 + /* *(u32 *)(r10 -8) = r4 */ 140 + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, -8), 141 + /* r4 = *(u32 *)(r10 -4) fp-8=????rrrr*/ 142 + BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_10, -4), 143 + BPF_MOV64_IMM(BPF_REG_0, 0), 144 + BPF_EXIT_INSN(), 145 + }, 146 + .result = REJECT, 147 + .errstr = "invalid read from stack off -4+0 size 4", 148 + .prog_type = BPF_PROG_TYPE_SCHED_CLS, 149 + }, 150 + { 151 + "Spill a u32 const scalar. Refill as u16. Offset to skb->data", 152 + .insns = { 153 + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 154 + offsetof(struct __sk_buff, data)), 155 + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, 156 + offsetof(struct __sk_buff, data_end)), 157 + /* r4 = 20 */ 158 + BPF_MOV32_IMM(BPF_REG_4, 20), 159 + /* *(u32 *)(r10 -8) = r4 */ 160 + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, -8), 161 + /* r4 = *(u16 *)(r10 -8) */ 162 + BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_10, -8), 163 + /* r0 = r2 */ 164 + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), 165 + /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=65535 */ 166 + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), 167 + /* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv,umax=65535 */ 168 + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), 169 + /* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv20 */ 170 + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), 171 + BPF_MOV64_IMM(BPF_REG_0, 0), 172 + BPF_EXIT_INSN(), 173 + }, 174 + .result = REJECT, 175 + .errstr = "invalid access to packet", 176 + .prog_type = BPF_PROG_TYPE_SCHED_CLS, 177 + }, 178 + { 179 + "Spill a u32 const scalar. Refill as u16 from fp-6. Offset to skb->data", 180 + .insns = { 181 + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 182 + offsetof(struct __sk_buff, data)), 183 + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, 184 + offsetof(struct __sk_buff, data_end)), 185 + /* r4 = 20 */ 186 + BPF_MOV32_IMM(BPF_REG_4, 20), 187 + /* *(u32 *)(r10 -8) = r4 */ 188 + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, -8), 189 + /* r4 = *(u16 *)(r10 -6) */ 190 + BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_10, -6), 191 + /* r0 = r2 */ 192 + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), 193 + /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=65535 */ 194 + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), 195 + /* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv,umax=65535 */ 196 + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), 197 + /* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv20 */ 198 + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), 199 + BPF_MOV64_IMM(BPF_REG_0, 0), 200 + BPF_EXIT_INSN(), 201 + }, 202 + .result = REJECT, 203 + .errstr = "invalid access to packet", 204 + .prog_type = BPF_PROG_TYPE_SCHED_CLS, 205 + }, 206 + { 207 + "Spill and refill a u32 const scalar at non 8byte aligned stack addr. Offset to skb->data", 208 + .insns = { 209 + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 210 + offsetof(struct __sk_buff, data)), 211 + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, 212 + offsetof(struct __sk_buff, data_end)), 213 + /* r4 = 20 */ 214 + BPF_MOV32_IMM(BPF_REG_4, 20), 215 + /* *(u32 *)(r10 -8) = r4 */ 216 + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, -8), 217 + /* *(u32 *)(r10 -4) = r4 */ 218 + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, -4), 219 + /* r4 = *(u32 *)(r10 -4), */ 220 + BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_10, -4), 221 + /* r0 = r2 */ 222 + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), 223 + /* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=U32_MAX */ 224 + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), 225 + /* if (r0 > r3) R0=pkt,umax=U32_MAX R2=pkt R3=pkt_end R4=inv */ 226 + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), 227 + /* r0 = *(u32 *)r2 R0=pkt,umax=U32_MAX R2=pkt R3=pkt_end R4=inv */ 228 + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), 229 + BPF_MOV64_IMM(BPF_REG_0, 0), 230 + BPF_EXIT_INSN(), 231 + }, 232 + .result = REJECT, 233 + .errstr = "invalid access to packet", 234 + .prog_type = BPF_PROG_TYPE_SCHED_CLS, 235 + }, 236 + { 237 + "Spill and refill a umax=40 bounded scalar. Offset to skb->data", 238 + .insns = { 239 + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 240 + offsetof(struct __sk_buff, data)), 241 + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, 242 + offsetof(struct __sk_buff, data_end)), 243 + BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_1, 244 + offsetof(struct __sk_buff, tstamp)), 245 + BPF_JMP_IMM(BPF_JLE, BPF_REG_4, 40, 2), 246 + BPF_MOV64_IMM(BPF_REG_0, 0), 247 + BPF_EXIT_INSN(), 248 + /* *(u32 *)(r10 -8) = r4 R4=inv,umax=40 */ 249 + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_4, -8), 250 + /* r4 = (*u32 *)(r10 - 8) */ 251 + BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_10, -8), 252 + /* r2 += r4 R2=pkt R4=inv,umax=40 */ 253 + BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_4), 254 + /* r0 = r2 R2=pkt,umax=40 R4=inv,umax=40 */ 255 + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), 256 + /* r2 += 20 R0=pkt,umax=40 R2=pkt,umax=40 */ 257 + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 20), 258 + /* if (r2 > r3) R0=pkt,umax=40 R2=pkt,off=20,umax=40 */ 259 + BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_3, 1), 260 + /* r0 = *(u32 *)r0 R0=pkt,r=20,umax=40 R2=pkt,off=20,r=20,umax=40 */ 261 + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 0), 262 + BPF_MOV64_IMM(BPF_REG_0, 0), 263 + BPF_EXIT_INSN(), 264 + }, 265 + .result = ACCEPT, 266 + .prog_type = BPF_PROG_TYPE_SCHED_CLS, 267 + },