On Fri, Oct 22, 2021 at 06:21:44PM +0200, [email protected] wrote:
> >Synopsis: opening and closeing bpf rapidly causes problems
> >Category: system
> >Environment:
> System : OpenBSD 7.0
> Details : OpenBSD 7.0 (GENERIC.MP) #1332: Thu Sep 30 16:53:51 MDT
> 2021
>
> [email protected]:/usr/src/sys/arch/arm64/compile/GENERIC.MP
>
> Architecture: OpenBSD.arm64
> Machine : arm64
> >Description:
> Trying to attack a switch on my own network caused my raspberry pi to
> freeze (watchdog rebooted it). Though after disabling watchdog it just froze.
> Whether there is a panic I could not tell as I'm console-less and my dongle
> for
> serial doesn't reach the wires to my Pi where it's currently located. It'd be
> a hardship. Thankfully I can repeat the problem. Other thankfulness is it
> can only be done as root in a default system.
> >How-To-Repeat:
> The following program repeats this:
>
> #include <sys/param.h>
> #include <sys/time.h>
> #include <sys/ioctl.h>
> #include <sys/socket.h>
>
> #include <net/bpf.h>
> #include <net/if.h>
>
> #include <errno.h>
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
> #include <fcntl.h>
>
> int open_filter(char *interface);
>
> int
> main(void)
> {
> int fd;
>
> for (int i = 0; i < 10000; i++) {
> fd = open_filter("bse0");
> if (fd < 0)
> continue;
> close(fd);
> }
>
> exit(0);
> }
>
>
>
>
> /*
> * open the bpf devices and attach them to the corresponding interface that
> * is provided
> */
>
> int
> open_filter(char *interface)
> {
> struct ifreq ifr;
> char buf[PATH_MAX];
> int i = 0, fd;
> u_int hdrcomplete, dltype;
>
> do {
> snprintf(buf, sizeof(buf), "/dev/bpf%d", i++);
> fd = open(buf, O_RDWR, 0);
> } while (fd < 0 && errno == EBUSY);
>
> if (fd < 0) {
> perror("open");
> return -1;
> }
>
> /* set interface on bpf */
> memset(&ifr, 0, sizeof(ifr));
> strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name) - 1);
> if (ioctl(fd, BIOCSETIF, &ifr) < 0) {
> perror("ioctl 2");
> close (fd);
> return -1;
> }
> /* write complete frame headers */
> hdrcomplete=1;
> if (ioctl(fd, BIOCSHDRCMPLT, &hdrcomplete) < 0) {
> perror("ioctl 3");
> return -1;
> }
> /*
> * If we're not ethernet return with -1 as there is no point opening
> * bpf for a utility that is a ethernet spoofer
> */
> if (ioctl(fd, BIOCGDLT, &dltype) < 0) {
> perror("ioctl 4");
> return -1;
> }
> if (dltype == DLT_EN10MB)
> return (fd);
>
> fprintf(stderr, "dltype != DLT_EN10MB, missing -l flag?\n");
>
> errno = ENOSYS;
> close (fd);
> return -1;
> }
> >Fix:
> None provided. Unfortunately I don't have a DDB capable console.
Thank you for the report. It looks that the bug is generic and not
specific to the machine.
On amd64, the test program triggers the following:
panic: free: size too large 32768 > 512 (0xffff800005683000) type devbuf
Stopped at db_enter+0x10: popq %rbp
TID PID UID PRFLAGS PFLAGS CPU COMMAND
182043 96299 0 0x3 0 3K bpftest
*203812 7765 0 0x14000 0x200 1 smr
db_enter() at db_enter+0x10
panic(ffffffff81e64303) at panic+0xbf
free(ffff800005683000,2,8000) at free+0x409
bpf_d_smr(ffff800005612e00) at bpf_d_smr+0x4f
smr_thread(ffff8000225da548) at smr_thread+0x21e
end trace frame: 0x0, count: 10
The panic happens when freeing bd->bd_fbuf. Curiously, both bd->bd_sbuf
and bd->bd_hbuf are NULL. Also, many calls of ioctl(BIOCSETIF) have
failed with ENOMEM. If bpf_allocbufs() fails, bd->bd_fbuf might be
a dangling pointer.
The following fix seems to help:
Index: net/bpf.c
===================================================================
RCS file: src/sys/net/bpf.c,v
retrieving revision 1.205
diff -u -p -r1.205 bpf.c
--- net/bpf.c 15 Jun 2021 05:24:47 -0000 1.205
+++ net/bpf.c 23 Oct 2021 03:19:56 -0000
@@ -1553,6 +1553,7 @@ bpf_allocbufs(struct bpf_d *d)
d->bd_sbuf = malloc(d->bd_bufsize, M_DEVBUF, M_NOWAIT);
if (d->bd_sbuf == NULL) {
free(d->bd_fbuf, M_DEVBUF, d->bd_bufsize);
+ d->bd_fbuf = NULL;
return (ENOMEM);
}