Be more wary about OpenSSL not setting errno on error.
OpenSSL will sometimes return SSL_ERROR_SYSCALL without having set errno; this is apparently a reflection of recv(2)'s habit of not setting errno when reporting EOF. Ensure that we treat such cases the same as read EOF. Previously, we'd frequently report them like "could not accept SSL connection: Success" which is confusing, or worse report them with an unrelated errno left over from some previous syscall. To fix, ensure that errno is zeroed immediately before the call, and report its value only when it's not zero afterwards; otherwise report EOF. For consistency, I've applied the same coding pattern in libpq's pqsecure_raw_read(). Bare recv(2) shouldn't really return -1 without setting errno, but in case it does we might as well cope. Per report from Andres Freund. Back-patch to all supported versions. Discussion: https://postgr.es/m/20231208181451.deqnflwxqoehhxpe@awork3.anarazel.de
This commit is contained in:
parent
d3fe6e90ba
commit
0a5c46a7a4
|
@ -460,6 +460,7 @@ aloop:
|
||||||
* per-thread error queue following another call to an OpenSSL I/O
|
* per-thread error queue following another call to an OpenSSL I/O
|
||||||
* routine.
|
* routine.
|
||||||
*/
|
*/
|
||||||
|
errno = 0;
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
r = SSL_accept(port->ssl);
|
r = SSL_accept(port->ssl);
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
|
@ -496,7 +497,7 @@ aloop:
|
||||||
WAIT_EVENT_SSL_OPEN_SERVER);
|
WAIT_EVENT_SSL_OPEN_SERVER);
|
||||||
goto aloop;
|
goto aloop;
|
||||||
case SSL_ERROR_SYSCALL:
|
case SSL_ERROR_SYSCALL:
|
||||||
if (r < 0)
|
if (r < 0 && errno != 0)
|
||||||
ereport(COMMERROR,
|
ereport(COMMERROR,
|
||||||
(errcode_for_socket_access(),
|
(errcode_for_socket_access(),
|
||||||
errmsg("could not accept SSL connection: %m")));
|
errmsg("could not accept SSL connection: %m")));
|
||||||
|
@ -732,7 +733,7 @@ be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
|
||||||
break;
|
break;
|
||||||
case SSL_ERROR_SYSCALL:
|
case SSL_ERROR_SYSCALL:
|
||||||
/* leave it to caller to ereport the value of errno */
|
/* leave it to caller to ereport the value of errno */
|
||||||
if (n != -1)
|
if (n != -1 || errno == 0)
|
||||||
{
|
{
|
||||||
errno = ECONNRESET;
|
errno = ECONNRESET;
|
||||||
n = -1;
|
n = -1;
|
||||||
|
@ -790,8 +791,14 @@ be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
|
||||||
n = -1;
|
n = -1;
|
||||||
break;
|
break;
|
||||||
case SSL_ERROR_SYSCALL:
|
case SSL_ERROR_SYSCALL:
|
||||||
/* leave it to caller to ereport the value of errno */
|
|
||||||
if (n != -1)
|
/*
|
||||||
|
* Leave it to caller to ereport the value of errno. However, if
|
||||||
|
* errno is still zero then assume it's a read EOF situation, and
|
||||||
|
* report ECONNRESET. (This seems possible because SSL_write can
|
||||||
|
* also do reads.)
|
||||||
|
*/
|
||||||
|
if (n != -1 || errno == 0)
|
||||||
{
|
{
|
||||||
errno = ECONNRESET;
|
errno = ECONNRESET;
|
||||||
n = -1;
|
n = -1;
|
||||||
|
|
|
@ -936,6 +936,8 @@ pq_recvbuf(void)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
r = secure_read(MyProcPort, PqRecvBuffer + PqRecvLength,
|
r = secure_read(MyProcPort, PqRecvBuffer + PqRecvLength,
|
||||||
PQ_RECV_BUFFER_SIZE - PqRecvLength);
|
PQ_RECV_BUFFER_SIZE - PqRecvLength);
|
||||||
|
|
||||||
|
@ -948,7 +950,10 @@ pq_recvbuf(void)
|
||||||
* Careful: an ereport() that tries to write to the client would
|
* Careful: an ereport() that tries to write to the client would
|
||||||
* cause recursion to here, leading to stack overflow and core
|
* cause recursion to here, leading to stack overflow and core
|
||||||
* dump! This message must go *only* to the postmaster log.
|
* dump! This message must go *only* to the postmaster log.
|
||||||
|
*
|
||||||
|
* If errno is zero, assume it's EOF and let the caller complain.
|
||||||
*/
|
*/
|
||||||
|
if (errno != 0)
|
||||||
ereport(COMMERROR,
|
ereport(COMMERROR,
|
||||||
(errcode_for_socket_access(),
|
(errcode_for_socket_access(),
|
||||||
errmsg("could not receive data from client: %m")));
|
errmsg("could not receive data from client: %m")));
|
||||||
|
@ -1028,6 +1033,8 @@ pq_getbyte_if_available(unsigned char *c)
|
||||||
/* Put the socket into non-blocking mode */
|
/* Put the socket into non-blocking mode */
|
||||||
socket_set_nonblocking(true);
|
socket_set_nonblocking(true);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
r = secure_read(MyProcPort, c, 1);
|
r = secure_read(MyProcPort, c, 1);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
{
|
{
|
||||||
|
@ -1044,7 +1051,10 @@ pq_getbyte_if_available(unsigned char *c)
|
||||||
* Careful: an ereport() that tries to write to the client would
|
* Careful: an ereport() that tries to write to the client would
|
||||||
* cause recursion to here, leading to stack overflow and core
|
* cause recursion to here, leading to stack overflow and core
|
||||||
* dump! This message must go *only* to the postmaster log.
|
* dump! This message must go *only* to the postmaster log.
|
||||||
|
*
|
||||||
|
* If errno is zero, assume it's EOF and let the caller complain.
|
||||||
*/
|
*/
|
||||||
|
if (errno != 0)
|
||||||
ereport(COMMERROR,
|
ereport(COMMERROR,
|
||||||
(errcode_for_socket_access(),
|
(errcode_for_socket_access(),
|
||||||
errmsg("could not receive data from client: %m")));
|
errmsg("could not receive data from client: %m")));
|
||||||
|
|
|
@ -200,7 +200,7 @@ rloop:
|
||||||
*/
|
*/
|
||||||
goto rloop;
|
goto rloop;
|
||||||
case SSL_ERROR_SYSCALL:
|
case SSL_ERROR_SYSCALL:
|
||||||
if (n < 0)
|
if (n < 0 && SOCK_ERRNO != 0)
|
||||||
{
|
{
|
||||||
result_errno = SOCK_ERRNO;
|
result_errno = SOCK_ERRNO;
|
||||||
if (result_errno == EPIPE ||
|
if (result_errno == EPIPE ||
|
||||||
|
@ -301,7 +301,13 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
|
||||||
n = 0;
|
n = 0;
|
||||||
break;
|
break;
|
||||||
case SSL_ERROR_SYSCALL:
|
case SSL_ERROR_SYSCALL:
|
||||||
if (n < 0)
|
|
||||||
|
/*
|
||||||
|
* If errno is still zero then assume it's a read EOF situation,
|
||||||
|
* and report EOF. (This seems possible because SSL_write can
|
||||||
|
* also do reads.)
|
||||||
|
*/
|
||||||
|
if (n < 0 && SOCK_ERRNO != 0)
|
||||||
{
|
{
|
||||||
result_errno = SOCK_ERRNO;
|
result_errno = SOCK_ERRNO;
|
||||||
if (result_errno == EPIPE || result_errno == ECONNRESET)
|
if (result_errno == EPIPE || result_errno == ECONNRESET)
|
||||||
|
@ -1510,11 +1516,12 @@ open_client_SSL(PGconn *conn)
|
||||||
* was using the system CA pool. For other errors, log
|
* was using the system CA pool. For other errors, log
|
||||||
* them using the normal SYSCALL logging.
|
* them using the normal SYSCALL logging.
|
||||||
*/
|
*/
|
||||||
if (!save_errno && vcode == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY &&
|
if (save_errno == 0 &&
|
||||||
|
vcode == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY &&
|
||||||
strcmp(conn->sslrootcert, "system") == 0)
|
strcmp(conn->sslrootcert, "system") == 0)
|
||||||
libpq_append_conn_error(conn, "SSL error: certificate verify failed: %s",
|
libpq_append_conn_error(conn, "SSL error: certificate verify failed: %s",
|
||||||
X509_verify_cert_error_string(vcode));
|
X509_verify_cert_error_string(vcode));
|
||||||
else if (r == -1)
|
else if (r == -1 && save_errno != 0)
|
||||||
libpq_append_conn_error(conn, "SSL SYSCALL error: %s",
|
libpq_append_conn_error(conn, "SSL SYSCALL error: %s",
|
||||||
SOCK_STRERROR(save_errno, sebuf, sizeof(sebuf)));
|
SOCK_STRERROR(save_errno, sebuf, sizeof(sebuf)));
|
||||||
else
|
else
|
||||||
|
|
|
@ -211,6 +211,8 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
|
||||||
int result_errno = 0;
|
int result_errno = 0;
|
||||||
char sebuf[PG_STRERROR_R_BUFLEN];
|
char sebuf[PG_STRERROR_R_BUFLEN];
|
||||||
|
|
||||||
|
SOCK_ERRNO_SET(0);
|
||||||
|
|
||||||
n = recv(conn->sock, ptr, len, 0);
|
n = recv(conn->sock, ptr, len, 0);
|
||||||
|
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
|
@ -237,6 +239,11 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
|
||||||
"\tbefore or while processing the request.");
|
"\tbefore or while processing the request.");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
/* If errno didn't get set, treat it as regular EOF */
|
||||||
|
n = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
libpq_append_conn_error(conn, "could not receive data from server: %s",
|
libpq_append_conn_error(conn, "could not receive data from server: %s",
|
||||||
SOCK_STRERROR(result_errno,
|
SOCK_STRERROR(result_errno,
|
||||||
|
|
Loading…
Reference in New Issue