Index | Thread | Search

From:
Vitaliy Makkoveev <mvs@openbsd.org>
Subject:
Re: syzkaller ip divert packet options
To:
Alexander Bluhm <alexander.bluhm@gmx.net>
Cc:
tech@openbsd.org
Date:
Mon, 4 Mar 2024 18:59:43 +0300

Download raw body.

Thread
On Mon, Mar 04, 2024 at 09:56:47PM +0100, Alexander Bluhm wrote:
> Hi,
> 
> syskaller has found a crash caused by divert_output().
> 
> https://syzkaller.appspot.com/bug?extid=b1ba3a2a8ef13e5b4698
> 
> panic: malformed IPv4 option passed to ip_optcopy
> Stopped at      db_enter+0x1c:  addq    $0x8,%rsp
>     TID    PID    UID     PRFLAGS     PFLAGS  CPU  COMMAND
>  362957  35437      0           0          0    0  syz-executor.0
> *219614  35437      0           0  0x4000000    1  syz-executor.0
> db_enter() at db_enter+0x1c sys/arch/amd64/amd64/db_interface.c:437
> panic(ffffffff8291a600) at panic+0x17b sys/kern/subr_prf.c:198
> ip_fragment(fffffd807158d200,ffff80002a22a5d8,ffff8000001a02a8,5dc) at ip_fragment+0x5b4
> ip_output(fffffd807158d200,0,fffffd806f48e388,22,0,0,838db83e2fdf1dbc) at ip_output+0xe3c sys/netinet/ip_output.c:478
> divert_output(fffffd806f48e310,fffffd807158d200,fffffd80716d6200,0) at divert_output+0x30a sys/netinet/ip_divert.c:174
> sosend(ffff800000db6248,fffffd80716d6200,ffff80002a22a840,0,0,0) at sosend+0x66d
> sendit(ffff80002a188558,3,ffff80002a22a9d8,0,ffff80002a22a9c8) at sendit+0x65d sys/kern/uipc_syscalls.c:786
> sys_sendmmsg(ffff80002a188558,ffff80002a22ab80,ffff80002a22aad0) at sys_sendmmsg+0x344 sys/kern/uipc_syscalls.c:677
> syscall(ffff80002a22ab80) at syscall+0x5ae mi_syscall sys/sys/syscall_mi.h:183 [inline]
> syscall(ffff80002a22ab80) at syscall+0x5ae sys/arch/amd64/amd64/trap.c:577
> Xsyscall() at Xsyscall+0x128
> end of kernel
> end trace frame: 0x75df06dd130, count: 5
> 
> When sending raw packets with divert output, IP options are never
> 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.  This should also be called from
> divert_output().
> 
> ok?
> 

ok mvs

> bluhm
> 
> Index: netinet/ip_divert.c
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_divert.c,v
> diff -u -p -r1.94 ip_divert.c
> --- netinet/ip_divert.c	11 Feb 2024 18:14:26 -0000	1.94
> +++ netinet/ip_divert.c	4 Mar 2024 19:26:51 -0000
> @@ -100,21 +100,19 @@ divert_output(struct inpcb *inp, struct 
>  	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 
>  		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 
>  fail:
>  	m_freem(m);
>  	divstat_inc(divs_errors);
> -	return (error ? error : EINVAL);
> +	return (error);
>  }
>  
>  void
> Index: netinet/ip_var.h
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_var.h,v
> diff -u -p -r1.113 ip_var.h
> --- netinet/ip_var.h	13 Feb 2024 12:22:09 -0000	1.113
> +++ netinet/ip_var.h	4 Mar 2024 19:16:36 -0000
> @@ -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 *);
> Index: netinet/raw_ip.c
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/raw_ip.c,v
> diff -u -p -r1.156 raw_ip.c
> --- netinet/raw_ip.c	11 Feb 2024 18:14:26 -0000	1.156
> +++ netinet/raw_ip.c	4 Mar 2024 19:16:30 -0000
> @@ -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)
>  {
>