From: David Gwynne Subject: Re: syzkaller ip divert packet options To: Alexander Bluhm Cc: tech@openbsd.org Date: Tue, 5 Mar 2024 10:17:52 +1000 > On 5 Mar 2024, at 06:56, 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 > > 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) > { >