diff mbox

[ovs-dev] byte-order: Fix undefined behavior of BYTES_TO_BE32.

Message ID 20170613045114.1511-1-blp@ovn.org
State Accepted
Headers show

Commit Message

Ben Pfaff June 13, 2017, 4:51 a.m. UTC
A left shift that would produce a result that is not representable
by the type of the expression's result has "undefined behavior"
according to the C language standard. Avoid this by casting values
that could set the upper bit to unsigned types.

Also document and convert a macro to a function.

While we're at it, delete the unused macro BE16S_TO_BE32.

Found via gcc's undefined behavior sanitizer.

Reported-by: Lance Richardson <lrichard@redhat.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
---
 lib/byte-order.h | 21 +++++++++++++--------
 lib/flow.c       |  2 +-
 2 files changed, 14 insertions(+), 9 deletions(-)

Comments

Lance Richardson June 13, 2017, 1:09 p.m. UTC | #1
> From: "Ben Pfaff" <blp@ovn.org>
> To: dev@openvswitch.org
> Cc: "Ben Pfaff" <blp@ovn.org>, "Lance Richardson" <lrichard@redhat.com>
> Sent: Tuesday, 13 June, 2017 12:51:14 AM
> Subject: [PATCH] byte-order: Fix undefined behavior of BYTES_TO_BE32.
> 
> A left shift that would produce a result that is not representable
> by the type of the expression's result has "undefined behavior"
> according to the C language standard. Avoid this by casting values
> that could set the upper bit to unsigned types.
> 
> Also document and convert a macro to a function.
> 
> While we're at it, delete the unused macro BE16S_TO_BE32.
> 
> Found via gcc's undefined behavior sanitizer.
> 
> Reported-by: Lance Richardson <lrichard@redhat.com>
> Signed-off-by: Ben Pfaff <blp@ovn.org>
> ---
>  lib/byte-order.h | 21 +++++++++++++--------
>  lib/flow.c       |  2 +-
>  2 files changed, 14 insertions(+), 9 deletions(-)
> 

Looks good.

Acked-by: Lance Richardson <lrichard@redhat.com>
Ben Pfaff June 13, 2017, 3:17 p.m. UTC | #2
On Tue, Jun 13, 2017 at 09:09:44AM -0400, Lance Richardson wrote:
> > From: "Ben Pfaff" <blp@ovn.org>
> > To: dev@openvswitch.org
> > Cc: "Ben Pfaff" <blp@ovn.org>, "Lance Richardson" <lrichard@redhat.com>
> > Sent: Tuesday, 13 June, 2017 12:51:14 AM
> > Subject: [PATCH] byte-order: Fix undefined behavior of BYTES_TO_BE32.
> > 
> > A left shift that would produce a result that is not representable
> > by the type of the expression's result has "undefined behavior"
> > according to the C language standard. Avoid this by casting values
> > that could set the upper bit to unsigned types.
> > 
> > Also document and convert a macro to a function.
> > 
> > While we're at it, delete the unused macro BE16S_TO_BE32.
> > 
> > Found via gcc's undefined behavior sanitizer.
> > 
> > Reported-by: Lance Richardson <lrichard@redhat.com>
> > Signed-off-by: Ben Pfaff <blp@ovn.org>
> > ---
> >  lib/byte-order.h | 21 +++++++++++++--------
> >  lib/flow.c       |  2 +-
> >  2 files changed, 14 insertions(+), 9 deletions(-)
> > 
> 
> Looks good.
> 
> Acked-by: Lance Richardson <lrichard@redhat.com>

Thanks.  I applied this to master.

Do you think that it is worthwhile to apply this to older branches?
Lance Richardson June 13, 2017, 3:34 p.m. UTC | #3
> From: "Ben Pfaff" <blp@ovn.org>
> To: "Lance Richardson" <lrichard@redhat.com>
> Cc: dev@openvswitch.org
> Sent: Tuesday, 13 June, 2017 11:17:26 AM
> Subject: Re: [PATCH] byte-order: Fix undefined behavior of BYTES_TO_BE32.
> 
> On Tue, Jun 13, 2017 at 09:09:44AM -0400, Lance Richardson wrote:
> > > From: "Ben Pfaff" <blp@ovn.org>
> > > To: dev@openvswitch.org
> > > Cc: "Ben Pfaff" <blp@ovn.org>, "Lance Richardson" <lrichard@redhat.com>
> > > Sent: Tuesday, 13 June, 2017 12:51:14 AM
> > > Subject: [PATCH] byte-order: Fix undefined behavior of BYTES_TO_BE32.
> > > 
> > > A left shift that would produce a result that is not representable
> > > by the type of the expression's result has "undefined behavior"
> > > according to the C language standard. Avoid this by casting values
> > > that could set the upper bit to unsigned types.
> > > 
> > > Also document and convert a macro to a function.
> > > 
> > > While we're at it, delete the unused macro BE16S_TO_BE32.
> > > 
> > > Found via gcc's undefined behavior sanitizer.
> > > 
> > > Reported-by: Lance Richardson <lrichard@redhat.com>
> > > Signed-off-by: Ben Pfaff <blp@ovn.org>
> > > ---
> > >  lib/byte-order.h | 21 +++++++++++++--------
> > >  lib/flow.c       |  2 +-
> > >  2 files changed, 14 insertions(+), 9 deletions(-)
> > > 
> > 
> > Looks good.
> > 
> > Acked-by: Lance Richardson <lrichard@redhat.com>
> 
> Thanks.  I applied this to master.
> 
> Do you think that it is worthwhile to apply this to older branches?
> 

I would guess not... the only danger here would be if the compiler
incorrectly optimized something based on inferring that the high-order
bit of the value being shifted 24 bits has to be zero because there
would be "undefined behavior" if it were set. From looking through
the code, there doesn't seem to be any real exposure.

Thanks,

   Lance
Ben Pfaff June 13, 2017, 4:49 p.m. UTC | #4
On Tue, Jun 13, 2017 at 11:34:25AM -0400, Lance Richardson wrote:
> > From: "Ben Pfaff" <blp@ovn.org>
> > To: "Lance Richardson" <lrichard@redhat.com>
> > Cc: dev@openvswitch.org
> > Sent: Tuesday, 13 June, 2017 11:17:26 AM
> > Subject: Re: [PATCH] byte-order: Fix undefined behavior of BYTES_TO_BE32.
> > 
> > On Tue, Jun 13, 2017 at 09:09:44AM -0400, Lance Richardson wrote:
> > > > From: "Ben Pfaff" <blp@ovn.org>
> > > > To: dev@openvswitch.org
> > > > Cc: "Ben Pfaff" <blp@ovn.org>, "Lance Richardson" <lrichard@redhat.com>
> > > > Sent: Tuesday, 13 June, 2017 12:51:14 AM
> > > > Subject: [PATCH] byte-order: Fix undefined behavior of BYTES_TO_BE32.
> > > > 
> > > > A left shift that would produce a result that is not representable
> > > > by the type of the expression's result has "undefined behavior"
> > > > according to the C language standard. Avoid this by casting values
> > > > that could set the upper bit to unsigned types.
> > > > 
> > > > Also document and convert a macro to a function.
> > > > 
> > > > While we're at it, delete the unused macro BE16S_TO_BE32.
> > > > 
> > > > Found via gcc's undefined behavior sanitizer.
> > > > 
> > > > Reported-by: Lance Richardson <lrichard@redhat.com>
> > > > Signed-off-by: Ben Pfaff <blp@ovn.org>
> > > > ---
> > > >  lib/byte-order.h | 21 +++++++++++++--------
> > > >  lib/flow.c       |  2 +-
> > > >  2 files changed, 14 insertions(+), 9 deletions(-)
> > > > 
> > > 
> > > Looks good.
> > > 
> > > Acked-by: Lance Richardson <lrichard@redhat.com>
> > 
> > Thanks.  I applied this to master.
> > 
> > Do you think that it is worthwhile to apply this to older branches?
> > 
> 
> I would guess not... the only danger here would be if the compiler
> incorrectly optimized something based on inferring that the high-order
> bit of the value being shifted 24 bits has to be zero because there
> would be "undefined behavior" if it were set. From looking through
> the code, there doesn't seem to be any real exposure.

OK, thanks.
diff mbox

Patch

diff --git a/lib/byte-order.h b/lib/byte-order.h
index e864658f9b59..b2a9082bbada 100644
--- a/lib/byte-order.h
+++ b/lib/byte-order.h
@@ -98,17 +98,22 @@  uint32_byteswap(uint32_t crc) {
          ((((ovs_be64) (VALUE)) & UINT64_C(0xff00000000000000)) >> 56))
 #endif
 
+/* Returns the ovs_be32 that you would get from:
+ *
+ *    union { uint8_t b[4]; ovs_be32 be32; } x = { .b = { b0, b1, b2, b3 } };
+ *    return x.be32;
+ *
+ * but without the undefined behavior. */
+static inline ovs_be32
+bytes_to_be32(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3)
+{
 #if WORDS_BIGENDIAN
-#define BYTES_TO_BE32(B1, B2, B3, B4) \
-    (OVS_FORCE ovs_be32)((uint32_t)(B1) << 24 | (B2) << 16 | (B3) << 8 | (B4))
-#define BE16S_TO_BE32(B1, B2) \
-    (OVS_FORCE ovs_be32)((uint32_t)(B1) << 16 | (B2))
+    uint32_t x = ((uint32_t) b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
 #else
-#define BYTES_TO_BE32(B1, B2, B3, B4) \
-    (OVS_FORCE ovs_be32)((uint32_t)(B1) | (B2) << 8 | (B3) << 16 | (B4) << 24)
-#define BE16S_TO_BE32(B1, B2) \
-    (OVS_FORCE ovs_be32)((uint32_t)(B1) | (B2) << 16)
+    uint32_t x = ((uint32_t) b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
 #endif
+    return (OVS_FORCE ovs_be32) x;
+}
 
 /* These functions zero-extend big-endian values to longer ones,
  * or truncate long big-endian value to shorter ones. */
diff --git a/lib/flow.c b/lib/flow.c
index 1f51b66e7b44..d73e796a2f45 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -823,7 +823,7 @@  miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
 
     packet->l4_ofs = (char *)data - frame;
     miniflow_push_be32(mf, nw_frag,
-                       BYTES_TO_BE32(nw_frag, nw_tos, nw_ttl, nw_proto));
+                       bytes_to_be32(nw_frag, nw_tos, nw_ttl, nw_proto));
 
     if (OVS_LIKELY(!(nw_frag & FLOW_NW_FRAG_LATER))) {
         if (OVS_LIKELY(nw_proto == IPPROTO_TCP)) {