INVARIANTS kernels may trigger a KASSERT panic from sndbuf_acquire(), when
fuzzing write(2) using stress2, because of a race in chn_write().
In the case of chn_write(), what sndbuf_acquire() does is extend the
ready-to-read area of the buffer by a specified amount of bytes. The KASSERT in
question makes sure the number of bytes we want to extend the ready area by, is
less than or equal to the number of free bytes in the buffer. This makes sense,
because we cannot extend the ready area to something larger than what is
available (i.e., free) in the first place.
What chn_write() currently does for every write is; calculate the appropriate
write size, let's say X, unlock the channel, uiomove() X bytes to the channel's
buffer, lock the channel, and call sndbuf_acquire() to extend the ready area by
X bytes. The problem with this approach, however, is the following. Suppose an
empty channel buffer with a length of 1024 bytes, and 2 threads, (A) and (B),
where (B) is a higher-priority one. Suppose thread (A) wants to write 1024
bytes. It unlocks the channel and uiomove()s 1024 bytes to the channel buffer.
At the same time, thread (B) picks up the lock, and because it is higher
priority, it keeps dominating the lock for a few iterations. By the time thread
(A) picks up the lock again, it tries to call sndbuf_acquire() with an
size of 1024 bytes, which was calculated before it performed the uiomove(). In
this case, there is a very high chance that the buffer will not be empty, that
is, have a free area of 1024 bytes, as was the case when thread (A) started
executing, and so the KASSERT will trigger a panic because the condition (bytes
<= free) is not met.
To fix this, call sndbuf_acquire() and update the remaining total write size
before unlocking the channel. Do the same for chn_read() to avoid similar
panics from sndbuf_dispose().
Reported by: pho
Tested by: christos, pho
Sponsored by: The FreeBSD Foundation
MFC after: 1 week