All of lore.kernel.org
 help / color / mirror / Atom feed
* Add variable offset to packet pointer in XDP/TC programs
@ 2021-11-08 10:39 Federico Parola
  2021-11-09  5:29 ` Alexei Starovoitov
  0 siblings, 1 reply; 4+ messages in thread
From: Federico Parola @ 2021-11-08 10:39 UTC (permalink / raw)
  To: bpf

Dear all,
I found out that every time an offset stored in a 2 (or more) bytes 
variable is added to a packet pointer subsequent checks against packet 
boundaries become ineffective.
Here is a toy example to test the problem (it doesn't do anything useful):

int test(struct __sk_buff *ctx) {
     void *data = (void *)(long)ctx->data;
     void *data_end = (void *)(long)ctx->data_end;

     /* Skipping an amount of bytes stored in __u8 works */
     if (data + sizeof(__u8) > data_end)
         return TC_ACT_OK;
     bpf_trace_printk("Skipping %d bytes", *(__u8 *)data);
     data += *(__u8 *)data;

     /* Skipping an amount of bytes stored in __u16 works but... */
     if (data + sizeof(__u16) > data_end)
         return TC_ACT_OK;
     bpf_trace_printk("Skipping %d bytes", *(__u16 *)data);
     data += *(__u16 *)data;

     /* ...this check is not effective and packet access is rejected */
     if (data + sizeof(__u8) > data_end)
         return TC_ACT_OK;
     bpf_trace_printk("Next byte is %x", *(__u8 *)data);

     return TC_ACT_OK;
}

My practical use case would be skipping variable-size TLS header 
extensions until I reach the desired one (the length of these options is 
2 bytes long).
Another use case can be found here: 
https://lists.iovisor.org/g/iovisor-dev/topic/access_packet_payload_in_tc/86442134
After I use the bpf_skb_pull_data() I would like to directly jump to the 
part of packet I was working on and avoid re-parsing everything from 
scratch, however if I save the offset in a 2 bytes variable and then add 
it to the packet pointer I'm no longer able to access it (if the offset 
is stored in a 1 byte var everything works).

Is this a verifier bug?

Best regards,
Federico Parola

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Add variable offset to packet pointer in XDP/TC programs
  2021-11-08 10:39 Add variable offset to packet pointer in XDP/TC programs Federico Parola
@ 2021-11-09  5:29 ` Alexei Starovoitov
       [not found]   ` <59fac794-ae38-783c-dd02-7506283cc2c4@polito.it>
  0 siblings, 1 reply; 4+ messages in thread
From: Alexei Starovoitov @ 2021-11-09  5:29 UTC (permalink / raw)
  To: Federico Parola; +Cc: bpf

On Mon, Nov 8, 2021 at 6:04 AM Federico Parola
<federico.parola@polito.it> wrote:
>
> Dear all,
> I found out that every time an offset stored in a 2 (or more) bytes
> variable is added to a packet pointer subsequent checks against packet
> boundaries become ineffective.
> Here is a toy example to test the problem (it doesn't do anything useful):
>
> int test(struct __sk_buff *ctx) {
>      void *data = (void *)(long)ctx->data;
>      void *data_end = (void *)(long)ctx->data_end;
>
>      /* Skipping an amount of bytes stored in __u8 works */
>      if (data + sizeof(__u8) > data_end)
>          return TC_ACT_OK;
>      bpf_trace_printk("Skipping %d bytes", *(__u8 *)data);
>      data += *(__u8 *)data;
>
>      /* Skipping an amount of bytes stored in __u16 works but... */
>      if (data + sizeof(__u16) > data_end)
>          return TC_ACT_OK;
>      bpf_trace_printk("Skipping %d bytes", *(__u16 *)data);
>      data += *(__u16 *)data;
>
>      /* ...this check is not effective and packet access is rejected */
>      if (data + sizeof(__u8) > data_end)
>          return TC_ACT_OK;
>      bpf_trace_printk("Next byte is %x", *(__u8 *)data);
>
>      return TC_ACT_OK;
> }
>
> My practical use case would be skipping variable-size TLS header
> extensions until I reach the desired one (the length of these options is
> 2 bytes long).
> Another use case can be found here:
> https://lists.iovisor.org/g/iovisor-dev/topic/access_packet_payload_in_tc/86442134
> After I use the bpf_skb_pull_data() I would like to directly jump to the
> part of packet I was working on and avoid re-parsing everything from
> scratch, however if I save the offset in a 2 bytes variable and then add
> it to the packet pointer I'm no longer able to access it (if the offset
> is stored in a 1 byte var everything works).
>
> Is this a verifier bug?

It's because of:
        if (dst_reg->umax_value > MAX_PACKET_OFF ||
            dst_reg->umax_value + dst_reg->off > MAX_PACKET_OFF)
                /* Risk of overflow.  For instance, ptr + (1<<63) may be less
                 * than pkt_end, but that's because it's also less than pkt.
                 */
                return;

by adding u16 scalar the offset becomes bigger than MAX_PACKET_OFF.
Could you try clamping the value before 'data += ' ?

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Add variable offset to packet pointer in XDP/TC programs
       [not found]   ` <59fac794-ae38-783c-dd02-7506283cc2c4@polito.it>
@ 2021-11-09 16:13     ` Alexei Starovoitov
  2021-11-11  8:59       ` Federico Parola
  0 siblings, 1 reply; 4+ messages in thread
From: Alexei Starovoitov @ 2021-11-09 16:13 UTC (permalink / raw)
  To: Federico Parola, bpf

On Tue, Nov 9, 2021 at 5:47 AM Federico Parola
<federico.parola@polito.it> wrote:
>
> Thanks for your answer.

Please do not top post and don't drop mailing list.

> If I perform something like:
>
> *(__u16 *)data &= 0xefff;
> data += *(__u16 *)data;
>
> To limit the max value of my offset the program is accepted, is this
> what you mean with "clamping"?

kinda. I don't think you meant to mangle the packet in the above.

> So packet pointers are stored on 16 bits? And every time we add an
> offset we must guarantee not to overflow these size?

no. the pointers are 64-bit, but there could be additional alu ops
on them. So MAX_PACKET_OFF was picked as the practical limit.

Do you have a real life use case where you need to add full u16?

>
> On 09/11/21 06:29, Alexei Starovoitov wrote:
> > On Mon, Nov 8, 2021 at 6:04 AM Federico Parola
> > <federico.parola@polito.it> wrote:
> >>
> >> Dear all,
> >> I found out that every time an offset stored in a 2 (or more) bytes
> >> variable is added to a packet pointer subsequent checks against packet
> >> boundaries become ineffective.
> >> Here is a toy example to test the problem (it doesn't do anything useful):
> >>
> >> int test(struct __sk_buff *ctx) {
> >>       void *data = (void *)(long)ctx->data;
> >>       void *data_end = (void *)(long)ctx->data_end;
> >>
> >>       /* Skipping an amount of bytes stored in __u8 works */
> >>       if (data + sizeof(__u8) > data_end)
> >>           return TC_ACT_OK;
> >>       bpf_trace_printk("Skipping %d bytes", *(__u8 *)data);
> >>       data += *(__u8 *)data;
> >>
> >>       /* Skipping an amount of bytes stored in __u16 works but... */
> >>       if (data + sizeof(__u16) > data_end)
> >>           return TC_ACT_OK;
> >>       bpf_trace_printk("Skipping %d bytes", *(__u16 *)data);
> >>       data += *(__u16 *)data;
> >>
> >>       /* ...this check is not effective and packet access is rejected */
> >>       if (data + sizeof(__u8) > data_end)
> >>           return TC_ACT_OK;
> >>       bpf_trace_printk("Next byte is %x", *(__u8 *)data);
> >>
> >>       return TC_ACT_OK;
> >> }
> >>
> >> My practical use case would be skipping variable-size TLS header
> >> extensions until I reach the desired one (the length of these options is
> >> 2 bytes long).
> >> Another use case can be found here:
> >> https://lists.iovisor.org/g/iovisor-dev/topic/access_packet_payload_in_tc/86442134
> >> After I use the bpf_skb_pull_data() I would like to directly jump to the
> >> part of packet I was working on and avoid re-parsing everything from
> >> scratch, however if I save the offset in a 2 bytes variable and then add
> >> it to the packet pointer I'm no longer able to access it (if the offset
> >> is stored in a 1 byte var everything works).
> >>
> >> Is this a verifier bug?
> >
> > It's because of:
> >          if (dst_reg->umax_value > MAX_PACKET_OFF ||
> >              dst_reg->umax_value + dst_reg->off > MAX_PACKET_OFF)
> >                  /* Risk of overflow.  For instance, ptr + (1<<63) may be less
> >                   * than pkt_end, but that's because it's also less than pkt.
> >                   */
> >                  return;
> >
> > by adding u16 scalar the offset becomes bigger than MAX_PACKET_OFF.
> > Could you try clamping the value before 'data += ' ?
> >

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Add variable offset to packet pointer in XDP/TC programs
  2021-11-09 16:13     ` Alexei Starovoitov
@ 2021-11-11  8:59       ` Federico Parola
  0 siblings, 0 replies; 4+ messages in thread
From: Federico Parola @ 2021-11-11  8:59 UTC (permalink / raw)
  To: Alexei Starovoitov, bpf

On 09/11/21 17:13, Alexei Starovoitov wrote:
> On Tue, Nov 9, 2021 at 5:47 AM Federico Parola
> <federico.parola@polito.it> wrote:
>>
>> Thanks for your answer.
> 
> Please do not top post and don't drop mailing list.
> 
>> If I perform something like:
>>
>> *(__u16 *)data &= 0xefff;
>> data += *(__u16 *)data;
>>
>> To limit the max value of my offset the program is accepted, is this
>> what you mean with "clamping"?
> 
> kinda. I don't think you meant to mangle the packet in the above.
> 
>> So packet pointers are stored on 16 bits? And every time we add an
>> offset we must guarantee not to overflow these size?
> 
> no. the pointers are 64-bit, but there could be additional alu ops
> on them. So MAX_PACKET_OFF was picked as the practical limit.
> 
> Do you have a real life use case where you need to add full u16?

Definitely not, both my use cases can be solved limiting the size of the 
offset with a mask (I suppose a comparison with an upper limit works as 
well).
Thank you.

>>
>> On 09/11/21 06:29, Alexei Starovoitov wrote:
>>> On Mon, Nov 8, 2021 at 6:04 AM Federico Parola
>>> <federico.parola@polito.it> wrote:
>>>>
>>>> Dear all,
>>>> I found out that every time an offset stored in a 2 (or more) bytes
>>>> variable is added to a packet pointer subsequent checks against packet
>>>> boundaries become ineffective.
>>>> Here is a toy example to test the problem (it doesn't do anything useful):
>>>>
>>>> int test(struct __sk_buff *ctx) {
>>>>        void *data = (void *)(long)ctx->data;
>>>>        void *data_end = (void *)(long)ctx->data_end;
>>>>
>>>>        /* Skipping an amount of bytes stored in __u8 works */
>>>>        if (data + sizeof(__u8) > data_end)
>>>>            return TC_ACT_OK;
>>>>        bpf_trace_printk("Skipping %d bytes", *(__u8 *)data);
>>>>        data += *(__u8 *)data;
>>>>
>>>>        /* Skipping an amount of bytes stored in __u16 works but... */
>>>>        if (data + sizeof(__u16) > data_end)
>>>>            return TC_ACT_OK;
>>>>        bpf_trace_printk("Skipping %d bytes", *(__u16 *)data);
>>>>        data += *(__u16 *)data;
>>>>
>>>>        /* ...this check is not effective and packet access is rejected */
>>>>        if (data + sizeof(__u8) > data_end)
>>>>            return TC_ACT_OK;
>>>>        bpf_trace_printk("Next byte is %x", *(__u8 *)data);
>>>>
>>>>        return TC_ACT_OK;
>>>> }
>>>>
>>>> My practical use case would be skipping variable-size TLS header
>>>> extensions until I reach the desired one (the length of these options is
>>>> 2 bytes long).
>>>> Another use case can be found here:
>>>> https://lists.iovisor.org/g/iovisor-dev/topic/access_packet_payload_in_tc/86442134
>>>> After I use the bpf_skb_pull_data() I would like to directly jump to the
>>>> part of packet I was working on and avoid re-parsing everything from
>>>> scratch, however if I save the offset in a 2 bytes variable and then add
>>>> it to the packet pointer I'm no longer able to access it (if the offset
>>>> is stored in a 1 byte var everything works).
>>>>
>>>> Is this a verifier bug?
>>>
>>> It's because of:
>>>           if (dst_reg->umax_value > MAX_PACKET_OFF ||
>>>               dst_reg->umax_value + dst_reg->off > MAX_PACKET_OFF)
>>>                   /* Risk of overflow.  For instance, ptr + (1<<63) may be less
>>>                    * than pkt_end, but that's because it's also less than pkt.
>>>                    */
>>>                   return;
>>>
>>> by adding u16 scalar the offset becomes bigger than MAX_PACKET_OFF.
>>> Could you try clamping the value before 'data += ' ?
>>>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-11-11  8:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-08 10:39 Add variable offset to packet pointer in XDP/TC programs Federico Parola
2021-11-09  5:29 ` Alexei Starovoitov
     [not found]   ` <59fac794-ae38-783c-dd02-7506283cc2c4@polito.it>
2021-11-09 16:13     ` Alexei Starovoitov
2021-11-11  8:59       ` Federico Parola

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.