From patchwork Fri Apr 20 13:57:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jann Horn X-Patchwork-Id: 901940 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="mjHi5JgQ"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 40SHWb0b0jz9s1X for ; Fri, 20 Apr 2018 23:58:35 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754902AbeDTN6X (ORCPT ); Fri, 20 Apr 2018 09:58:23 -0400 Received: from mail-vk0-f74.google.com ([209.85.213.74]:38052 "EHLO mail-vk0-f74.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755020AbeDTN6S (ORCPT ); Fri, 20 Apr 2018 09:58:18 -0400 Received: by mail-vk0-f74.google.com with SMTP id r65so5622453vke.5 for ; Fri, 20 Apr 2018 06:58:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:date:message-id:subject:from:to; bh=anWo3Z3EcUOPxDTngjK7mDKtWyOQrxGlUAq0QclIq5Y=; b=mjHi5JgQP7VgVG3qslr+QdBtAwpDKV4hGUBy2PTMAxL5bPrgsoidRHNYxAe3lqKvNZ G3mvCDn3+o/LxX73yzS4Q8ZgtcDupgTZaUU1bbO6eGOA/1vU5zYJoGH+3vzlo7Mvo1zz 1U1laRwLQIUXoQyJcCdLXkWqV+T1vLCtOc6sliGfaiIx8yAV+jSwz9kETfu4KnQoqKjm UE4u5cx1DxMcrP75FMWe+Bhhlz2OVEKM3fQX7YRKvWkCb5qGbwvoDCW1999932kkSr1z hi41uc+cCkoHEH2Z+RbC/2qujk6IOcmKMY87umH9LKRLmj8/0uG8M4UXMzVogwJJCxZg wgFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:date:message-id:subject:from:to; bh=anWo3Z3EcUOPxDTngjK7mDKtWyOQrxGlUAq0QclIq5Y=; b=NrouI87aaLXK4moIxVxOMuwU5ydksIOdEjFY6i8TufVjv9W5OGXdLZ6bJpBcq59LXh P3/OgRmQLG+5xaTNMXJw8/br7tENGErvFyf30jE/HN5H5aXw0dkKaFMp4otLTxDC6F9l 0gL1QJTP/rylXX9l6SKkwvHsoJYf7CTx2Px3VxWqZoXb9ZbfifH7MrSQ/+Kzgtgwy01+ l1rmKveVhlSg5uSMlsT+9vIRMoHI4xdg1rPS2S1MJuSm7bb/Nwl9me5QFc+L/Zyw2BPZ 1ShteR0t1OzqGJrojyLLawJ0URk64z5xO8eG1lPz10i+2AKhYgwAKsLjfRvw4kd3+XZM fr2w== X-Gm-Message-State: ALQs6tBRfnfPci3R78AWdTPCHAHkp8K0itJlppSqSPe2pl8ifCl3SAx4 cwJtP02i0O5qf+lMV7rGLinaLe/EhA== X-Google-Smtp-Source: AIpwx49LWnabJOjhYGeMqNMkVuwUteiq7KSfLqCAcihFLtiHbPQkelgJD9cERie4HaCSlowJGqGTVHx8Nw== MIME-Version: 1.0 X-Received: by 10.159.54.100 with SMTP id s33mr4892539uad.0.1524232697178; Fri, 20 Apr 2018 06:58:17 -0700 (PDT) Date: Fri, 20 Apr 2018 15:57:30 +0200 Message-Id: <20180420135730.44921-1-jannh@google.com> X-Mailer: git-send-email 2.17.0.484.g0c8726318c-goog Subject: [PATCH net] tcp: don't read out-of-bounds opsize From: Jann Horn To: davem@davemloft.net, kuznet@ms2.inr.ac.ru, yoshfuji@linux-ipv6.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, jannh@google.com Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org The old code reads the "opsize" variable from out-of-bounds memory (first byte behind the segment) if a broken TCP segment ends directly after an opcode that is neither EOL nor NOP. The result of the read isn't used for anything, so the worst thing that could theoretically happen is a pagefault; and since the physmap is usually mostly contiguous, even that seems pretty unlikely. The following C reproducer triggers the uninitialized read - however, you can't actually see anything happen unless you put something like a pr_warn() in tcp_parse_md5sig_option() to print the opsize. ==================================== #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void systemf(const char *command, ...) { char *full_command; va_list ap; va_start(ap, command); if (vasprintf(&full_command, command, ap) == -1) err(1, "vasprintf"); va_end(ap); printf("systemf: <<<%s>>>\n", full_command); system(full_command); } char *devname; int tun_alloc(char *name) { int fd = open("/dev/net/tun", O_RDWR); if (fd == -1) err(1, "open tun dev"); static struct ifreq req = { .ifr_flags = IFF_TUN|IFF_NO_PI }; strcpy(req.ifr_name, name); if (ioctl(fd, TUNSETIFF, &req)) err(1, "TUNSETIFF"); devname = req.ifr_name; printf("device name: %s\n", devname); return fd; } #define IPADDR(a,b,c,d) (((a)<<0)+((b)<<8)+((c)<<16)+((d)<<24)) void sum_accumulate(unsigned int *sum, void *data, int len) { assert((len&2)==0); for (int i=0; i> 16) + (sum & 0xffff); sum = (sum >> 16) + (sum & 0xffff); return htons(~sum); } void fix_ip_sum(struct iphdr *ip) { unsigned int sum = 0; sum_accumulate(&sum, ip, sizeof(*ip)); ip->check = sum_final(sum); } void fix_tcp_sum(struct iphdr *ip, struct tcphdr *tcp) { unsigned int sum = 0; struct { unsigned int saddr; unsigned int daddr; unsigned char pad; unsigned char proto_num; unsigned short tcp_len; } fakehdr = { .saddr = ip->saddr, .daddr = ip->daddr, .proto_num = ip->protocol, .tcp_len = htons(ntohs(ip->tot_len) - ip->ihl*4) }; sum_accumulate(&sum, &fakehdr, sizeof(fakehdr)); sum_accumulate(&sum, tcp, tcp->doff*4); tcp->check = sum_final(sum); } int main(void) { int tun_fd = tun_alloc("inject_dev%d"); systemf("ip link set %s up", devname); systemf("ip addr add 192.168.42.1/24 dev %s", devname); struct { struct iphdr ip; struct tcphdr tcp; unsigned char tcp_opts[20]; } __attribute__((packed)) syn_packet = { .ip = { .ihl = sizeof(struct iphdr)/4, .version = 4, .tot_len = htons(sizeof(syn_packet)), .ttl = 30, .protocol = IPPROTO_TCP, /* FIXUP check */ .saddr = IPADDR(192,168,42,2), .daddr = IPADDR(192,168,42,1) }, .tcp = { .source = htons(1), .dest = htons(1337), .seq = 0x12345678, .doff = (sizeof(syn_packet.tcp)+sizeof(syn_packet.tcp_opts))/4, .syn = 1, .window = htons(64), .check = 0 /*FIXUP*/ }, .tcp_opts = { /* INVALID: trailing MD5SIG opcode after NOPs */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 19 } }; fix_ip_sum(&syn_packet.ip); fix_tcp_sum(&syn_packet.ip, &syn_packet.tcp); while (1) { int write_res = write(tun_fd, &syn_packet, sizeof(syn_packet)); if (write_res != sizeof(syn_packet)) err(1, "packet write failed"); } } ==================================== Fixes: cfb6eeb4c860 ("[TCP]: MD5 Signature Option (RFC2385) support.") Signed-off-by: Jann Horn --- net/ipv4/tcp_input.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 367def6ddeda..e51c644484dc 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3868,11 +3868,8 @@ const u8 *tcp_parse_md5sig_option(const struct tcphdr *th) int length = (th->doff << 2) - sizeof(*th); const u8 *ptr = (const u8 *)(th + 1); - /* If the TCP option is too short, we can short cut */ - if (length < TCPOLEN_MD5SIG) - return NULL; - - while (length > 0) { + /* If not enough data remaining, we can short cut */ + while (length >= TCPOLEN_MD5SIG) { int opcode = *ptr++; int opsize;