From: Alexander Bluhm Subject: interface ioctl loopback flags To: tech@openbsd.org Date: Thu, 11 Apr 2024 16:27:38 +0200 Hi, syzkaller has found a crash in igmp_leavegroup. https://syzkaller.appspot.com/bug?extid=2f24ed6c8ddb2d6bb22c uvm_fault(0xfffffd80074a6158, 0x4, 0, 1) -> e kernel: page fault trap, code=0 Stopped at igmp_leavegroup+0xaf: movl 0x4(%rax),%r12d TID PID UID PRFLAGS PFLAGS CPU COMMAND igmp_leavegroup(ffff800000dcbe00,ffff8000006ab000) at igmp_leavegroup+0xaf sys/netinet/igmp.c:512 in_delmulti(ffff800000dcbe00) at in_delmulti+0xd3 sys/netinet/in.c:908 ip_freemoptions(ffff8000006ba790) at ip_freemoptions+0x5d sys/netinet/ip_output.c:1737 in_pcbdetach(fffffd806ed65168) at in_pcbdetach+0x97 sys/netinet/in_pcb.c:606 udp_detach(fffffd806e5f4948) at udp_detach+0x3f sys/netinet/udp_usrreq.c:1139 soclose(fffffd806e5f4948,0) at soclose+0x80 pru_detach sys/sys/protosw.h:283 [inline] soclose(fffffd806e5f4948,0) at soclose+0x80 sys/kern/uipc_socket.c:411 soo_close(fffffd807251a350,ffff80002a5d71f8) at soo_close+0x44 fdrop(fffffd807251a350,ffff80002a5d71f8) at fdrop+0xd5 sys/kern/kern_descrip.c:1274 closef(fffffd807251a350,ffff80002a5d71f8) at closef+0x11b sys/kern/kern_descrip.c:1258 fdfree(ffff80002a5d71f8) at fdfree+0xe3 sys/kern/kern_descrip.c:1190 exit1(ffff80002a5d71f8,0,0,1) at exit1+0x371 sys/kern/kern_exit.c:199 sys_exit(ffff80002a5d71f8,ffff80002a6650e0,ffff80002a665030) at sys_exit+0x1a sys/kern/kern_exit.c:89 syscall(ffff80002a6650e0) at syscall+0x72a sys/arch/amd64/amd64/trap.c:577 Xsyscall() at Xsyscall+0x128 It crashes here as inm->inm_rti is NULL. void igmp_leavegroup(struct in_multi *inm, struct ifnet *ifp) { switch (inm->inm_state) { case IGMP_DELAYING_MEMBER: case IGMP_IDLE_MEMBER: if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && (ifp->if_flags & IFF_LOOPBACK) == 0) * if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) igmp_sendpkt(ifp, inm, IGMP_HOST_LEAVE_MESSAGE, INADDR_ALLROUTERS_GROUP); break; case IGMP_LAZY_MEMBER: case IGMP_AWAKENING_MEMBER: case IGMP_SLEEPING_MEMBER: break; } } The inm->inm_rti should be set in igmp_joingroup() which calls rti_fill(). void igmp_joingroup(struct in_multi *inm, struct ifnet *ifp) { int i; inm->inm_state = IGMP_IDLE_MEMBER; if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && (ifp->if_flags & IFF_LOOPBACK) == 0) { * i = rti_fill(inm); igmp_sendpkt(ifp, inm, i, 0); inm->inm_state = IGMP_DELAYING_MEMBER; inm->inm_timer = IGMP_RANDOM_DELAY( IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); igmp_timers_are_running = 1; } else inm->inm_timer = 0; } Note that both places have the condition (ifp->if_flags & IFF_LOOPBACK). ddb in igmp_leavegroup() shows that ifp is lo0 without IFF_LOOPBACK. As lo0 is a loopback interface, the rti_fill() is skipped and inm->inm_rti is kept as NULL. Then syzkaller clears the IFF_LOOPBACK flag with SIOCSIFFLAGS ioctl. Finally igmp_leavegroup() expects that inm->inm_rti exists for non loopback interface. Is there any usecase that userland can modify the IFF_LOOPBACK flag? If I add IFF_LOOPBACK to the unchangeable flags, kernel does not crash. ok? bluhm Index: net/if.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/net/if.h,v diff -u -p -r1.215 if.h --- net/if.h 11 Nov 2023 14:24:03 -0000 1.215 +++ net/if.h 11 Apr 2024 13:58:22 -0000 @@ -219,7 +219,7 @@ struct if_status_description { /* flags set internally only: */ #define IFF_CANTCHANGE \ - (IFF_BROADCAST|IFF_POINTOPOINT|IFF_RUNNING|IFF_OACTIVE|\ + (IFF_BROADCAST|IFF_LOOPBACK|IFF_POINTOPOINT|IFF_RUNNING|IFF_OACTIVE|\ IFF_SIMPLEX|IFF_MULTICAST|IFF_ALLMULTI) #define IFXF_MPSAFE 0x1 /* [I] if_start is mpsafe */