[Openvpn-devel,v3] forward: Fix potential unaligned access in drop_if_recursive_routing

Message ID 20241211171349.8892-1-gert@greenie.muc.de
State Accepted
Headers show
Series [Openvpn-devel,v3] forward: Fix potential unaligned access in drop_if_recursive_routing | expand

Commit Message

Gert Doering Dec. 11, 2024, 5:13 p.m. UTC
From: Frank Lichtenheld <frank@lichtenheld.com>

ASAN error:
forward.c:1433:13: runtime error: member access within
misaligned address 0x51e00002f52e for type
'const struct in6_addr', which requires 4 byte alignment

v2: Use memcmp instead of memcpy

Change-Id: I74a9eec4954f3f9d208792b6b34357571f76ae4c
Signed-off-by: Frank Lichtenheld <frank@lichtenheld.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
---

This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master.

Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/827
This mail reflects revision 3 of this Change.

Acked-by according to Gerrit (reflected above):
Gert Doering <gert@greenie.muc.de>

Comments

Gert Doering Dec. 11, 2024, 6:43 p.m. UTC | #1
Thanks for that.  We had quite a discussion on IRC on why this is needed
and whether it's a bug in clang - turns out it is not, the linux 
implementation of IN6_ARE_ADDR_EQUAL() uses 4x 32bit compare, which is
sensitive to alignment, while all the rest of the world uses memcmp()
which does not care...  so this code now does the same on Linux,
and does not crash on "unaligned ipv6 header in an outgoing TAP packet"
(the buf is aligned, but the ethernet header misaligns everything).

Your patch has been applied to the master and release/2.6 branch
(minor bug).

Mildly tested on a FreeBSD and a Linux t_client setup (and, of course,
the buildbot army which uncovered this in the "tap" client tests).

Uncrustify complains about multline comment not having a "*", so
fixed on the fly.

commit 387c2076af14a0f1ba97b6ca0175d81d1e8391a5 (master)
commit 62d41dec8db8c87f9b005c168827bb3387ea5c65 (release/2.6)
Author: Frank Lichtenheld
Date:   Wed Dec 11 18:13:48 2024 +0100

     forward: Fix potential unaligned access in drop_if_recursive_routing

     Signed-off-by: Frank Lichtenheld <frank@lichtenheld.com>
     Acked-by: Gert Doering <gert@greenie.muc.de>
     Message-Id: <20241211171349.8892-1-gert@greenie.muc.de>
     URL: https://www.mail-archive.com/search?l=mid&q=20241211171349.8892-1-gert@greenie.muc.de
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering

Patch

diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index d50b24c..2c72001 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -1390,8 +1390,6 @@ 
 
     if (proto_ver == 4)
     {
-        const struct openvpn_iphdr *pip;
-
         /* make sure we got whole IP header */
         if (BLEN(buf) < ((int) sizeof(struct openvpn_iphdr) + ip_hdr_offset))
         {
@@ -1404,18 +1402,16 @@ 
             return;
         }
 
-        pip = (struct openvpn_iphdr *) (BPTR(buf) + ip_hdr_offset);
+        struct openvpn_iphdr *pip = (struct openvpn_iphdr *) (BPTR(buf) + ip_hdr_offset);
 
         /* drop packets with same dest addr as gateway */
-        if (tun_sa.addr.in4.sin_addr.s_addr == pip->daddr)
+        if (memcmp(&tun_sa.addr.in4.sin_addr.s_addr, &pip->daddr, sizeof(pip->daddr)) == 0)
         {
             drop = true;
         }
     }
     else if (proto_ver == 6)
     {
-        const struct openvpn_ipv6hdr *pip6;
-
         /* make sure we got whole IPv6 header */
         if (BLEN(buf) < ((int) sizeof(struct openvpn_ipv6hdr) + ip_hdr_offset))
         {
@@ -1428,9 +1424,10 @@ 
             return;
         }
 
+        struct openvpn_ipv6hdr *pip6 = (struct openvpn_ipv6hdr *) (BPTR(buf) + ip_hdr_offset);
+
         /* drop packets with same dest addr as gateway */
-        pip6 = (struct openvpn_ipv6hdr *) (BPTR(buf) + ip_hdr_offset);
-        if (IN6_ARE_ADDR_EQUAL(&tun_sa.addr.in6.sin6_addr, &pip6->daddr))
+        if (OPENVPN_IN6_ARE_ADDR_EQUAL(&tun_sa.addr.in6.sin6_addr, &pip6->daddr))
         {
             drop = true;
         }
diff --git a/src/openvpn/proto.h b/src/openvpn/proto.h
index 7b94fbc..ac70134 100644
--- a/src/openvpn/proto.h
+++ b/src/openvpn/proto.h
@@ -83,6 +83,12 @@ 
 #define SIZE_ETH_TO_8021Q_HDR (sizeof(struct openvpn_8021qhdr) \
                                - sizeof(struct openvpn_ethhdr))
 
+/** Version of IN6_ARE_ADDR_EQUAL that is guaranteed to work for
+    unaligned access. E.g. Linux uses 32bit compares which are
+    not safe if the struct is unaligned. */
+#define OPENVPN_IN6_ARE_ADDR_EQUAL(a, b) \
+    (memcmp(a, b, sizeof(struct in6_addr)) == 0)
+
 struct openvpn_iphdr {
 #define OPENVPN_IPH_GET_VER(v) (((v) >> 4) & 0x0F)
 #define OPENVPN_IPH_GET_LEN(v) (((v) & 0x0F) << 2)