sosend_generic() performs an initial comparison of the amount of data
(including control messages) to be transmitted with the send buffer
size. When sending data with a control message, we then compare the
amount of data being sent with the amount of space in the receive buffer
size; if insufficient space is available, the data is lost. There are
at least two problems with this:
- The size of control messages will typically change (increase) between these two checks. If the send and rcv socket buffer limits are equal, this means that the second check can fail even if the first one succeeds.
- If the receive buffer is smaller than the send buffer, sending a message of size larger than the former and smaller than the latter will cause data loss. (This would seem to be an odd configuration, though.)
The first problem is easy to hit. Address it by removing the space
check in sbappendcontrol_locked() (note that sbappend_locked() omits the
check too).
We rely on the SB_STOP-based backpressure mechanism to ensure that
senders are blocked once the rcv buffer is full. With this change, it
becomes possible for the rcv buffer to contain data above its limit.
This doesn't violate any invariants in the socket layer, though, and
there is a bound on how much the limit can be exceeded. Given the
severity of the bug being fixed, I think this is a reasonable
compromise.
Note that there are still some bugs in this area; in particular, mbuf
allocation failures can cause data loss in some cases.