tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
sys/dev/ic/lpt.c: Context-switch-caused heap OOB read
Hello tech-kern@
A bug that I recently found in OpenBSD sys/dev/ic/lpt.c applies to
NetBSD and FreeBSD too.
In short lptwrite reuses the same state struct under concurrent write
syscalls. Care is taken in most of the code that the current pointer and
remaining counts always point into the allocated heap buffer on context
switches. However in lptwrite, sc->sc_cp (the iterator pointer) is
updated and only after a potentially sleeping call to uiomove is
sc->sc_count updated to the new value. In this instance a user with
write permissions to a parallel port printer can cause the data from
kernel heap UUMs and OOBs to be sent to said printer. I can go into the
specific interleaving at issue here, if anyone wants.
Steps to reproduce:
run NetBSD in a way that allows you to attach and monitor a parallel
port device, e.g. under qemu using
-chardev file,id=charptr0,path=log -device isa-parallel,chardev=charptr0
Then the script below (which simply makes concurrent writes of 'A'*n to
the same fd of the parallel port causes OOB heap memory to be printed to
the parallel port quite reliably for me where just 'A' would be
expected.
export N=20 TARGET="/dev/lpa0"
exec 3>"$TARGET"
for i in $(seq $N); do
tr '\000' 'A' < /dev/zero | dd bs=1024 >&3 2>/dev/null &
done
My prefered fix would be to only keep iterator context per-syscall
execution and not use the per-fd struct lpt_softc *sc there, but the
solution we settled on in OpenBSD was to simply initialize the buffer
and make sure that we only read from inside it. The output may still
skip over bytes or output zeros on concurrent writes to the same fd, but
wont impact the kernel.
I am attaching the diff that Vitaliy Makkoveev and I settled on for
reference.
I am a bit confused, why kernel ASan/MSan has not found this in
syzkaller yet. Maybe they have to few cpus?
Best Regards
Johann Höpfner
Index: sys/dev/ic/lpt.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/lpt.c,v
retrieving revision 1.17
diff -u -p -r1.17 lpt.c
--- sys/dev/ic/lpt.c 25 Jun 2025 20:28:09 -0000 1.17
+++ sys/dev/ic/lpt.c 8 Jul 2026 10:45:32 -0000
@@ -206,7 +206,7 @@ lptopen(dev_t dev, int flag, int mode, s
sc->sc_control = control;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, control);
- sc->sc_inbuf = malloc(LPT_BSIZE, M_DEVBUF, M_WAITOK);
+ sc->sc_inbuf = malloc(LPT_BSIZE, M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_count = 0;
sc->sc_state = LPT_OPEN;
@@ -309,6 +309,8 @@ lptpushbytes(struct lpt_softc *sc)
error = EIO;
if (error != EWOULDBLOCK)
return error;
+ if (sc->sc_count == 0)
+ return 0;
}
break;
}
@@ -360,10 +362,10 @@ lptwrite(dev_t dev, struct uio *uio, int
int error = 0;
while ((n = ulmin(LPT_BSIZE, uio->uio_resid)) != 0) {
- sc->sc_cp = sc->sc_inbuf;
- error = uiomove(sc->sc_cp, n, uio);
+ error = uiomove(sc->sc_inbuf, n, uio);
if (error != 0)
return error;
+ sc->sc_cp = sc->sc_inbuf;
sc->sc_count = n;
error = lptpushbytes(sc);
if (error) {
Home |
Main Index |
Thread Index |
Old Index