From: Josef Bacik input_action_end_bpf() validates the SRH of a packet and pulls it into the head before the program runs. If the program decapsulates the packet with bpf_lwt_seg6_action(SEG6_LOCAL_ACTION_END_DT6), bpf_update_srh_state() looks for a routing header in the inner packet and takes it for the new SRH as it is. Nobody has looked at that header, and whoever has sent the packet has written it. ipv6_find_hdr() finds it in a fragment of the skb as well, but hdrlen is then read at that offset from skb->data, past the end of the head. The header need not be an SRH, and its hdrlen need not fit into the packet, but it is marked as valid, and bpf_lwt_seg6_adjust_srh() and bpf_lwt_seg6_store_bytes() check their offsets against it. A program which then adds a TLV at the end of the SRH, where hdrlen has it, makes bpf_lwt_seg6_adjust_srh() move and write memory behind the head of the skb, as far as the sender likes within the 2 KB that hdrlen can say: BUG: KASAN: slab-out-of-bounds in bpf_skb_net_hdr_push+0x185/0x240 Read of size 2088 at addr ffff888107d6f09e by task srh/102 BUG: KASAN: slab-out-of-bounds in bpf_skb_net_hdr_push+0x1b5/0x240 Write of size 8 at addr ffff888107d6f8be by task srh/102 Use seg6_get_srh(), which pulls and validates the header. Without a valid SRH the state says that there is none, and the packet goes its way as before. The pull can move the head, so compute the data pointers behind it. Fixes: 486cdf21583e ("bpf: add End.DT6 action to bpf_lwt_seg6_action helper") Signed-off-by: Josef Bacik Signed-off-by: Alexei Starovoitov --- net/core/filter.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index a18702f89feb..6ac386dea4e8 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -7022,15 +7022,15 @@ static void bpf_update_srh_state(struct sk_buff *skb) { struct seg6_bpf_srh_state *srh_state = this_cpu_ptr(&seg6_bpf_srh_states); - int srhoff = 0; - if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) { - srh_state->srh = NULL; - } else { - srh_state->srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); + /* Behind a decapsulation this is a header that nobody has looked at */ + srh_state->srh = seg6_get_srh(skb, 0); + if (srh_state->srh) { srh_state->hdrlen = srh_state->srh->hdrlen << 3; srh_state->valid = true; } + /* it may have been pulled into the head */ + bpf_compute_data_pointers(skb); } BPF_CALL_4(bpf_lwt_seg6_action, struct sk_buff *, skb, -- 2.55.0