diff mbox series

[ovs-dev,v2,09/11] dp-packet: Ensure packet base is always non-NULL.

Message ID 20211221110322.14345.33610.stgit@dceara.remote.csb
State Changes Requested
Headers show
Series Fix UndefinedBehaviorSanitizer reported issues and enable it in CI. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed

Commit Message

Dumitru Ceara Dec. 21, 2021, 11:03 a.m. UTC
UB Sanitizer report:
  lib/dp-packet.h:297:39: runtime error: applying zero offset to null pointer
      #0 0x7946f5 in dp_packet_tail /root/ovs/./lib/dp-packet.h:297:39
      #1 0x794331 in dp_packet_tailroom /root/ovs/./lib/dp-packet.h:325:49
      #2 0x7942a0 in dp_packet_prealloc_tailroom /root/ovs/lib/dp-packet.c:297:16
      #3 0xc347cf in eth_compose /root/ovs/lib/packets.c:1061:5
      [...]

Signed-off-by: Dumitru Ceara <dceara@redhat.com>
---
 lib/netdev-dummy.c           |    6 +++---
 ofproto/bond.c               |    3 ++-
 ofproto/ofproto-dpif-trace.c |    4 ++--
 ofproto/ofproto-dpif-xlate.c |    4 ++--
 ofproto/ofproto-dpif.c       |    6 +++---
 utilities/ovs-ofctl.c        |    6 ++----
 6 files changed, 14 insertions(+), 15 deletions(-)

Comments

Aaron Conole Dec. 21, 2021, 3:44 p.m. UTC | #1
Dumitru Ceara <dceara@redhat.com> writes:

> UB Sanitizer report:
>   lib/dp-packet.h:297:39: runtime error: applying zero offset to null pointer
>       #0 0x7946f5 in dp_packet_tail /root/ovs/./lib/dp-packet.h:297:39
>       #1 0x794331 in dp_packet_tailroom /root/ovs/./lib/dp-packet.h:325:49
>       #2 0x7942a0 in dp_packet_prealloc_tailroom /root/ovs/lib/dp-packet.c:297:16
>       #3 0xc347cf in eth_compose /root/ovs/lib/packets.c:1061:5
>       [...]
>
> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
> ---
>  lib/netdev-dummy.c           |    6 +++---
>  ofproto/bond.c               |    3 ++-
>  ofproto/ofproto-dpif-trace.c |    4 ++--
>  ofproto/ofproto-dpif-xlate.c |    4 ++--
>  ofproto/ofproto-dpif.c       |    6 +++---
>  utilities/ovs-ofctl.c        |    6 ++----
>  6 files changed, 14 insertions(+), 15 deletions(-)
>
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 1f386b81bbaf..2bb6c38bd5ca 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -188,7 +188,7 @@ dummy_packet_stream_init(struct dummy_packet_stream *s, struct stream *stream)
>  {
>      int rxbuf_size = stream ? 2048 : 0;
>      s->stream = stream;
> -    dp_packet_init(&s->rxbuf, rxbuf_size);
> +    dp_packet_init(&s->rxbuf, MAX(rxbuf_size, ETH_HEADER_LEN));
>      ovs_list_init(&s->txq);
>  }
>  
> @@ -1149,7 +1149,7 @@ netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
>              if (flow.dl_type == htons(ETH_TYPE_ARP)
>                  && flow.nw_proto == ARP_OP_REQUEST
>                  && flow.nw_dst == dev->address.s_addr) {
> -                struct dp_packet *reply = dp_packet_new(0);
> +                struct dp_packet *reply = dp_packet_new(ETH_HEADER_LEN);
>                  compose_arp(reply, ARP_OP_REPLY, dev->hwaddr, flow.dl_src,
>                              false, flow.nw_dst, flow.nw_src);
>                  netdev_dummy_queue_packet(dev, reply, NULL, 0);
> @@ -1647,7 +1647,7 @@ eth_from_flow_str(const char *s, size_t packet_size,
>          return NULL;
>      }
>  
> -    packet = dp_packet_new(0);
> +    packet = dp_packet_new(MAX(packet_size, ETH_HEADER_LEN));
>      if (packet_size) {
>          flow_compose(packet, flow, NULL, 0);
>          if (dp_packet_size(packet) < packet_size) {
> diff --git a/ofproto/bond.c b/ofproto/bond.c
> index cdfdf0b9d8c0..8ab27ed09c3a 100644
> --- a/ofproto/bond.c
> +++ b/ofproto/bond.c
> @@ -809,7 +809,8 @@ bond_compose_learning_packet(struct bond *bond, const struct eth_addr eth_src,
>      flow.dl_src = eth_src;
>      member = choose_output_member(bond, &flow, NULL, vlan);
>  
> -    packet = dp_packet_new(0);
> +    packet = dp_packet_new(2 + ETH_HEADER_LEN + VLAN_HEADER_LEN
> +                           + ARP_ETH_HEADER_LEN);
>      compose_rarp(packet, eth_src);
>      if (vlan) {
>          eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(vlan));
> diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
> index 78a54c715dc7..b4a9974689d1 100644
> --- a/ofproto/ofproto-dpif-trace.c
> +++ b/ofproto/ofproto-dpif-trace.c
> @@ -245,7 +245,7 @@ parse_flow_and_packet(int argc, const char *argv[],
>  
>              struct dp_packet payload;
>              memset(&payload, 0, sizeof payload);
> -            dp_packet_init(&payload, 0);
> +            dp_packet_init(&payload, ETH_HEADER_LEN);
>              if (dp_packet_put_hex(&payload, argv[++i], NULL)[0] != '\0') {
>                  dp_packet_uninit(&payload);
>                  error = xstrdup("Trailing garbage in packet data");
> @@ -436,7 +436,7 @@ parse_flow_and_packet(int argc, const char *argv[],
>  
>      if (generate_packet) {
>          /* Generate a packet, as requested. */
> -        packet = dp_packet_new(0);
> +        packet = dp_packet_new(ETH_HEADER_LEN);
>          flow_compose(packet, flow, l7, l7_len);
>      } else if (packet) {
>          /* Use the metadata from the flow and the packet argument to
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 991e22e6b64b..c1fd4c585d56 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -3490,7 +3490,7 @@ tnl_send_nd_request(struct xlate_ctx *ctx, const struct xport *out_dev,
>  {
>      struct dp_packet packet;
>  
> -    dp_packet_init(&packet, 0);
> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>      compose_nd_ns(&packet, eth_src, ipv6_src, ipv6_dst);
>      compose_table_xlate(ctx, out_dev, &packet);
>      dp_packet_uninit(&packet);
> @@ -3503,7 +3503,7 @@ tnl_send_arp_request(struct xlate_ctx *ctx, const struct xport *out_dev,
>  {
>      struct dp_packet packet;
>  
> -    dp_packet_init(&packet, 0);
> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>      compose_arp(&packet, ARP_OP_REQUEST,
>                  eth_src, eth_addr_zero, true, ip_src, ip_dst);
>  
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index bc3df8ea1510..e44e365dd405 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -1268,7 +1268,7 @@ check_ct_eventmask(struct dpif_backer *backer)
>      nl_msg_end_nested(&actions, ct_start);
>  
>      /* Compose a dummy UDP packet. */
> -    dp_packet_init(&packet, 0);
> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>      flow_compose(&packet, &flow, NULL, 64);
>  
>      /* Execute the actions.  On older datapaths this fails with EINVAL, on
> @@ -1361,7 +1361,7 @@ check_ct_timeout_policy(struct dpif_backer *backer)
>      nl_msg_end_nested(&actions, ct_start);
>  
>      /* Compose a dummy UDP packet. */
> -    dp_packet_init(&packet, 0);
> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>      flow_compose(&packet, &flow, NULL, 64);
>  
>      /* Execute the actions.  On older datapaths this fails with EINVAL, on
> @@ -3501,7 +3501,7 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size)
>          struct dp_packet packet;
>          void *packet_pdu;
>  
> -        dp_packet_init(&packet, 0);
> +        dp_packet_init(&packet, ETH_HEADER_LEN);
>          packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
>                                   pdu_size);
>          memcpy(packet_pdu, pdu, pdu_size);
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index ede7f1e61ab8..55b5e1a4c95a 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -4902,15 +4902,13 @@ ofctl_compose_packet(struct ovs_cmdl_context *ctx)
>      }
>  
>      struct dp_packet p;
> -    memset(&p, 0, sizeof p);
> -    dp_packet_init(&p, 0);
> +    dp_packet_init(&p, ETH_HEADER_LEN);
>  
>      void *l7 = NULL;
>      size_t l7_len = 64;
>      if (ctx->argc > 2) {
>          struct dp_packet payload;
> -        memset(&payload, 0, sizeof payload);
> -        dp_packet_init(&payload, 0);
> +        dp_packet_init(&payload, ETH_HEADER_LEN);
>          if (dp_packet_put_hex(&payload, ctx->argv[2], NULL)[0] != '\0') {
>              ovs_fatal(0, "%s: trailing garbage in packet data", ctx->argv[2]);
>          }

If the issue is just in dp_packet_prealloc_tailroom() can we just do a
size check there?

-    if (size > dp_packet_tailroom(b)) {
+    /* check against pkt_size to start, because it is more likely to fail */
+    if (dp_packet_size(b) < size || size > dp_packet_tailroom(b)) {

Otherwise, I think there will be multiple reallocations with all of
these changes.

Maybe I missed a different ubsan flake - otherwise, we can also assert
that size must be non-zero when allocating a dp_packet, but that's a
bigger change.
Dumitru Ceara Jan. 4, 2022, 4:38 p.m. UTC | #2
On 12/21/21 16:44, Aaron Conole wrote:
> Dumitru Ceara <dceara@redhat.com> writes:
> 
>> UB Sanitizer report:
>>   lib/dp-packet.h:297:39: runtime error: applying zero offset to null pointer
>>       #0 0x7946f5 in dp_packet_tail /root/ovs/./lib/dp-packet.h:297:39
>>       #1 0x794331 in dp_packet_tailroom /root/ovs/./lib/dp-packet.h:325:49
>>       #2 0x7942a0 in dp_packet_prealloc_tailroom /root/ovs/lib/dp-packet.c:297:16
>>       #3 0xc347cf in eth_compose /root/ovs/lib/packets.c:1061:5
>>       [...]
>>
>> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
>> ---
>>  lib/netdev-dummy.c           |    6 +++---
>>  ofproto/bond.c               |    3 ++-
>>  ofproto/ofproto-dpif-trace.c |    4 ++--
>>  ofproto/ofproto-dpif-xlate.c |    4 ++--
>>  ofproto/ofproto-dpif.c       |    6 +++---
>>  utilities/ovs-ofctl.c        |    6 ++----
>>  6 files changed, 14 insertions(+), 15 deletions(-)
>>
>> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
>> index 1f386b81bbaf..2bb6c38bd5ca 100644
>> --- a/lib/netdev-dummy.c
>> +++ b/lib/netdev-dummy.c
>> @@ -188,7 +188,7 @@ dummy_packet_stream_init(struct dummy_packet_stream *s, struct stream *stream)
>>  {
>>      int rxbuf_size = stream ? 2048 : 0;
>>      s->stream = stream;
>> -    dp_packet_init(&s->rxbuf, rxbuf_size);
>> +    dp_packet_init(&s->rxbuf, MAX(rxbuf_size, ETH_HEADER_LEN));
>>      ovs_list_init(&s->txq);
>>  }
>>  
>> @@ -1149,7 +1149,7 @@ netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
>>              if (flow.dl_type == htons(ETH_TYPE_ARP)
>>                  && flow.nw_proto == ARP_OP_REQUEST
>>                  && flow.nw_dst == dev->address.s_addr) {
>> -                struct dp_packet *reply = dp_packet_new(0);
>> +                struct dp_packet *reply = dp_packet_new(ETH_HEADER_LEN);
>>                  compose_arp(reply, ARP_OP_REPLY, dev->hwaddr, flow.dl_src,
>>                              false, flow.nw_dst, flow.nw_src);
>>                  netdev_dummy_queue_packet(dev, reply, NULL, 0);
>> @@ -1647,7 +1647,7 @@ eth_from_flow_str(const char *s, size_t packet_size,
>>          return NULL;
>>      }
>>  
>> -    packet = dp_packet_new(0);
>> +    packet = dp_packet_new(MAX(packet_size, ETH_HEADER_LEN));
>>      if (packet_size) {
>>          flow_compose(packet, flow, NULL, 0);
>>          if (dp_packet_size(packet) < packet_size) {
>> diff --git a/ofproto/bond.c b/ofproto/bond.c
>> index cdfdf0b9d8c0..8ab27ed09c3a 100644
>> --- a/ofproto/bond.c
>> +++ b/ofproto/bond.c
>> @@ -809,7 +809,8 @@ bond_compose_learning_packet(struct bond *bond, const struct eth_addr eth_src,
>>      flow.dl_src = eth_src;
>>      member = choose_output_member(bond, &flow, NULL, vlan);
>>  
>> -    packet = dp_packet_new(0);
>> +    packet = dp_packet_new(2 + ETH_HEADER_LEN + VLAN_HEADER_LEN
>> +                           + ARP_ETH_HEADER_LEN);
>>      compose_rarp(packet, eth_src);
>>      if (vlan) {
>>          eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(vlan));
>> diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
>> index 78a54c715dc7..b4a9974689d1 100644
>> --- a/ofproto/ofproto-dpif-trace.c
>> +++ b/ofproto/ofproto-dpif-trace.c
>> @@ -245,7 +245,7 @@ parse_flow_and_packet(int argc, const char *argv[],
>>  
>>              struct dp_packet payload;
>>              memset(&payload, 0, sizeof payload);
>> -            dp_packet_init(&payload, 0);
>> +            dp_packet_init(&payload, ETH_HEADER_LEN);
>>              if (dp_packet_put_hex(&payload, argv[++i], NULL)[0] != '\0') {
>>                  dp_packet_uninit(&payload);
>>                  error = xstrdup("Trailing garbage in packet data");
>> @@ -436,7 +436,7 @@ parse_flow_and_packet(int argc, const char *argv[],
>>  
>>      if (generate_packet) {
>>          /* Generate a packet, as requested. */
>> -        packet = dp_packet_new(0);
>> +        packet = dp_packet_new(ETH_HEADER_LEN);
>>          flow_compose(packet, flow, l7, l7_len);
>>      } else if (packet) {
>>          /* Use the metadata from the flow and the packet argument to
>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>> index 991e22e6b64b..c1fd4c585d56 100644
>> --- a/ofproto/ofproto-dpif-xlate.c
>> +++ b/ofproto/ofproto-dpif-xlate.c
>> @@ -3490,7 +3490,7 @@ tnl_send_nd_request(struct xlate_ctx *ctx, const struct xport *out_dev,
>>  {
>>      struct dp_packet packet;
>>  
>> -    dp_packet_init(&packet, 0);
>> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>>      compose_nd_ns(&packet, eth_src, ipv6_src, ipv6_dst);
>>      compose_table_xlate(ctx, out_dev, &packet);
>>      dp_packet_uninit(&packet);
>> @@ -3503,7 +3503,7 @@ tnl_send_arp_request(struct xlate_ctx *ctx, const struct xport *out_dev,
>>  {
>>      struct dp_packet packet;
>>  
>> -    dp_packet_init(&packet, 0);
>> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>>      compose_arp(&packet, ARP_OP_REQUEST,
>>                  eth_src, eth_addr_zero, true, ip_src, ip_dst);
>>  
>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>> index bc3df8ea1510..e44e365dd405 100644
>> --- a/ofproto/ofproto-dpif.c
>> +++ b/ofproto/ofproto-dpif.c
>> @@ -1268,7 +1268,7 @@ check_ct_eventmask(struct dpif_backer *backer)
>>      nl_msg_end_nested(&actions, ct_start);
>>  
>>      /* Compose a dummy UDP packet. */
>> -    dp_packet_init(&packet, 0);
>> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>>      flow_compose(&packet, &flow, NULL, 64);
>>  
>>      /* Execute the actions.  On older datapaths this fails with EINVAL, on
>> @@ -1361,7 +1361,7 @@ check_ct_timeout_policy(struct dpif_backer *backer)
>>      nl_msg_end_nested(&actions, ct_start);
>>  
>>      /* Compose a dummy UDP packet. */
>> -    dp_packet_init(&packet, 0);
>> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>>      flow_compose(&packet, &flow, NULL, 64);
>>  
>>      /* Execute the actions.  On older datapaths this fails with EINVAL, on
>> @@ -3501,7 +3501,7 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size)
>>          struct dp_packet packet;
>>          void *packet_pdu;
>>  
>> -        dp_packet_init(&packet, 0);
>> +        dp_packet_init(&packet, ETH_HEADER_LEN);
>>          packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
>>                                   pdu_size);
>>          memcpy(packet_pdu, pdu, pdu_size);
>> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
>> index ede7f1e61ab8..55b5e1a4c95a 100644
>> --- a/utilities/ovs-ofctl.c
>> +++ b/utilities/ovs-ofctl.c
>> @@ -4902,15 +4902,13 @@ ofctl_compose_packet(struct ovs_cmdl_context *ctx)
>>      }
>>  
>>      struct dp_packet p;
>> -    memset(&p, 0, sizeof p);
>> -    dp_packet_init(&p, 0);
>> +    dp_packet_init(&p, ETH_HEADER_LEN);
>>  
>>      void *l7 = NULL;
>>      size_t l7_len = 64;
>>      if (ctx->argc > 2) {
>>          struct dp_packet payload;
>> -        memset(&payload, 0, sizeof payload);
>> -        dp_packet_init(&payload, 0);
>> +        dp_packet_init(&payload, ETH_HEADER_LEN);
>>          if (dp_packet_put_hex(&payload, ctx->argv[2], NULL)[0] != '\0') {
>>              ovs_fatal(0, "%s: trailing garbage in packet data", ctx->argv[2]);
>>          }
> 
> If the issue is just in dp_packet_prealloc_tailroom() can we just do a
> size check there?
> 
> -    if (size > dp_packet_tailroom(b)) {
> +    /* check against pkt_size to start, because it is more likely to fail */
> +    if (dp_packet_size(b) < size || size > dp_packet_tailroom(b)) {

This creates issues.  I think due to the fact that the packet tailroom
might shrink in some cases now.

> 
> Otherwise, I think there will be multiple reallocations with all of
> these changes.

They're mainly in test/init code in which case it doesn't matter too
much.  The only exceptions are for bond learn, lacpdu and tunnel arp/nd
request packet composition which shouldn't happen too often I guess.

In any case, we can also do something similar to what you suggested,
with a small change:

diff --git a/lib/dp-packet.c b/lib/dp-packet.c
index 72f6d09ac7f3..8ae528ba6a6e 100644
--- a/lib/dp-packet.c
+++ b/lib/dp-packet.c
@@ -294,7 +294,7 @@ dp_packet_resize(struct dp_packet *b, size_t new_headroom, size_t new_tailroom)
 void
 dp_packet_prealloc_tailroom(struct dp_packet *b, size_t size)
 {
-    if (size > dp_packet_tailroom(b)) {
+    if ((!dp_packet_base(b) && size) || (size > dp_packet_tailroom(b))) {
         dp_packet_resize(b, dp_packet_headroom(b), MAX(size, 64));
     }
 }

This passes tests.  What do you think?

> 
> Maybe I missed a different ubsan flake - otherwise, we can also assert
> that size must be non-zero when allocating a dp_packet, but that's a
> bigger change.
> 
Thanks,
Dumitru
Aaron Conole Jan. 6, 2022, 1:48 p.m. UTC | #3
Dumitru Ceara <dceara@redhat.com> writes:

> On 12/21/21 16:44, Aaron Conole wrote:
>> Dumitru Ceara <dceara@redhat.com> writes:
>> 
>>> UB Sanitizer report:
>>>   lib/dp-packet.h:297:39: runtime error: applying zero offset to null pointer
>>>       #0 0x7946f5 in dp_packet_tail /root/ovs/./lib/dp-packet.h:297:39
>>>       #1 0x794331 in dp_packet_tailroom /root/ovs/./lib/dp-packet.h:325:49
>>>       #2 0x7942a0 in dp_packet_prealloc_tailroom /root/ovs/lib/dp-packet.c:297:16
>>>       #3 0xc347cf in eth_compose /root/ovs/lib/packets.c:1061:5
>>>       [...]
>>>
>>> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
>>> ---
>>>  lib/netdev-dummy.c           |    6 +++---
>>>  ofproto/bond.c               |    3 ++-
>>>  ofproto/ofproto-dpif-trace.c |    4 ++--
>>>  ofproto/ofproto-dpif-xlate.c |    4 ++--
>>>  ofproto/ofproto-dpif.c       |    6 +++---
>>>  utilities/ovs-ofctl.c        |    6 ++----
>>>  6 files changed, 14 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
>>> index 1f386b81bbaf..2bb6c38bd5ca 100644
>>> --- a/lib/netdev-dummy.c
>>> +++ b/lib/netdev-dummy.c
>>> @@ -188,7 +188,7 @@ dummy_packet_stream_init(struct dummy_packet_stream *s, struct stream *stream)
>>>  {
>>>      int rxbuf_size = stream ? 2048 : 0;
>>>      s->stream = stream;
>>> -    dp_packet_init(&s->rxbuf, rxbuf_size);
>>> +    dp_packet_init(&s->rxbuf, MAX(rxbuf_size, ETH_HEADER_LEN));
>>>      ovs_list_init(&s->txq);
>>>  }
>>>  
>>> @@ -1149,7 +1149,7 @@ netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
>>>              if (flow.dl_type == htons(ETH_TYPE_ARP)
>>>                  && flow.nw_proto == ARP_OP_REQUEST
>>>                  && flow.nw_dst == dev->address.s_addr) {
>>> -                struct dp_packet *reply = dp_packet_new(0);
>>> +                struct dp_packet *reply = dp_packet_new(ETH_HEADER_LEN);
>>>                  compose_arp(reply, ARP_OP_REPLY, dev->hwaddr, flow.dl_src,
>>>                              false, flow.nw_dst, flow.nw_src);
>>>                  netdev_dummy_queue_packet(dev, reply, NULL, 0);
>>> @@ -1647,7 +1647,7 @@ eth_from_flow_str(const char *s, size_t packet_size,
>>>          return NULL;
>>>      }
>>>  
>>> -    packet = dp_packet_new(0);
>>> +    packet = dp_packet_new(MAX(packet_size, ETH_HEADER_LEN));
>>>      if (packet_size) {
>>>          flow_compose(packet, flow, NULL, 0);
>>>          if (dp_packet_size(packet) < packet_size) {
>>> diff --git a/ofproto/bond.c b/ofproto/bond.c
>>> index cdfdf0b9d8c0..8ab27ed09c3a 100644
>>> --- a/ofproto/bond.c
>>> +++ b/ofproto/bond.c
>>> @@ -809,7 +809,8 @@ bond_compose_learning_packet(struct bond *bond, const struct eth_addr eth_src,
>>>      flow.dl_src = eth_src;
>>>      member = choose_output_member(bond, &flow, NULL, vlan);
>>>  
>>> -    packet = dp_packet_new(0);
>>> +    packet = dp_packet_new(2 + ETH_HEADER_LEN + VLAN_HEADER_LEN
>>> +                           + ARP_ETH_HEADER_LEN);
>>>      compose_rarp(packet, eth_src);
>>>      if (vlan) {
>>>          eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(vlan));
>>> diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
>>> index 78a54c715dc7..b4a9974689d1 100644
>>> --- a/ofproto/ofproto-dpif-trace.c
>>> +++ b/ofproto/ofproto-dpif-trace.c
>>> @@ -245,7 +245,7 @@ parse_flow_and_packet(int argc, const char *argv[],
>>>  
>>>              struct dp_packet payload;
>>>              memset(&payload, 0, sizeof payload);
>>> -            dp_packet_init(&payload, 0);
>>> +            dp_packet_init(&payload, ETH_HEADER_LEN);
>>>              if (dp_packet_put_hex(&payload, argv[++i], NULL)[0] != '\0') {
>>>                  dp_packet_uninit(&payload);
>>>                  error = xstrdup("Trailing garbage in packet data");
>>> @@ -436,7 +436,7 @@ parse_flow_and_packet(int argc, const char *argv[],
>>>  
>>>      if (generate_packet) {
>>>          /* Generate a packet, as requested. */
>>> -        packet = dp_packet_new(0);
>>> +        packet = dp_packet_new(ETH_HEADER_LEN);
>>>          flow_compose(packet, flow, l7, l7_len);
>>>      } else if (packet) {
>>>          /* Use the metadata from the flow and the packet argument to
>>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>>> index 991e22e6b64b..c1fd4c585d56 100644
>>> --- a/ofproto/ofproto-dpif-xlate.c
>>> +++ b/ofproto/ofproto-dpif-xlate.c
>>> @@ -3490,7 +3490,7 @@ tnl_send_nd_request(struct xlate_ctx *ctx, const struct xport *out_dev,
>>>  {
>>>      struct dp_packet packet;
>>>  
>>> -    dp_packet_init(&packet, 0);
>>> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>>>      compose_nd_ns(&packet, eth_src, ipv6_src, ipv6_dst);
>>>      compose_table_xlate(ctx, out_dev, &packet);
>>>      dp_packet_uninit(&packet);
>>> @@ -3503,7 +3503,7 @@ tnl_send_arp_request(struct xlate_ctx *ctx, const struct xport *out_dev,
>>>  {
>>>      struct dp_packet packet;
>>>  
>>> -    dp_packet_init(&packet, 0);
>>> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>>>      compose_arp(&packet, ARP_OP_REQUEST,
>>>                  eth_src, eth_addr_zero, true, ip_src, ip_dst);
>>>  
>>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>>> index bc3df8ea1510..e44e365dd405 100644
>>> --- a/ofproto/ofproto-dpif.c
>>> +++ b/ofproto/ofproto-dpif.c
>>> @@ -1268,7 +1268,7 @@ check_ct_eventmask(struct dpif_backer *backer)
>>>      nl_msg_end_nested(&actions, ct_start);
>>>  
>>>      /* Compose a dummy UDP packet. */
>>> -    dp_packet_init(&packet, 0);
>>> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>>>      flow_compose(&packet, &flow, NULL, 64);
>>>  
>>>      /* Execute the actions.  On older datapaths this fails with EINVAL, on
>>> @@ -1361,7 +1361,7 @@ check_ct_timeout_policy(struct dpif_backer *backer)
>>>      nl_msg_end_nested(&actions, ct_start);
>>>  
>>>      /* Compose a dummy UDP packet. */
>>> -    dp_packet_init(&packet, 0);
>>> +    dp_packet_init(&packet, ETH_HEADER_LEN);
>>>      flow_compose(&packet, &flow, NULL, 64);
>>>  
>>>      /* Execute the actions.  On older datapaths this fails with EINVAL, on
>>> @@ -3501,7 +3501,7 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size)
>>>          struct dp_packet packet;
>>>          void *packet_pdu;
>>>  
>>> -        dp_packet_init(&packet, 0);
>>> +        dp_packet_init(&packet, ETH_HEADER_LEN);
>>>          packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
>>>                                   pdu_size);
>>>          memcpy(packet_pdu, pdu, pdu_size);
>>> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
>>> index ede7f1e61ab8..55b5e1a4c95a 100644
>>> --- a/utilities/ovs-ofctl.c
>>> +++ b/utilities/ovs-ofctl.c
>>> @@ -4902,15 +4902,13 @@ ofctl_compose_packet(struct ovs_cmdl_context *ctx)
>>>      }
>>>  
>>>      struct dp_packet p;
>>> -    memset(&p, 0, sizeof p);
>>> -    dp_packet_init(&p, 0);
>>> +    dp_packet_init(&p, ETH_HEADER_LEN);
>>>  
>>>      void *l7 = NULL;
>>>      size_t l7_len = 64;
>>>      if (ctx->argc > 2) {
>>>          struct dp_packet payload;
>>> -        memset(&payload, 0, sizeof payload);
>>> -        dp_packet_init(&payload, 0);
>>> +        dp_packet_init(&payload, ETH_HEADER_LEN);
>>>          if (dp_packet_put_hex(&payload, ctx->argv[2], NULL)[0] != '\0') {
>>>              ovs_fatal(0, "%s: trailing garbage in packet data", ctx->argv[2]);
>>>          }
>> 
>> If the issue is just in dp_packet_prealloc_tailroom() can we just do a
>> size check there?
>> 
>> -    if (size > dp_packet_tailroom(b)) {
>> +    /* check against pkt_size to start, because it is more likely to fail */
>> +    if (dp_packet_size(b) < size || size > dp_packet_tailroom(b)) {
>
> This creates issues.  I think due to the fact that the packet tailroom
> might shrink in some cases now.
>
>> 
>> Otherwise, I think there will be multiple reallocations with all of
>> these changes.
>
> They're mainly in test/init code in which case it doesn't matter too
> much.  The only exceptions are for bond learn, lacpdu and tunnel arp/nd
> request packet composition which shouldn't happen too often I guess.
>
> In any case, we can also do something similar to what you suggested,
> with a small change:
>
> diff --git a/lib/dp-packet.c b/lib/dp-packet.c
> index 72f6d09ac7f3..8ae528ba6a6e 100644
> --- a/lib/dp-packet.c
> +++ b/lib/dp-packet.c
> @@ -294,7 +294,7 @@ dp_packet_resize(struct dp_packet *b, size_t new_headroom, size_t new_tailroom)
>  void
>  dp_packet_prealloc_tailroom(struct dp_packet *b, size_t size)
>  {
> -    if (size > dp_packet_tailroom(b)) {
> +    if ((!dp_packet_base(b) && size) || (size > dp_packet_tailroom(b))) {
>          dp_packet_resize(b, dp_packet_headroom(b), MAX(size, 64));
>      }
>  }
>
> This passes tests.  What do you think?

Looks good to me :)

Thanks for following up.

>> 
>> Maybe I missed a different ubsan flake - otherwise, we can also assert
>> that size must be non-zero when allocating a dp_packet, but that's a
>> bigger change.
>> 
> Thanks,
> Dumitru
diff mbox series

Patch

diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index 1f386b81bbaf..2bb6c38bd5ca 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -188,7 +188,7 @@  dummy_packet_stream_init(struct dummy_packet_stream *s, struct stream *stream)
 {
     int rxbuf_size = stream ? 2048 : 0;
     s->stream = stream;
-    dp_packet_init(&s->rxbuf, rxbuf_size);
+    dp_packet_init(&s->rxbuf, MAX(rxbuf_size, ETH_HEADER_LEN));
     ovs_list_init(&s->txq);
 }
 
@@ -1149,7 +1149,7 @@  netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
             if (flow.dl_type == htons(ETH_TYPE_ARP)
                 && flow.nw_proto == ARP_OP_REQUEST
                 && flow.nw_dst == dev->address.s_addr) {
-                struct dp_packet *reply = dp_packet_new(0);
+                struct dp_packet *reply = dp_packet_new(ETH_HEADER_LEN);
                 compose_arp(reply, ARP_OP_REPLY, dev->hwaddr, flow.dl_src,
                             false, flow.nw_dst, flow.nw_src);
                 netdev_dummy_queue_packet(dev, reply, NULL, 0);
@@ -1647,7 +1647,7 @@  eth_from_flow_str(const char *s, size_t packet_size,
         return NULL;
     }
 
-    packet = dp_packet_new(0);
+    packet = dp_packet_new(MAX(packet_size, ETH_HEADER_LEN));
     if (packet_size) {
         flow_compose(packet, flow, NULL, 0);
         if (dp_packet_size(packet) < packet_size) {
diff --git a/ofproto/bond.c b/ofproto/bond.c
index cdfdf0b9d8c0..8ab27ed09c3a 100644
--- a/ofproto/bond.c
+++ b/ofproto/bond.c
@@ -809,7 +809,8 @@  bond_compose_learning_packet(struct bond *bond, const struct eth_addr eth_src,
     flow.dl_src = eth_src;
     member = choose_output_member(bond, &flow, NULL, vlan);
 
-    packet = dp_packet_new(0);
+    packet = dp_packet_new(2 + ETH_HEADER_LEN + VLAN_HEADER_LEN
+                           + ARP_ETH_HEADER_LEN);
     compose_rarp(packet, eth_src);
     if (vlan) {
         eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(vlan));
diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
index 78a54c715dc7..b4a9974689d1 100644
--- a/ofproto/ofproto-dpif-trace.c
+++ b/ofproto/ofproto-dpif-trace.c
@@ -245,7 +245,7 @@  parse_flow_and_packet(int argc, const char *argv[],
 
             struct dp_packet payload;
             memset(&payload, 0, sizeof payload);
-            dp_packet_init(&payload, 0);
+            dp_packet_init(&payload, ETH_HEADER_LEN);
             if (dp_packet_put_hex(&payload, argv[++i], NULL)[0] != '\0') {
                 dp_packet_uninit(&payload);
                 error = xstrdup("Trailing garbage in packet data");
@@ -436,7 +436,7 @@  parse_flow_and_packet(int argc, const char *argv[],
 
     if (generate_packet) {
         /* Generate a packet, as requested. */
-        packet = dp_packet_new(0);
+        packet = dp_packet_new(ETH_HEADER_LEN);
         flow_compose(packet, flow, l7, l7_len);
     } else if (packet) {
         /* Use the metadata from the flow and the packet argument to
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 991e22e6b64b..c1fd4c585d56 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -3490,7 +3490,7 @@  tnl_send_nd_request(struct xlate_ctx *ctx, const struct xport *out_dev,
 {
     struct dp_packet packet;
 
-    dp_packet_init(&packet, 0);
+    dp_packet_init(&packet, ETH_HEADER_LEN);
     compose_nd_ns(&packet, eth_src, ipv6_src, ipv6_dst);
     compose_table_xlate(ctx, out_dev, &packet);
     dp_packet_uninit(&packet);
@@ -3503,7 +3503,7 @@  tnl_send_arp_request(struct xlate_ctx *ctx, const struct xport *out_dev,
 {
     struct dp_packet packet;
 
-    dp_packet_init(&packet, 0);
+    dp_packet_init(&packet, ETH_HEADER_LEN);
     compose_arp(&packet, ARP_OP_REQUEST,
                 eth_src, eth_addr_zero, true, ip_src, ip_dst);
 
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index bc3df8ea1510..e44e365dd405 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1268,7 +1268,7 @@  check_ct_eventmask(struct dpif_backer *backer)
     nl_msg_end_nested(&actions, ct_start);
 
     /* Compose a dummy UDP packet. */
-    dp_packet_init(&packet, 0);
+    dp_packet_init(&packet, ETH_HEADER_LEN);
     flow_compose(&packet, &flow, NULL, 64);
 
     /* Execute the actions.  On older datapaths this fails with EINVAL, on
@@ -1361,7 +1361,7 @@  check_ct_timeout_policy(struct dpif_backer *backer)
     nl_msg_end_nested(&actions, ct_start);
 
     /* Compose a dummy UDP packet. */
-    dp_packet_init(&packet, 0);
+    dp_packet_init(&packet, ETH_HEADER_LEN);
     flow_compose(&packet, &flow, NULL, 64);
 
     /* Execute the actions.  On older datapaths this fails with EINVAL, on
@@ -3501,7 +3501,7 @@  send_pdu_cb(void *port_, const void *pdu, size_t pdu_size)
         struct dp_packet packet;
         void *packet_pdu;
 
-        dp_packet_init(&packet, 0);
+        dp_packet_init(&packet, ETH_HEADER_LEN);
         packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
                                  pdu_size);
         memcpy(packet_pdu, pdu, pdu_size);
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index ede7f1e61ab8..55b5e1a4c95a 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -4902,15 +4902,13 @@  ofctl_compose_packet(struct ovs_cmdl_context *ctx)
     }
 
     struct dp_packet p;
-    memset(&p, 0, sizeof p);
-    dp_packet_init(&p, 0);
+    dp_packet_init(&p, ETH_HEADER_LEN);
 
     void *l7 = NULL;
     size_t l7_len = 64;
     if (ctx->argc > 2) {
         struct dp_packet payload;
-        memset(&payload, 0, sizeof payload);
-        dp_packet_init(&payload, 0);
+        dp_packet_init(&payload, ETH_HEADER_LEN);
         if (dp_packet_put_hex(&payload, ctx->argv[2], NULL)[0] != '\0') {
             ovs_fatal(0, "%s: trailing garbage in packet data", ctx->argv[2]);
         }