Index | Thread | Search

From:
Alexandr Nedvedicky <sashan@fastmail.net>
Subject:
Re: sys/net/pf_ioctl.c: out of bounds read in pf_rule_copyin
To:
Johann H??pfner <hoepf@cit.tum.de>
Cc:
tech@openbsd.org
Date:
Mon, 6 Jul 2026 14:00:25 +0200

Download raw body.

Thread
Hello,

I've seen the report too.

On Mon, Jul 06, 2026 at 12:05:31PM +0200, Johann H??pfner wrote:
> Hello tech@
> 
> In testing an experimental kernel-address-sanitizer in syzkaller 
> the following issue was discovered. (and independently found by the
> implementation running in syzbot this morning:
> https://syzkaller.appspot.com/bug?extid=f028f6e8a734a5e8de11)
> 
> pf_rule_copyin executes strlcpy on non-null-terminated fields from 
> userspace leading to a read beyond the allocation pointed to by "from", 
> only if userspace sends a struct pf_rule without zero-terminators, e.g.
> as follows. 
> 
> #include <fcntl.h>
> #include <string.h>
> #include <sys/ioctl.h>
> #include <net/if.h>
> #include <net/pfvar.h>
> int main(){
>         int fd = open("/dev/pf", O_RDWR);
>         struct pfioc_rule pr;
>         memset(&pr, 'A', sizeof(struct pfioc_rule));
>         ioctl(fd, DIOCADDRULE, &pr);
> }
> 
> Due to "from" being allocated in a 4096 byte bucket this is very unlikely
> to cause a crash without kasan.
> 

    I'm not sure how diff below helps. My understanding of the crash
    is that kasan fuzzer tries to load memory which is shorter than
    sizeof (struct pfioc_rule), the pfioc_rule size on amd64 is 3424.

    I have not seen the reproducer used by sanitizer, however my guess is
    the sanitizer passes buffer (&pr) to DIOCADDRULE rule ioctl(2) command
    which is smaller than sizeof (struct pfioc_rule).

    so the diff below will mostlikely die (crash) as soon as it
    does ...->whatever[sizeof(from->label) - 1] = '\0';

what  is really missing here is check which ensures the DIOCADDRULE
ioctl comes with the right size of data
(size(from) == sizeof (struct pfioc_rule)). The pf(4) currently
relies on fact that DIOCADDRULE command is available to root user
only and root user should be executing the trusted code.

perhaps we can either teach syzkaller to generate more sesnsible (realistic)
reproducers. In this case explain syzcaller to always pass buffer of
expected size.

or fix the pf(4) ioctl to check the operation comes with expected size.


Also I've tried simple shell script on my pf test machine. the machine did
not crash.

my understanding is that there is enough address space mapped on stack, so dereference
is safe in kernel. I don't know what kind of magic is sanitizer doing to make kernel
to dereference invalid address. Perhaps the pfioc_rule of invalid size is placed
somewhere close to the end of the page.

thanks and
regards
sashan

--------8<---------------8<---------------8<------------------8<--------
#!/bin/sh

SIZE=1

while [ ${SIZE} -le 3424 ] ; do
echo "Trying ${SIZE}"
cat <<EOF > crash.c

#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/pfvar.h>


int main(){
        int fd = open("/dev/pf", O_RDWR);
        char pfioc_rule[$SIZE];
	memset(pfioc_rule, 'A', sizeof (pfioc_rule));
        ioctl(fd, DIOCADDRULE, pfioc_rule);
}
EOF

make crash
./crash

SIZE=$((${SIZE} + 1))

done