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