SecureSocket: Handle interrupted reads and writes

If a system call performed by SSL_read is interrupted by a signal, it
seems to set its error to SSL_ERROR_WANT_READ. This triggers logic
added in hrev53853 which assumes the caller is doing async reads and
returns B_WOULD_BLOCK.

This breaks uses of BSecureSocket that do blocking reads.

* Detect interrupted signal by checking for EINTR in errno.
* Adding this retry loop to BScureSocket::Write as well since it can
  have the same problem.

Resolves issue #15853.

Change-Id: I8198a8496fa3a2ccee00bda87375a482a0d4ba3d
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2825
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
This commit is contained in:
Kyle Ambroff-Kao 2020-05-27 20:40:45 -07:00 committed by waddlesplash
parent 57672294e9
commit 38963e7596

View File

@ -549,14 +549,21 @@ BSecureSocket::Read(void* buffer, size_t size)
bytesRead = SSL_read(fPrivate->fSSL, buffer, size);
if (bytesRead >= 0)
return bytesRead;
// Don't retry in cases of "no data available" for non-blocking sockets
int error = SSL_get_error(fPrivate->fSSL, bytesRead);
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)
return B_WOULD_BLOCK;
// Otherwise, check if we should retry (maybe we were interrupted by
// a signal, for example)
if (errno != EINTR) {
// Don't retry in cases of "no data available" for non-blocking
// sockets.
int error = SSL_get_error(fPrivate->fSSL, bytesRead);
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)
return B_WOULD_BLOCK;
}
// See if the error was retryable. We may have been interrupted by
// a signal, in which case we will retry. But it is also possible that
// another error has occurred which is not retryable. openssl will
// decide for us here.
retry = BIO_should_retry(SSL_get_rbio(fPrivate->fSSL));
} while(retry != 0);
} while (retry != 0);
return fPrivate->ErrorCode(bytesRead);
}
@ -568,9 +575,27 @@ BSecureSocket::Write(const void* buffer, size_t size)
if (!IsConnected())
return B_ERROR;
int bytesWritten = SSL_write(fPrivate->fSSL, buffer, size);
if (bytesWritten >= 0)
return bytesWritten;
int bytesWritten;
int retry;
do {
bytesWritten = SSL_write(fPrivate->fSSL, buffer, size);
if (bytesWritten >= 0)
return bytesWritten;
if (errno != EINTR) {
// Don't retry in cases of "no buffer space available" for
// non-blocking sockets.
int error = SSL_get_error(fPrivate->fSSL, bytesWritten);
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)
return B_WOULD_BLOCK;
}
// See if the error was retryable. We may have been interrupted by
// a signal, in which case we will retry. But it is also possible that
// another error has occurred which is not retryable. openssl will
// decide for us here.
retry = BIO_should_retry(SSL_get_wbio(fPrivate->fSSL));
} while (retry != 0);
return fPrivate->ErrorCode(bytesWritten);
}