From 2840b3825eb6e0b7320fb111d06832ca9a9e3148 Mon Sep 17 00:00:00 2001
From: Martine Lenders <m.lenders@fu-berlin.de>
Date: Wed, 9 Jan 2019 19:32:46 +0100
Subject: [PATCH] sock_dns: fix out-of-bound errors

Fixes #10739
---
 Makefile.dep                        |  1 +
 sys/net/application_layer/dns/dns.c | 60 +++++++++++++++++++++++------
 2 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/Makefile.dep b/Makefile.dep
index 3cecda907e..e324203ade 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -689,6 +689,7 @@ endif
 
 ifneq (,$(filter sock_dns,$(USEMODULE)))
   USEMODULE += sock_util
+  USEMODULE += posix
 endif
 
 ifneq (,$(filter sock_util,$(USEMODULE)))
diff --git a/sys/net/application_layer/dns/dns.c b/sys/net/application_layer/dns/dns.c
index adef0df244..8bda172367 100644
--- a/sys/net/application_layer/dns/dns.c
+++ b/sys/net/application_layer/dns/dns.c
@@ -15,6 +15,7 @@
  * @}
  */
 
+#include <arpa/inet.h>
 #include <string.h>
 #include <stdio.h>
 
@@ -73,34 +74,58 @@ static unsigned _get_short(uint8_t *buf)
     return _tmp;
 }
 
-static size_t _skip_hostname(uint8_t *buf)
+static ssize_t _skip_hostname(const uint8_t *buf, size_t len, uint8_t *bufpos)
 {
-    uint8_t *bufpos = buf;
+    const uint8_t *buflim = buf + len;
+    unsigned res = 0;
 
+    if (bufpos >= buflim) {
+        /* out-of-bound */
+        return -EBADMSG;
+    }
     /* handle DNS Message Compression */
     if (*bufpos >= 192) {
+        if ((bufpos + 2) >= buflim) {
+            return -EBADMSG;
+        }
         return 2;
     }
 
-    while(*bufpos) {
-        bufpos += *bufpos + 1;
+    while (bufpos[res]) {
+        res += bufpos[res] + 1;
+        if ((&bufpos[res]) >= buflim) {
+            /* out-of-bound */
+            return -EBADMSG;
+        }
     }
-    return (bufpos - buf + 1);
+    return res + 1;
 }
 
 static int _parse_dns_reply(uint8_t *buf, size_t len, void* addr_out, int family)
 {
+    const uint8_t *buflim = buf + len;
     sock_dns_hdr_t *hdr = (sock_dns_hdr_t*) buf;
     uint8_t *bufpos = buf + sizeof(*hdr);
 
     /* skip all queries that are part of the reply */
     for (unsigned n = 0; n < ntohs(hdr->qdcount); n++) {
-        bufpos += _skip_hostname(bufpos);
+        ssize_t tmp = _skip_hostname(buf, len, bufpos);
+        if (tmp < 0) {
+            return tmp;
+        }
+        bufpos += tmp;
         bufpos += 4;    /* skip type and class of query */
     }
 
     for (unsigned n = 0; n < ntohs(hdr->ancount); n++) {
-        bufpos += _skip_hostname(bufpos);
+        ssize_t tmp = _skip_hostname(buf, len, bufpos);
+        if (tmp < 0) {
+            return tmp;
+        }
+        bufpos += tmp;
+        if ((bufpos + 2 + 2 + 4) >= buflim) {
+            return -EBADMSG;
+        }
         uint16_t _type = ntohs(_get_short(bufpos));
         bufpos += 2;
         uint16_t class = ntohs(_get_short(bufpos));
@@ -108,20 +133,31 @@ static int _parse_dns_reply(uint8_t *buf, size_t len, void* addr_out, int family
         bufpos += 4; /* skip ttl */
 
         unsigned addrlen = ntohs(_get_short(bufpos));
-        bufpos += 2;
-        if ((bufpos + addrlen) > (buf + len)) {
-            return -EBADMSG;
-        }
-
         /* skip unwanted answers */
         if ((class != DNS_CLASS_IN) ||
                 ((_type == DNS_TYPE_A) && (family == AF_INET6)) ||
                 ((_type == DNS_TYPE_AAAA) && (family == AF_INET)) ||
                 ! ((_type == DNS_TYPE_A) || ((_type == DNS_TYPE_AAAA))
                     )) {
+            if (addrlen > len) {
+                /* buffer wraps around memory space */
+                return -EBADMSG;
+            }
             bufpos += addrlen;
+            /* other out-of-bound is checked in `_skip_hostname()` at start of
+             * loop */
             continue;
         }
+        if (((addrlen != INADDRSZ) && (family == AF_INET)) ||
+            ((addrlen != IN6ADDRSZ) && (family == AF_INET6)) ||
+            ((addrlen != IN6ADDRSZ) && (addrlen != INADDRSZ) &&
+             (family == AF_UNSPEC))) {
+            return -EBADMSG;
+        }
+        bufpos += 2;
+        if ((bufpos + addrlen) >= buflim) {
+            return -EBADMSG;
+        }
 
         memcpy(addr_out, bufpos, addrlen);
         return addrlen;
-- 
GitLab