I think we both agree the description of BIOCLOCK is very hard to read.
It is not written in the third-person imperfect way, and switches back and
forth between explaining the purpose of the mechanism and the actual
mechanism. Worse, it goes wrong by leading with the purpose..
> That sounds as if "lock" is either the wrong term or should be
> qualified so as to distinguish it from *file* locks.
I want to go further with this.
Here's a draft of new text which is much shorter:
BIOCLOCK
Prevents further changes to the configuration of the bpf
descriptor by only permitting a set of "known safe" ioctls:
BIOCFLUSH, BIOCGBLEN, BIOCGDIRFILT, BIOCGDLT, BIOCGDIRFILT,
BIOCGDLTLIST, BIOCGETIF, BIOCGHDRCMPLT, BIOCGRSIG, BIOCGRTIMEOUT,
BIOCGSTATS, BIOCIMMEDIATE, BIOCLOCK, BIOCSRTIMEOUT,
BIOCSWTIMEOUT, BIOCDWTIMEOUT, BIOCVERSION, TIOCGPGRP, and
FIONREAD. All other ioctl operations will be denied with EPERM.
The purpose of this mechanism is that a privileged program can
open a bpf device, drop privileges, set the interface, provide
filters and modes, and then lock it with BIOCLOCK. If the device
is opened with write, BIOCLOCK does not prevent writes, so a
write filter is probably needed. If applied correctly, a bug in
a program will not result in the ability to see or send
unfiltered traffic to the network interface.
The first paragraph describes the mechanism, so the word "lock" in the
second paragraph doesn't feel unnatural.
Index: share/man/man4/bpf.4
===================================================================
RCS file: /cvs/src/share/man/man4/bpf.4,v
diff -u -p -u -r1.47 bpf.4
--- share/man/man4/bpf.4 15 Aug 2024 12:20:20 -0000 1.47
+++ share/man/man4/bpf.4 12 Nov 2025 23:17:36 -0000
@@ -189,23 +189,11 @@ returned by
.Dv BIOCGSTATS .
.Pp
.It Dv BIOCLOCK
-This ioctl is designed to prevent the security issues associated
-with an open
+Prevents further changes to the configuration of the
.Nm
-descriptor in unprivileged programs.
-Even with dropped privileges, an open
-.Nm
-descriptor can be abused by a rogue program to listen on any interface
-on the system, send packets on these interfaces if the descriptor was
-opened read-write and send signals to arbitrary processes using the
-signaling mechanism of
-.Nm bpf .
-By allowing only
+descriptor by only permitting a set of
.Dq known safe
-ioctls, the
-.Dv BIOCLOCK
-ioctl prevents this abuse.
-The allowable ioctls are
+ioctls:
.Dv BIOCFLUSH ,
.Dv BIOCGBLEN ,
.Dv BIOCGDIRFILT ,
@@ -226,23 +214,19 @@ The allowable ioctls are
.Dv TIOCGPGRP ,
and
.Dv FIONREAD .
-Use of any other ioctl is denied with error
+All other ioctl operations will be denied with
.Er EPERM .
-Once a descriptor is locked, it is not possible to unlock it.
-A process with root privileges is not affected by the lock.
.Pp
-A privileged program can open a
+The purpose of this mechanism is that a privileged program can open a
.Nm
-device, drop privileges, set the interface, filters and modes on the
-descriptor, and lock it.
-Once the descriptor is locked, the system is safe
-from further abuse through the descriptor.
-Locking a descriptor does not prevent writes.
-If the application does not need to send packets through
-.Nm bpf ,
-it can open the device read-only to prevent writing.
-If sending packets is necessary, a write-filter can be set before locking the
-descriptor to prevent arbitrary packets from being sent out.
+device, drop privileges, set the interface, provide filters and modes,
+and then lock it with
+.Dv BIOCLOCK .
+If the device is opened with write,
+.Dv BIOCLOCK
+does not prevent writes, so a write filter is probably needed.
+If applied correctly, a bug in a program will not result in the ability to
+see or send unfiltered traffic to the network interface.
.Pp
.It Dv BIOCGETIF Fa "struct ifreq *"
Returns the name of the hardware interface that the file is listening on.
Additionally you asked:
> I.e., you're not objecting to the text of that sentence, you're
> objecting to the reality that the sentence describes, i.e. "root gets
> a free pass".
No, I'm also objecting to root being able to bypass this mechanism, so
here's the kernel diff which makes root not special. The man page diff
above removed mention of the special case for root. I've reached out to
a few of our privsep-bpf tool developers to search for reason for root
to be special, and so far we've found no reason in our tree so far.
Index: sys/net/bpf.c
===================================================================
RCS file: /cvs/src/sys/net/bpf.c,v
diff -u -p -u -r1.234 bpf.c
--- sys/net/bpf.c 7 Jul 2025 02:28:50 -0000 1.234
+++ sys/net/bpf.c 12 Nov 2025 22:58:01 -0000
@@ -778,8 +778,8 @@ bpfioctl(dev_t dev, u_long cmd, caddr_t
int error = 0;
d = bpfilter_lookup(minor(dev));
- if (d->bd_locked && suser(p) != 0) {
- /* list of allowed ioctls when locked and not root */
+ if (d->bd_locked) {
+ /* list of allowed ioctls when locked */
switch (cmd) {
case BIOCGBLEN:
case BIOCFLUSH: