2018-05-14 23:40 GMT+01:00 Daniel Borkmann <dan...@iogearbox.net>:
> On 05/12/2018 07:25 PM, Mathieu Xhonneux wrote:
> [...]
>> +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.

>> +     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.

>> +     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.

Reply via email to