Thread (11 messages) 11 messages, 3 authors, 2019-05-30

Re: [PATCH bpf-next v5 3/6] bpf: Add IPv6 Segment Routing helpers

From: Mathieu Xhonneux <hidden>
Date: 2018-05-16 21:59:50

 2018-05-14 23:40 GMT+01:00 Daniel Borkmann [off-list ref]:
On 05/12/2018 07:25 PM, Mathieu Xhonneux wrote:
[...]
quoted
+BPF_CALL_4(bpf_lwt_seg6_store_bytes, struct sk_buff *, skb, u32, offset,
+        const void *, from, u32, len)
+{
+#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
+     struct seg6_bpf_srh_state *srh_state =
+             this_cpu_ptr(&seg6_bpf_srh_states);
+     void *srh_tlvs, *srh_end, *ptr;
+     struct ipv6_sr_hdr *srh;
+     int srhoff = 0;
+
+     if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
+             return -EINVAL;
+
+     srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
+     srh_tlvs = (void *)((char *)srh + ((srh->first_segment + 1) << 4));
+     srh_end = (void *)((char *)srh + sizeof(*srh) + srh_state->hdrlen);
Do we need to check that this cannot go out of bounds wrt skb data?
input_action_bpf_end (which calls the BPF program) already verifies
using get_srh() that the whole SRH is accessible and is not out of
bounds.
The seg6 helpers (e.g. bpf_lwt_seg6_adjust_srh) then modify
srh_state->hdrlen following the evolution of the SRH in size. I don't
think that a check on srh_end is needed here as the SRH is already
verified once, and srh_state->hdrlen is then updated to keep this
bound correct.
quoted
+     ptr = skb->data + offset;
+     if (ptr >= srh_tlvs && ptr + len <= srh_end)
+             srh_state->valid = 0;
+     else if (ptr < (void *)&srh->flags ||
+              ptr + len > (void *)&srh->segments)
+             return -EFAULT;
+
+     if (unlikely(bpf_try_make_writable(skb, offset + len)))
+             return -EFAULT;
+
+     memcpy(ptr, from, len);
You have a use after free here. bpf_try_make_writable() is potentially changing
underlying skb->data (e.g. see pskb_expand_head()). Therefore memcpy()'ing into
cached ptr is invalid.
OK.
quoted
+     if (len > 0) {
+             ret = skb_cow_head(skb, len);
+             if (unlikely(ret < 0))
+                     return ret;
+
+             ret = bpf_skb_net_hdr_push(skb, offset, len);
+     } else {
+             ret = bpf_skb_net_hdr_pop(skb, offset, -1 * len);
+     }
+     if (unlikely(ret < 0))
+             return ret;
And here as well. You changed underlying pointers via skb_cow_head(), but in
the error path you leave the cached pointers that now point to already freed
buffer. Thus, you'd now be able to access the new skb data out of bounds since
cb->data_end is still the old one due to missing bpf_compute_data_pointers(skb).
Please fix and audit your whole series carefully against these types of subtle
bugs.
Right.
I went through the whole series again. I found a similar mistake in
bpf_push_seg6_encap, and added also a bpf_compute_data_pointers(skb)
there. I didn't find anything else, so I hope that we're covered here
(bpf_lwt_seg6_store_bytes, bpf_lwt_seg6_adjust_srh and
bpf_push_seg6_encap are the only functions modifying the packet in
this series).

Thanks. I'll submit a v6 ASAP.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help