linux/net/ipv4
Jann Horn 7e5a206ab6 tcp: don't read out-of-bounds opsize
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 <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <net/if.h>
#include <linux/if.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <linux/if_tun.h>
#include <err.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <assert.h>

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<len/2; i++) {
    *sum += ntohs(((unsigned short *)data)[i]);
  }
}

unsigned short sum_final(unsigned int sum) {
  sum = (sum >> 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: cfb6eeb4c8 ("[TCP]: MD5 Signature Option (RFC2385) support.")
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-04-23 09:51:06 -04:00
..
netfilter kbuild: rename *-asn1.[ch] to *.asn1.[ch] 2018-04-07 19:04:02 +09:00
Kconfig ipmr,ipmr6: Define a uniform vif_device 2018-03-01 13:13:23 -05:00
Makefile ipmr,ipmr6: Define a uniform vif_device 2018-03-01 13:13:23 -05:00
af_inet.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next 2018-03-31 23:33:04 -04:00
ah4.c
arp.c arp: fix arp_filter on l3slave devices 2018-04-05 22:05:03 -04:00
cipso_ipv4.c
datagram.c
devinet.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
esp4.c
esp4_offload.c esp: check the NETIF_F_HW_ESP_TX_CSUM bit before segmenting 2018-02-27 10:46:01 +01:00
fib_frontend.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
fib_lookup.h
fib_notifier.c
fib_rules.c ipv6: route: dissect flow in input path if fib rules need it 2018-02-28 22:44:44 -05:00
fib_semantics.c route: check sysctl_fib_multipath_use_neigh earlier than hash 2018-04-01 20:57:39 -04:00
fib_trie.c net/ipv4: Allow notifier to fail route replace 2018-03-29 14:10:30 -04:00
fou.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
gre_demux.c
gre_offload.c
icmp.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
igmp.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
inet_connection_sock.c
inet_diag.c sock_diag: request _diag module only when the family or proto has been registered 2018-03-12 11:03:42 -04:00
inet_fragment.c inet: frags: remove inet_frag_maybe_warn_overflow() 2018-03-31 23:25:39 -04:00
inet_hashtables.c
inet_timewait_sock.c soreuseport: initialise timewait reuseport field 2018-04-07 22:32:32 -04:00
inetpeer.c inetpeer: fix uninit-value in inet_getpeer 2018-04-09 10:57:35 -04:00
ip_forward.c net: rename skb_gso_validate_mtu -> skb_gso_validate_network_len 2018-03-04 17:49:17 -05:00
ip_fragment.c inet: frags: fix ip6frag_low_thresh boundary 2018-04-04 12:04:59 -04:00
ip_gre.c ip_gre: clear feature flags when incompatible o_flags are set 2018-04-10 11:03:32 -04:00
ip_input.c net: Make ip_ra_chain per struct net 2018-03-22 15:12:56 -04:00
ip_options.c
ip_output.c net: Fix one possible memleak in ip_setup_cork 2018-04-16 12:57:06 -04:00
ip_sockglue.c net: Replace ip_ra_lock with per-net mutex 2018-03-22 15:12:56 -04:00
ip_tunnel.c ip_tunnel: better validate user provided tunnel names 2018-04-05 15:16:15 -04:00
ip_tunnel_core.c
ip_vti.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
ipcomp.c
ipconfig.c net: Use octal not symbolic permissions 2018-03-26 12:07:48 -04:00
ipip.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
ipmr.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
ipmr_base.c ipmr: Make ipmr_dump() common 2018-03-26 13:14:43 -04:00
netfilter.c
ping.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
proc.c inet: frags: break the 2GB limit for frags storage 2018-03-31 23:25:39 -04:00
protocol.c
raw.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
raw_diag.c
route.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net 2018-04-09 17:04:10 -07:00
syncookies.c net/ipv4: disable SMC TCP option with SYN Cookies 2018-03-25 20:53:54 -04:00
sysctl_net_ipv4.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
tcp.c tcp: clear tp->packets_out when purging write queue 2018-04-16 11:23:42 -04:00
tcp_bbr.c net-tcp_bbr: set tp->snd_ssthresh to BDP upon STARTUP exit 2018-03-16 15:07:48 -04:00
tcp_bic.c
tcp_cdg.c
tcp_cong.c
tcp_cubic.c
tcp_dctcp.c
tcp_diag.c
tcp_fastopen.c
tcp_highspeed.c
tcp_htcp.c
tcp_hybla.c
tcp_illinois.c net/tcp/illinois: replace broken algorithm reference link 2018-02-28 12:03:47 -05:00
tcp_input.c tcp: don't read out-of-bounds opsize 2018-04-23 09:51:06 -04:00
tcp_ipv4.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next 2018-03-31 23:33:04 -04:00
tcp_lp.c
tcp_metrics.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
tcp_minisocks.c crypto : chtls - CPL handler definition 2018-03-31 23:37:32 -04:00
tcp_nv.c
tcp_offload.c
tcp_output.c tcp_bbr: better deal with suboptimal GSO (II) 2018-03-01 21:44:28 -05:00
tcp_rate.c
tcp_recovery.c
tcp_scalable.c
tcp_timer.c tcp: purge write queue upon aborting the connection 2018-03-07 15:01:03 -05:00
tcp_ulp.c
tcp_vegas.c
tcp_vegas.h
tcp_veno.c
tcp_westwood.c
tcp_yeah.c
tunnel4.c inet: whitespace cleanup 2018-02-28 11:43:28 -05:00
udp.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next 2018-03-31 23:33:04 -04:00
udp_diag.c
udp_impl.h
udp_offload.c
udp_tunnel.c
udplite.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
xfrm4_input.c
xfrm4_mode_beet.c
xfrm4_mode_transport.c
xfrm4_mode_tunnel.c xfrm: Verify MAC header exists before overwriting eth_hdr(skb)->h_proto 2018-03-07 10:54:29 +01:00
xfrm4_output.c net: xfrm: use skb_gso_validate_network_len() to check gso sizes 2018-03-04 17:49:17 -05:00
xfrm4_policy.c net: Drop pernet_operations::async 2018-03-27 13:18:09 -04:00
xfrm4_protocol.c
xfrm4_state.c
xfrm4_tunnel.c