From c8a0ef6cb9913b93aa3144737437f48b578e9aa0 Mon Sep 17 00:00:00 2001 From: bluhm Date: Tue, 5 Mar 2024 09:45:13 +0000 Subject: [PATCH] Validate IPv4 packet options in divert output. When sending raw packets over divert socket, IP options were not validated. Fragment code tries to copy them and crashes. Raw IP output has a similar feature, but uses rip_chkhdr() to prevent invalid packets from userland. Call this funtion also from divert_output() for strict user input validation. Reported-by: syzbot+b1ba3a2a8ef13e5b4698@syzkaller.appspotmail.com OK dlg@ deraadt@ mvs@ --- sys/netinet/ip_divert.c | 28 ++++++++++++++-------------- sys/netinet/ip_var.h | 4 +++- sys/netinet/raw_ip.c | 4 +--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c index d5ee3004bcb..9c050bb12cc 100644 --- a/sys/netinet/ip_divert.c +++ b/sys/netinet/ip_divert.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_divert.c,v 1.94 2024/02/11 18:14:26 mvs Exp $ */ +/* $OpenBSD: ip_divert.c,v 1.95 2024/03/05 09:45:13 bluhm Exp $ */ /* * Copyright (c) 2009 Michele Marchetto @@ -100,21 +100,19 @@ divert_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam, if ((error = in_nam2sin(nam, &sin))) goto fail; - /* Do basic sanity checks. */ - if (m->m_pkthdr.len < sizeof(struct ip)) + if (m->m_pkthdr.len > IP_MAXPACKET) { + error = EMSGSIZE; goto fail; - if ((m = m_pullup(m, sizeof(struct ip))) == NULL) { - /* m_pullup() has freed the mbuf, so just return. */ - divstat_inc(divs_errors); - return (ENOBUFS); } - ip = mtod(m, struct ip *); - if (ip->ip_v != IPVERSION) + + m = rip_chkhdr(m, NULL); + if (m == NULL) { + error = EINVAL; goto fail; + } + + ip = mtod(m, struct ip *); off = ip->ip_hl << 2; - if (off < sizeof(struct ip) || ntohs(ip->ip_len) < off || - m->m_pkthdr.len < ntohs(ip->ip_len)) - goto fail; dir = (sin->sin_addr.s_addr == INADDR_ANY ? PF_OUT : PF_IN); @@ -135,8 +133,10 @@ divert_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam, min_hdrlen = 0; break; } - if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen) + if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen) { + error = EINVAL; goto fail; + } m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET; @@ -181,7 +181,7 @@ divert_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam, fail: m_freem(m); divstat_inc(divs_errors); - return (error ? error : EINVAL); + return (error); } void diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index b0e6ba89106..2c0d45379df 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_var.h,v 1.113 2024/02/13 12:22:09 bluhm Exp $ */ +/* $OpenBSD: ip_var.h,v 1.114 2024/03/05 09:45:13 bluhm Exp $ */ /* $NetBSD: ip_var.h,v 1.16 1996/02/13 23:43:20 christos Exp $ */ /* @@ -261,6 +261,8 @@ void rip_init(void); int rip_input(struct mbuf **, int *, int, int); int rip_output(struct mbuf *, struct socket *, struct sockaddr *, struct mbuf *); +struct mbuf * + rip_chkhdr(struct mbuf *, struct mbuf *); int rip_attach(struct socket *, int, int); int rip_detach(struct socket *); void rip_lock(struct socket *); diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index 0075382bf54..f40c8e95225 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -1,4 +1,4 @@ -/* $OpenBSD: raw_ip.c,v 1.156 2024/02/11 18:14:26 mvs Exp $ */ +/* $OpenBSD: raw_ip.c,v 1.157 2024/03/05 09:45:13 bluhm Exp $ */ /* $NetBSD: raw_ip.c,v 1.25 1996/02/18 18:58:33 christos Exp $ */ /* @@ -128,8 +128,6 @@ rip_init(void) in_pcbinit(&rawcbtable, 1); } -struct mbuf *rip_chkhdr(struct mbuf *, struct mbuf *); - int rip_input(struct mbuf **mp, int *offp, int proto, int af) { -- 2.20.1