Index | Thread | Search

From:
Alexander Bluhm <bluhm@openbsd.org>
Subject:
interface ioctl loopback flags
To:
tech@openbsd.org
Date:
Thu, 11 Apr 2024 16:27:38 +0200

Download raw body.

Thread
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 */