From 7969638740e6d9b44bd55ed8fbfb6ed29aa686b5 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 7 Jun 2024 14:37:29 -0500 Subject: [PATCH 1/6] SSLSocket: Add stream protocol including select. This assumes that the SSL layer is readable/writable exactly when the underlying socket is readable/writable. --- shared-bindings/ssl/SSLSocket.c | 56 +++++++++++++++++++++++++++++++-- shared-bindings/ssl/SSLSocket.h | 2 ++ shared-module/ssl/SSLSocket.c | 15 +++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/shared-bindings/ssl/SSLSocket.c b/shared-bindings/ssl/SSLSocket.c index e244f77c76a9..a791da4093f8 100644 --- a/shared-bindings/ssl/SSLSocket.c +++ b/shared-bindings/ssl/SSLSocket.c @@ -10,10 +10,11 @@ #include #include "shared/runtime/context_manager_helpers.h" -#include "py/objtuple.h" +#include "py/mperrno.h" #include "py/objlist.h" +#include "py/objtuple.h" #include "py/runtime.h" -#include "py/mperrno.h" +#include "py/stream.h" #include "shared/netutils/netutils.h" @@ -247,9 +248,58 @@ static const mp_rom_map_elem_t ssl_sslsocket_locals_dict_table[] = { static MP_DEFINE_CONST_DICT(ssl_sslsocket_locals_dict, ssl_sslsocket_locals_dict_table); +static mp_uint_t sslsocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errorcode) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t ret = common_hal_ssl_sslsocket_recv_into(self, buf, size); + if (ret < 0) { + *errorcode = -ret; + return MP_STREAM_ERROR; + } + return ret; +} + +static mp_uint_t sslsocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errorcode) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t ret = common_hal_ssl_sslsocket_send(self, buf, size); + if (ret < 0) { + *errorcode = -ret; + return MP_STREAM_ERROR; + } + return ret; +} + +static mp_uint_t sslsocket_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; + if (request == MP_STREAM_POLL) { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_STREAM_POLL_RD) && common_hal_ssl_sslsocket_readable(self) > 0) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR) && common_hal_ssl_sslsocket_writable(self)) { + ret |= MP_STREAM_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + + +static const mp_stream_p_t sslsocket_stream_p = { + .read = sslsocket_read, + .write = sslsocket_write, + .ioctl = sslsocket_ioctl, + .is_text = false, +}; + + MP_DEFINE_CONST_OBJ_TYPE( ssl_sslsocket_type, MP_QSTR_SSLSocket, MP_TYPE_FLAG_NONE, - locals_dict, &ssl_sslsocket_locals_dict + locals_dict, &ssl_sslsocket_locals_dict, + protocol, &sslsocket_stream_p ); diff --git a/shared-bindings/ssl/SSLSocket.h b/shared-bindings/ssl/SSLSocket.h index 4079048a6cb1..bb46576330f5 100644 --- a/shared-bindings/ssl/SSLSocket.h +++ b/shared-bindings/ssl/SSLSocket.h @@ -20,6 +20,8 @@ void common_hal_ssl_sslsocket_close(ssl_sslsocket_obj_t *self); void common_hal_ssl_sslsocket_connect(ssl_sslsocket_obj_t *self, mp_obj_t addr); bool common_hal_ssl_sslsocket_get_closed(ssl_sslsocket_obj_t *self); bool common_hal_ssl_sslsocket_get_connected(ssl_sslsocket_obj_t *self); +bool common_hal_ssl_sslsocket_readable(ssl_sslsocket_obj_t *self); +bool common_hal_ssl_sslsocket_writable(ssl_sslsocket_obj_t *self); void common_hal_ssl_sslsocket_listen(ssl_sslsocket_obj_t *self, int backlog); mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t *buf, uint32_t len); mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t *self, const uint8_t *buf, uint32_t len); diff --git a/shared-module/ssl/SSLSocket.c b/shared-module/ssl/SSLSocket.c index 9303964af719..3b0f437086a6 100644 --- a/shared-module/ssl/SSLSocket.c +++ b/shared-module/ssl/SSLSocket.c @@ -448,3 +448,18 @@ void common_hal_ssl_sslsocket_setsockopt(ssl_sslsocket_obj_t *self, mp_obj_t lev void common_hal_ssl_sslsocket_settimeout(ssl_sslsocket_obj_t *self, mp_obj_t timeout_obj) { ssl_socket_settimeout(self, timeout_obj); } + +static bool poll_common(ssl_sslsocket_obj_t *self, int mode) { + const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock_obj, MP_STREAM_OP_IOCTL); + int errcode; + mp_int_t ret = stream_p->ioctl(self->sock_obj, MP_STREAM_POLL, mode, &errcode); + return ret != 0; +} + +bool common_hal_ssl_sslsocket_readable(ssl_sslsocket_obj_t *self) { + return poll_common(self, MP_STREAM_POLL_RD); +} + +bool common_hal_ssl_sslsocket_writable(ssl_sslsocket_obj_t *self) { + return poll_common(self, MP_STREAM_POLL_WR); +} From 49a612056e3b6cff2526dd13fed05af3b5a62867 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 10 Jun 2024 12:07:48 -0500 Subject: [PATCH 2/6] take micropython tricks for selectability of ssl sockets --- shared-module/ssl/SSLSocket.c | 34 ++++++++++++++++++++++++++++++++-- shared-module/ssl/SSLSocket.h | 1 + 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/shared-module/ssl/SSLSocket.c b/shared-module/ssl/SSLSocket.c index 3b0f437086a6..9e3a62586006 100644 --- a/shared-module/ssl/SSLSocket.c +++ b/shared-module/ssl/SSLSocket.c @@ -22,6 +22,8 @@ #include "mbedtls/version.h" +#define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR) + #if defined(MBEDTLS_ERROR_C) #include "../../lib/mbedtls_errors/mp_mbedtls_errors.c" #endif @@ -220,6 +222,7 @@ ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t o->base.type = &ssl_sslsocket_type; o->ssl_context = self; o->sock_obj = socket; + o->poll_mask = 0; mp_load_method(socket, MP_QSTR_accept, o->accept_args); mp_load_method(socket, MP_QSTR_bind, o->bind_args); @@ -331,6 +334,7 @@ ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t } mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t *buf, uint32_t len) { + self->poll_mask = 0; int ret = mbedtls_ssl_read(&self->ssl, buf, len); DEBUG_PRINT("recv_into mbedtls_ssl_read() -> %d\n", ret); if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { @@ -342,17 +346,24 @@ mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t DEBUG_PRINT("returning %d\n", ret); return ret; } + if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + self->poll_mask = MP_STREAM_POLL_WR; + } DEBUG_PRINT("raising errno [error case] %d\n", ret); mbedtls_raise_error(ret); } mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t *self, const uint8_t *buf, uint32_t len) { + self->poll_mask = 0; int ret = mbedtls_ssl_write(&self->ssl, buf, len); DEBUG_PRINT("send mbedtls_ssl_write() -> %d\n", ret); if (ret >= 0) { DEBUG_PRINT("returning %d\n", ret); return ret; } + if (ret == MBEDTLS_ERR_SSL_WANT_READ) { + self->poll_mask = MP_STREAM_POLL_RD; + } DEBUG_PRINT("raising errno [error case] %d\n", ret); mbedtls_raise_error(ret); } @@ -449,10 +460,29 @@ void common_hal_ssl_sslsocket_settimeout(ssl_sslsocket_obj_t *self, mp_obj_t tim ssl_socket_settimeout(self, timeout_obj); } -static bool poll_common(ssl_sslsocket_obj_t *self, int mode) { +static bool poll_common(ssl_sslsocket_obj_t *self, uintptr_t arg) { + // Take into account that the library might have buffered data already + int has_pending = 0; + if (arg & MP_STREAM_POLL_RD) { + has_pending = mbedtls_ssl_check_pending(&self->ssl); + if (has_pending) { + // Shortcut if we only need to read and we have buffered data, no need to go to the underlying socket + return true; + } + } + + // If the library signaled us that it needs reading or writing, only + // check that direction + if (self->poll_mask && (arg & MP_STREAM_POLL_RDWR)) { + arg = (arg & ~MP_STREAM_POLL_RDWR) | self->poll_mask; + } + + // If direction the library needed is available, return a fake + // result to the caller so that it reenters a read or a write to + // allow the handshake to progress const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock_obj, MP_STREAM_OP_IOCTL); int errcode; - mp_int_t ret = stream_p->ioctl(self->sock_obj, MP_STREAM_POLL, mode, &errcode); + mp_int_t ret = stream_p->ioctl(self->sock_obj, MP_STREAM_POLL, arg, &errcode); return ret != 0; } diff --git a/shared-module/ssl/SSLSocket.h b/shared-module/ssl/SSLSocket.h index b9baad3ce174..f7f3d1ae83ce 100644 --- a/shared-module/ssl/SSLSocket.h +++ b/shared-module/ssl/SSLSocket.h @@ -29,6 +29,7 @@ typedef struct ssl_sslsocket_obj { mbedtls_x509_crt cacert; mbedtls_x509_crt cert; mbedtls_pk_context pkey; + uintptr_t poll_mask; bool closed; mp_obj_t accept_args[2]; mp_obj_t bind_args[3]; From 3215f6c4ffbba6acfa40b03d353fccb2f6381692 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 12 Jun 2024 16:41:19 -0500 Subject: [PATCH 3/6] SSLSocket: handle exceptions during protocol read/write operations These protocol operations should not raise exceptions, but sometimes they do. Catch the exception and extract the errno value if available. At the same time, harmonize the argument types for the underlying C routines --- shared-bindings/ssl/SSLSocket.c | 29 ++++++++++++++++++++--------- shared-bindings/ssl/SSLSocket.h | 4 ++-- shared-module/ssl/SSLSocket.c | 4 ++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/shared-bindings/ssl/SSLSocket.c b/shared-bindings/ssl/SSLSocket.c index a791da4093f8..1d2432c0e13f 100644 --- a/shared-bindings/ssl/SSLSocket.c +++ b/shared-bindings/ssl/SSLSocket.c @@ -248,9 +248,22 @@ static const mp_rom_map_elem_t ssl_sslsocket_locals_dict_table[] = { static MP_DEFINE_CONST_DICT(ssl_sslsocket_locals_dict, ssl_sslsocket_locals_dict_table); -static mp_uint_t sslsocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errorcode) { +typedef mp_uint_t (*readwrite_func)(ssl_sslsocket_obj_t *, const uint8_t *, mp_uint_t); + +static mp_int_t readwrite_common(mp_obj_t self_in, readwrite_func fn, const uint8_t *buf, size_t size, int *errorcode) { ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_int_t ret = common_hal_ssl_sslsocket_recv_into(self, buf, size); + mp_int_t ret; + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + ret = fn(self, buf, size); + nlr_pop(); + } else { + mp_obj_t exc = MP_OBJ_FROM_PTR(nlr.ret_val); + if (nlr_push(&nlr) == 0) { + ret = -mp_obj_get_int(mp_load_attr(exc, MP_QSTR_errno)); + nlr_pop(); + } + } if (ret < 0) { *errorcode = -ret; return MP_STREAM_ERROR; @@ -258,14 +271,12 @@ static mp_uint_t sslsocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int return ret; } +static mp_uint_t sslsocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errorcode) { + return readwrite_common(self_in, (readwrite_func)common_hal_ssl_sslsocket_recv_into, buf, size, errorcode); +} + static mp_uint_t sslsocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errorcode) { - ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_int_t ret = common_hal_ssl_sslsocket_send(self, buf, size); - if (ret < 0) { - *errorcode = -ret; - return MP_STREAM_ERROR; - } - return ret; + return readwrite_common(self_in, common_hal_ssl_sslsocket_send, buf, size, errorcode); } static mp_uint_t sslsocket_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { diff --git a/shared-bindings/ssl/SSLSocket.h b/shared-bindings/ssl/SSLSocket.h index bb46576330f5..85467fee089a 100644 --- a/shared-bindings/ssl/SSLSocket.h +++ b/shared-bindings/ssl/SSLSocket.h @@ -23,7 +23,7 @@ bool common_hal_ssl_sslsocket_get_connected(ssl_sslsocket_obj_t *self); bool common_hal_ssl_sslsocket_readable(ssl_sslsocket_obj_t *self); bool common_hal_ssl_sslsocket_writable(ssl_sslsocket_obj_t *self); void common_hal_ssl_sslsocket_listen(ssl_sslsocket_obj_t *self, int backlog); -mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t *buf, uint32_t len); -mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t *self, const uint8_t *buf, uint32_t len); +mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t *buf, mp_uint_t len); +mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t *self, const uint8_t *buf, mp_uint_t len); void common_hal_ssl_sslsocket_settimeout(ssl_sslsocket_obj_t *self, mp_obj_t timeout_obj); void common_hal_ssl_sslsocket_setsockopt(ssl_sslsocket_obj_t *self, mp_obj_t level, mp_obj_t optname, mp_obj_t optval); diff --git a/shared-module/ssl/SSLSocket.c b/shared-module/ssl/SSLSocket.c index 9e3a62586006..0b40bd663c7d 100644 --- a/shared-module/ssl/SSLSocket.c +++ b/shared-module/ssl/SSLSocket.c @@ -333,7 +333,7 @@ ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t } } -mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t *buf, uint32_t len) { +mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t *buf, mp_uint_t len) { self->poll_mask = 0; int ret = mbedtls_ssl_read(&self->ssl, buf, len); DEBUG_PRINT("recv_into mbedtls_ssl_read() -> %d\n", ret); @@ -353,7 +353,7 @@ mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t mbedtls_raise_error(ret); } -mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t *self, const uint8_t *buf, uint32_t len) { +mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t *self, const uint8_t *buf, mp_uint_t len) { self->poll_mask = 0; int ret = mbedtls_ssl_write(&self->ssl, buf, len); DEBUG_PRINT("send mbedtls_ssl_write() -> %d\n", ret); From 4d4d65467793dcfadc42e45c927736f45c60c413 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 13 Jun 2024 11:11:17 -0500 Subject: [PATCH 4/6] MP3Decoder: set the nonblocking flag as needed this makes SSL sockets (which return readable when not yet actually readable) work better. --- shared-bindings/audiomp3/MP3Decoder.c | 6 ++++++ shared-module/audiomp3/MP3Decoder.c | 22 +++++++++++++++++++++- shared-module/audiomp3/MP3Decoder.h | 2 ++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/shared-bindings/audiomp3/MP3Decoder.c b/shared-bindings/audiomp3/MP3Decoder.c index 7a596f90dbc5..0ddb9856da42 100644 --- a/shared-bindings/audiomp3/MP3Decoder.c +++ b/shared-bindings/audiomp3/MP3Decoder.c @@ -72,6 +72,12 @@ //| decoder.file = stream //| //| If the stream is played with ``loop = True``, the loop will start at the beginning. +//| +//| It is possible to stream an mp3 from a socket, including a secure socket. +//| The MP3Decoder may change the timeout and non-blocking status of the socket. +//| Using a larger decode buffer with a stream can be helpful to avoid data underruns. +//| An `adafruit_requests` request must be made with ``headers={"Connection": "close"}`` so +//| that the socket closes when the stream ends. //| """ //| ... diff --git a/shared-module/audiomp3/MP3Decoder.c b/shared-module/audiomp3/MP3Decoder.c index 19867271f605..f59f4a4510b6 100644 --- a/shared-module/audiomp3/MP3Decoder.c +++ b/shared-module/audiomp3/MP3Decoder.c @@ -95,6 +95,18 @@ static off_t stream_lseek(void *stream, off_t offset, int whence) { #define INPUT_BUFFER_CONSUME(i, n) ((i).read_off += (n)) #define INPUT_BUFFER_CLEAR(i) ((i).read_off = (i).write_off = 0) +static void stream_set_blocking(audiomp3_mp3file_obj_t *self, bool block_ok) { + if (!self->settimeout_args[0]) { + return; + } + if (block_ok == self->block_ok) { + return; + } + self->block_ok = block_ok; + self->settimeout_args[2] = block_ok ? mp_const_none : mp_obj_new_int(0); + mp_call_method_n_kw(1, 0, self->settimeout_args); +} + /** Fill the input buffer unconditionally. * * Returns true if the input buffer contains any useful data, @@ -110,6 +122,8 @@ static bool mp3file_update_inbuf_always(audiomp3_mp3file_obj_t *self, bool block return INPUT_BUFFER_AVAILABLE(self->inbuf) > 0; } + stream_set_blocking(self, block_ok); + // We didn't previously reach EOF and we have input buffer space available // Move the unconsumed portion of the buffer to the start @@ -119,7 +133,7 @@ static bool mp3file_update_inbuf_always(audiomp3_mp3file_obj_t *self, bool block self->inbuf.read_off = 0; } - for (size_t to_read; !self->eof && (to_read = INPUT_BUFFER_SPACE(self->inbuf)) > 0 && (block_ok || stream_readable(self->stream));) { + for (size_t to_read; !self->eof && (to_read = INPUT_BUFFER_SPACE(self->inbuf)) > 0;) { uint8_t *write_ptr = self->inbuf.buf + self->inbuf.write_off; ssize_t n_read = stream_read(self->stream, write_ptr, to_read); @@ -328,9 +342,14 @@ void common_hal_audiomp3_mp3file_set_file(audiomp3_mp3file_obj_t *self, mp_obj_t background_callback_prevent(); self->stream = stream; + mp_load_method_maybe(stream, MP_QSTR_settimeout, self->settimeout_args); INPUT_BUFFER_CLEAR(self->inbuf); self->eof = 0; + + self->block_ok = false; + stream_set_blocking(self, true); + self->other_channel = -1; mp3file_update_inbuf_half(self, true); mp3file_find_sync_word(self, true); @@ -365,6 +384,7 @@ void common_hal_audiomp3_mp3file_deinit(audiomp3_mp3file_obj_t *self) { self->pcm_buffer[0] = NULL; self->pcm_buffer[1] = NULL; self->stream = mp_const_none; + self->settimeout_args[0] = MP_OBJ_NULL; self->samples_decoded = 0; } diff --git a/shared-module/audiomp3/MP3Decoder.h b/shared-module/audiomp3/MP3Decoder.h index 89328deb4a97..9f1b97a5a516 100644 --- a/shared-module/audiomp3/MP3Decoder.h +++ b/shared-module/audiomp3/MP3Decoder.h @@ -35,6 +35,8 @@ typedef struct { uint8_t buffer_index; uint8_t channel_count; bool eof; + bool block_ok; + mp_obj_t settimeout_args[3]; int8_t other_channel; int8_t other_buffer_index; From e00e2473db471bf8cdd69178c87375839fc10b47 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 13 Jun 2024 11:39:40 -0500 Subject: [PATCH 5/6] doc fix --- shared-bindings/audiomp3/MP3Decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/audiomp3/MP3Decoder.c b/shared-bindings/audiomp3/MP3Decoder.c index 0ddb9856da42..ff715720d028 100644 --- a/shared-bindings/audiomp3/MP3Decoder.c +++ b/shared-bindings/audiomp3/MP3Decoder.c @@ -76,7 +76,7 @@ //| It is possible to stream an mp3 from a socket, including a secure socket. //| The MP3Decoder may change the timeout and non-blocking status of the socket. //| Using a larger decode buffer with a stream can be helpful to avoid data underruns. -//| An `adafruit_requests` request must be made with ``headers={"Connection": "close"}`` so +//| An ``adafruit_requests`` request must be made with ``headers={"Connection": "close"}`` so //| that the socket closes when the stream ends. //| """ //| ... From 7c85f6a15af330770d7d7ccf2700d5eb9f83c222 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 13 Jun 2024 11:41:24 -0500 Subject: [PATCH 6/6] ensure variable is initialized --- shared-bindings/ssl/SSLSocket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/ssl/SSLSocket.c b/shared-bindings/ssl/SSLSocket.c index 1d2432c0e13f..3ed4fa366243 100644 --- a/shared-bindings/ssl/SSLSocket.c +++ b/shared-bindings/ssl/SSLSocket.c @@ -252,7 +252,7 @@ typedef mp_uint_t (*readwrite_func)(ssl_sslsocket_obj_t *, const uint8_t *, mp_u static mp_int_t readwrite_common(mp_obj_t self_in, readwrite_func fn, const uint8_t *buf, size_t size, int *errorcode) { ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_int_t ret; + mp_int_t ret = -EIO; nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { ret = fn(self, buf, size);