]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: dns: fix alignment issues in the DNS response parser
authorWilly Tarreau <w@1wt.eu>
Wed, 13 Jul 2016 09:59:39 +0000 (11:59 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 13 Jul 2016 10:13:24 +0000 (12:13 +0200)
Alexander Lebedev reported that the DNS parser crashes in 1.6 with a bus
error on Sparc when it receives a response. This is obviously caused by
some alignment issues. The issue can also be reproduced on ARMv5 when
setting /proc/cpu/alignment to 4 (which helps debugging).

Two places cause this crash in turn, the first one is when the IP address
from the packet is compared to the current one, and the second place is
when the address is assigned because an unaligned address is passed to
update_server_addr().

This patch modifies these places to properly use memcpy() and memcmp()
to manipulate the unaligned data.

Nenad Merdanovic found another set of places specific to 1.7 in functions
in_net_ipv4() and in_net_ipv6(), which are used to compare networks. 1.6
has the functions but does not use them. There we perform a temporary copy
to a local variable to fix the problem. The type of the function's argument
is wrong since it's not necessarily aligned, so we change it for a const
void * instead.

This fix must be backported to 1.6. Note that in 1.6 the code is slightly
different, there's no rec[] array, the pointer is used directly from the
buffer.

include/common/standard.h
src/dns.c
src/server.c
src/standard.c

index bf14369e865f2b16312ae93d795a10b9a6eb2fe2..962a759ca1b4971e9e22def421f0d136bd49f674 100644 (file)
@@ -843,10 +843,10 @@ void len2mask4(int len, struct in_addr *addr);
 void len2mask6(int len, struct in6_addr *addr);
 
 /* Return true if IPv4 address is part of the network */
-extern int in_net_ipv4(struct in_addr *addr, struct in_addr *mask, struct in_addr *net);
+extern int in_net_ipv4(const void *addr, const struct in_addr *mask, const struct in_addr *net);
 
 /* Return true if IPv6 address is part of the network */
-extern int in_net_ipv6(struct in6_addr *addr, struct in6_addr *mask, struct in6_addr *net);
+extern int in_net_ipv6(const void *addr, const struct in6_addr *mask, const struct in6_addr *net);
 
 /* Map IPv4 adress on IPv6 address, as specified in RFC 3513. */
 extern void v4tov6(struct in6_addr *sin6_addr, struct in_addr *sin_addr);
index 3b3dfc59e065da04d6eb2eddd6ac6db5c6180a5c..bf5baa0c721f480f83aed34307d57b6900bce223 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -757,11 +757,11 @@ int dns_get_ip_from_response(unsigned char *resp, unsigned char *resp_end,
                                continue;
 
                        if ((rec[i].type == AF_INET &&
-                            in_net_ipv4((struct in_addr *)rec[i].ip,
+                            in_net_ipv4(rec[i].ip,
                                         &resol->opts->pref_net[j].mask.in4,
                                         &resol->opts->pref_net[j].addr.in4)) ||
                            (rec[i].type == AF_INET6 &&
-                            in_net_ipv6((struct in6_addr *)rec[i].ip,
+                            in_net_ipv6(rec[i].ip,
                                         &resol->opts->pref_net[j].mask.in6,
                                         &resol->opts->pref_net[j].addr.in6))) {
                                score += 2;
@@ -772,7 +772,7 @@ int dns_get_ip_from_response(unsigned char *resp, unsigned char *resp_end,
                /* Check for current ip matching. */
                if (rec[i].type == currentip_sin_family &&
                    ((currentip_sin_family == AF_INET &&
-                     *(uint32_t *)rec[i].ip == *(uint32_t *)currentip) ||
+                     memcmp(rec[i].ip, currentip, 4) == 0) ||
                     (currentip_sin_family == AF_INET6 &&
                      memcmp(rec[i].ip, currentip, 16) == 0))) {
                        score += 1;
index 10957548e84b0120dac88fd0e5bcaaf0109e8cdf..39fc4db61bca260fe57f31caccd61e393dcf1106 100644 (file)
@@ -2631,7 +2631,7 @@ int update_server_addr(struct server *s, void *ip, int ip_sin_family, const char
        /* save the new IP address */
        switch (ip_sin_family) {
        case AF_INET:
-               ((struct sockaddr_in *)&s->addr)->sin_addr.s_addr = *(uint32_t *)ip;
+               memcpy(&((struct sockaddr_in *)&s->addr)->sin_addr.s_addr, ip, 4);
                break;
        case AF_INET6:
                memcpy(((struct sockaddr_in6 *)&s->addr)->sin6_addr.s6_addr, ip, 16);
index c9f68b5448b84ab85ff363231646b0d22e556533..4d0e3ed14635125ea54bb8aa87fbd195dd2752e8 100644 (file)
@@ -2426,22 +2426,29 @@ unsigned int full_hash(unsigned int a)
 }
 
 /* Return non-zero if IPv4 address is part of the network,
- * otherwise zero.
+ * otherwise zero. Note that <addr> may not necessarily be aligned
+ * while the two other ones must.
  */
-int in_net_ipv4(struct in_addr *addr, struct in_addr *mask, struct in_addr *net)
+int in_net_ipv4(const void *addr, const struct in_addr *mask, const struct in_addr *net)
 {
-       return((addr->s_addr & mask->s_addr) == (net->s_addr & mask->s_addr));
+       struct in_addr addr_copy;
+
+       memcpy(&addr_copy, addr, sizeof(addr_copy));
+       return((addr_copy.s_addr & mask->s_addr) == (net->s_addr & mask->s_addr));
 }
 
 /* Return non-zero if IPv6 address is part of the network,
- * otherwise zero.
+ * otherwise zero. Note that <addr> may not necessarily be aligned
+ * while the two other ones must.
  */
-int in_net_ipv6(struct in6_addr *addr, struct in6_addr *mask, struct in6_addr *net)
+int in_net_ipv6(const void *addr, const struct in6_addr *mask, const struct in6_addr *net)
 {
        int i;
+       struct in6_addr addr_copy;
 
+       memcpy(&addr_copy, addr, sizeof(addr_copy));
        for (i = 0; i < sizeof(struct in6_addr) / sizeof(int); i++)
-               if (((((int *)addr)[i] & ((int *)mask)[i])) !=
+               if (((((int *)&addr_copy)[i] & ((int *)mask)[i])) !=
                    (((int *)net)[i] & ((int *)mask)[i]))
                        return 0;
        return 1;