Skip to content

Commit a147d0b

Browse files
committed
ssl: fix errno display in exception messages
The errno reported in OpenSSL::SSL::SSLError message sometimes does not match what SSL_accept()/SSL_connect() actually encountered. Depending on the evaluation order of function arguments to ossl_raise(), it can be overwritten by peeraddr_ip_str(). Save errno into a local variable immediately after the OpenSSL function returns, and avoid touching the thread-local errno afterward. Also, expand rb_sys_fail() and rb_io_maybe_wait_{readable,writable}() so that they do not rely on errno, to simplify.
1 parent 4db2605 commit a147d0b

File tree

1 file changed

+27
-18
lines changed

1 file changed

+27
-18
lines changed

ext/openssl/ossl_ssl.c

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,11 +1696,15 @@ ossl_ssl_setup(VALUE self)
16961696
return Qtrue;
16971697
}
16981698

1699+
static int
1700+
errno_mapped(void)
1701+
{
16991702
#ifdef _WIN32
1700-
#define ssl_get_error(ssl, ret) (errno = rb_w32_map_errno(WSAGetLastError()), SSL_get_error((ssl), (ret)))
1703+
return rb_w32_map_errno(WSAGetLastError());
17011704
#else
1702-
#define ssl_get_error(ssl, ret) SSL_get_error((ssl), (ret))
1705+
return errno;
17031706
#endif
1707+
}
17041708

17051709
static void
17061710
write_would_block(int nonblock)
@@ -1741,7 +1745,7 @@ static void
17411745
io_wait_writable(VALUE io)
17421746
{
17431747
#ifdef HAVE_RB_IO_MAYBE_WAIT
1744-
if (!rb_io_maybe_wait_writable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) {
1748+
if (!rb_io_wait(io, INT2NUM(RUBY_IO_WRITABLE), RUBY_IO_TIMEOUT_DEFAULT)) {
17451749
rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become writable!");
17461750
}
17471751
#else
@@ -1755,7 +1759,7 @@ static void
17551759
io_wait_readable(VALUE io)
17561760
{
17571761
#ifdef HAVE_RB_IO_MAYBE_WAIT
1758-
if (!rb_io_maybe_wait_readable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) {
1762+
if (!rb_io_wait(io, INT2NUM(RUBY_IO_READABLE), RUBY_IO_TIMEOUT_DEFAULT)) {
17591763
rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become readable!");
17601764
}
17611765
#else
@@ -1769,7 +1773,6 @@ static VALUE
17691773
ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
17701774
{
17711775
SSL *ssl;
1772-
int ret, ret2;
17731776
VALUE cb_state;
17741777
int nonblock = opts != Qfalse;
17751778

@@ -1779,7 +1782,8 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
17791782

17801783
VALUE io = rb_attr_get(self, id_i_io);
17811784
for (;;) {
1782-
ret = func(ssl);
1785+
int ret = func(ssl);
1786+
int saved_errno = errno_mapped();
17831787

17841788
cb_state = rb_attr_get(self, ID_callback_state);
17851789
if (!NIL_P(cb_state)) {
@@ -1791,7 +1795,8 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
17911795
if (ret > 0)
17921796
break;
17931797

1794-
switch ((ret2 = ssl_get_error(ssl, ret))) {
1798+
int code = SSL_get_error(ssl, ret);
1799+
switch (code) {
17951800
case SSL_ERROR_WANT_WRITE:
17961801
if (no_exception_p(opts)) { return sym_wait_writable; }
17971802
write_would_block(nonblock);
@@ -1805,10 +1810,11 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
18051810
case SSL_ERROR_SYSCALL:
18061811
#ifdef __APPLE__
18071812
/* See ossl_ssl_write_internal() */
1808-
if (errno == EPROTOTYPE)
1813+
if (saved_errno == EPROTOTYPE)
18091814
continue;
18101815
#endif
1811-
if (errno) rb_sys_fail(funcname);
1816+
if (saved_errno)
1817+
rb_exc_raise(rb_syserr_new(saved_errno, funcname));
18121818
/* fallthrough */
18131819
default: {
18141820
VALUE error_append = Qnil;
@@ -1829,9 +1835,9 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
18291835
ossl_raise(eSSLError,
18301836
"%s%s returned=%d errno=%d peeraddr=%"PRIsVALUE" state=%s%"PRIsVALUE,
18311837
funcname,
1832-
ret2 == SSL_ERROR_SYSCALL ? " SYSCALL" : "",
1833-
ret2,
1834-
errno,
1838+
code == SSL_ERROR_SYSCALL ? " SYSCALL" : "",
1839+
code,
1840+
saved_errno,
18351841
peeraddr_ip_str(self),
18361842
SSL_state_string_long(ssl),
18371843
error_append);
@@ -1974,6 +1980,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19741980
for (;;) {
19751981
rb_str_locktmp(str);
19761982
int nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
1983+
int saved_errno = errno_mapped();
19771984
rb_str_unlocktmp(str);
19781985

19791986
cb_state = rb_attr_get(self, ID_callback_state);
@@ -1983,7 +1990,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19831990
rb_jump_tag(NUM2INT(cb_state));
19841991
}
19851992

1986-
switch (ssl_get_error(ssl, nread)) {
1993+
switch (SSL_get_error(ssl, nread)) {
19871994
case SSL_ERROR_NONE:
19881995
rb_str_set_len(str, nread);
19891996
return str;
@@ -2006,8 +2013,8 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
20062013
break;
20072014
case SSL_ERROR_SYSCALL:
20082015
if (!ERR_peek_error()) {
2009-
if (errno)
2010-
rb_sys_fail(0);
2016+
if (saved_errno)
2017+
rb_exc_raise(rb_syserr_new(saved_errno, "SSL_read"));
20112018
else {
20122019
/*
20132020
* The underlying BIO returned 0. This is actually a
@@ -2092,6 +2099,7 @@ ossl_ssl_write_internal_safe(VALUE _args)
20922099

20932100
for (;;) {
20942101
int nwritten = SSL_write(ssl, RSTRING_PTR(str), num);
2102+
int saved_errno = errno_mapped();
20952103

20962104
cb_state = rb_attr_get(self, ID_callback_state);
20972105
if (!NIL_P(cb_state)) {
@@ -2100,7 +2108,7 @@ ossl_ssl_write_internal_safe(VALUE _args)
21002108
rb_jump_tag(NUM2INT(cb_state));
21012109
}
21022110

2103-
switch (ssl_get_error(ssl, nwritten)) {
2111+
switch (SSL_get_error(ssl, nwritten)) {
21042112
case SSL_ERROR_NONE:
21052113
return INT2NUM(nwritten);
21062114
case SSL_ERROR_WANT_WRITE:
@@ -2121,10 +2129,11 @@ ossl_ssl_write_internal_safe(VALUE _args)
21212129
* make the error handling in line with the socket library.
21222130
* [Bug #14713] https://bugs.ruby-lang.org/issues/14713
21232131
*/
2124-
if (errno == EPROTOTYPE)
2132+
if (saved_errno == EPROTOTYPE)
21252133
continue;
21262134
#endif
2127-
if (errno) rb_sys_fail(0);
2135+
if (saved_errno)
2136+
rb_exc_raise(rb_syserr_new(saved_errno, "SSL_write"));
21282137
/* fallthrough */
21292138
default:
21302139
ossl_raise(eSSLError, "SSL_write");

0 commit comments

Comments
 (0)