kern/158930: BPF element leak in ifp->bpf_if->bif_dlist

lanxiaowei lanxw at arraynetworks.com.cn
Fri Jul 15 02:50:08 UTC 2011


>Number:         158930
>Category:       kern
>Synopsis:       BPF element leak in ifp->bpf_if->bif_dlist
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jul 15 02:50:07 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     lanxiaowei
>Release:        bsd7.0
>Organization:
arraynetworks
>Environment:
FreeBSD dev170-04.arraynetworks.net 7.0-RELEASE FreeBSD 7.0-RELEASE #0: Sun Feb 24 10:35:36 UTC 2008     root at driscoll.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  amd64

>Description:
when i send arp repeatedly in userland by BPF, i find:
when the data flow is large, the performance will be very slow.
for example: if i dont' send arp, the data flow can reach 3.5G,
if i send arp, the data flow can only reach 1.5G.

Finally, i find the reason:
the BPF will leak some device in ifp->bpf_if->bif_dlist, so the system will capture passing packets in ETHER_BPF_MTAP!  
>How-To-Repeat:
1. run daemon
userland code:
AN# cat a.c
#include <sys/types.h> 
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <net/if.h>
#include <net/bpf.h>
#include <fcntl.h>
#include <sys/ioctl.h>
int
open_bpf(char *ifname)
{
        struct ifreq ifr;
        int fd, n = 0;
        char device[sizeof "/dev/bpf000"];

        /*
         * Go through all the minors and find one that isn't in use.
         */
        do {
                (void)sprintf(device, "/dev/bpf%d", n++);
                fd = open(device, O_RDWR);
        } while (fd < 0 && errno == EBUSY && n < 100);

        if (fd < 0) {
                return -1;
        }

        (void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
        if (ioctl(fd, BIOCSETIF, (caddr_t) &ifr) < 0) {
                return -1;
        }

        return fd;
}




main()
{
int fd;
char buf[3333];
int len=3332;
while(1){
fd = open_bpf("em0");
        if (fd < 0) {
        return -1;
}

len = write(fd, buf, len);
close(fd);
sleep(10);
}
}

2.kgdb /kernel /dev/mem
(kgdb) p bpf_bpfd_cnt
$1 = 8 (it will increase, so it's a leak!)
>Fix:
in devfs_close(), i found the fd can't correctly closed.:
	if (vp->v_iflag & VI_DOOMED) {
		/* Forced close. */
	} else if (dsw->d_flags & D_TRACKCLOSE) {
		/* Keep device updated on status. */
	} else if (count_dev(dev) > 1) {<----------------it go into this case!
		VI_UNLOCK(vp);
		dev_relthread(dev);
		return (0);
	}



i don't know why it go into this case when close BPF device, so it don't call
bpfclose() to remove the BPF device from ifp->bpf_if->bif_dlist.


thanks.

Patch attached with submission follows:

#include <sys/types.h> 
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <net/if.h>
#include <net/bpf.h>
#include <fcntl.h>
#include <sys/ioctl.h>
int
open_bpf(char *ifname)
{
	struct ifreq ifr;
	int fd, n = 0;
	char device[sizeof "/dev/bpf000"];

	/*
	 * Go through all the minors and find one that isn't in use.
	 */
	do {
		(void)sprintf(device, "/dev/bpf%d", n++);
		fd = open(device, O_RDWR);
	} while (fd < 0 && errno == EBUSY && n < 100);

	if (fd < 0) {
		return -1;
	}

	(void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	if (ioctl(fd, BIOCSETIF, (caddr_t) &ifr) < 0) {
		return -1;
	}

	return fd;
}




main()
{
int fd;
char buf[3333];
int len=3332;
while(1){
fd = open_bpf("em0");
	if (fd < 0) {
	return -1;
}

len = write(fd, buf, len);
close(fd);
sleep(10);
}
}


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list