If sendmsg returns a short write, we end up going around the loop with
data_to_send being smaller. However if sendmsg then returns -EAGAIN
or -EINTR then we return an error. But we have "forgotten" that we
already sent some data.
This causes the caller to retry gnutls_record_send with the full
buffer (ie. with a buffer that has already been partially sent),
causing desynchronization.
Instead check if we sent some data in this case and return the number
of bytes sent.
Fixes: https://gitlab.com/gnutls/gnutls/-/issues/1470
Thanks: Dan Berrange for suggesting a fix
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
if (ret == -1) {
switch (errno) {
case EINTR:
- return GNUTLS_E_INTERRUPTED;
+ if (data_to_send < data_size) {
+ return data_size - data_to_send;
+ } else {
+ return GNUTLS_E_INTERRUPTED;
+ }
case EAGAIN:
- return GNUTLS_E_AGAIN;
+ if (data_to_send < data_size) {
+ return data_size - data_to_send;
+ } else {
+ return GNUTLS_E_AGAIN;
+ }
default:
return GNUTLS_E_PUSH_ERROR;
}