]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
DHCP: Ensure we have enough data to checksum IP and UDP
authorMaciej S. Szmigiero <mail@maciej.szmigiero.name>
Fri, 13 Sep 2019 09:53:25 +0000 (10:53 +0100)
committerRoy Marples <roy@marples.name>
Fri, 13 Sep 2019 09:53:25 +0000 (10:53 +0100)
While here, only skip UDP checksum after we have verified we
have enough data to be doubly sure.

Fixes a regression introduced in dhcpcd-7.2.0 when we started
to read from the UDP socket rather than just the BPF socket
where these checks are already made.

src/dhcp.c

index ac52382424266a8a40afc062f6d8a088ee26e3f8..3d8e46bdebc95710a7c77109c79dac80f00f0243 100644 (file)
@@ -3250,7 +3250,7 @@ valid_udp_packet(void *packet, size_t plen, struct in_addr *from,
                .ip_dst = ip->ip_dst
        };
        size_t ip_hlen;
-       uint16_t ip_len, uh_sum;
+       uint16_t ip_len, udp_len, uh_sum;
        struct udphdr *udp;
        uint32_t csum;
 
@@ -3276,27 +3276,31 @@ valid_udp_packet(void *packet, size_t plen, struct in_addr *from,
                errno = ERANGE;
                return -1;
        }
-       /* Check we don't go beyond the payload */
+       /* Check IP doesn't go beyond the payload */
        if (ip_len > plen) {
                errno = ENOBUFS;
                return -1;
        }
 
-       if (flags & BPF_PARTIALCSUM)
+       /* Check UDP doesn't go beyond the payload */
+       udp = (struct udphdr *)(void *)((char *)ip + ip_hlen);
+       udp_len = ntohs(udp->uh_ulen);
+       if (udp_len > plen - ip_hlen) {
+               errno =  ENOBUFS;
+               return -1;
+       }
+
+       if (udp->uh_sum == 0 || flags & BPF_PARTIALCSUM)
                return 0;
 
        /* UDP checksum is based on a pseudo IP header alongside
         * the UDP header and payload. */
-       udp = (struct udphdr *)(void *)((char *)ip + ip_hlen);
-       if (udp->uh_sum == 0)
-               return 0;
-
        uh_sum = udp->uh_sum;
        udp->uh_sum = 0;
        pseudo_ip.ip_len = udp->uh_ulen;
        csum = 0;
        in_cksum(&pseudo_ip, sizeof(pseudo_ip), &csum);
-       csum = in_cksum(udp, ntohs(udp->uh_ulen), &csum);
+       csum = in_cksum(udp, udp_len, &csum);
        if (csum != uh_sum) {
                errno = EINVAL;
                return -1;