Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 27 additions & 13 deletions extmod/modtls_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,25 @@ static inline void store_active_context(mp_obj_ssl_context_t *ssl_context) {
#endif
}

static mp_uint_t ssl_socket_close_transport(mp_obj_ssl_socket_t *ssl_socket, int *error_code) {
mp_obj_t transport = ssl_socket->sock;

// Clear the active SSL context.
store_active_context(NULL);

// Already closed socket, do nothing.
if (transport == MP_OBJ_NULL) {
return 0;
}

// Detach the transport before cleanup so repeated closes are harmless.
ssl_socket->sock = MP_OBJ_NULL;

// Release TLS state before forwarding close to the owned transport.
mbedtls_ssl_free(&ssl_socket->ssl);
return mp_get_stream(transport)->ioctl(transport, MP_STREAM_CLOSE, 0, error_code);
}

static void ssl_check_async_handshake_failure(mp_obj_ssl_socket_t *sslsock, int *errcode) {
if (
#if MBEDTLS_VERSION_NUMBER >= 0x03000000
Expand Down Expand Up @@ -245,14 +264,16 @@ static void ssl_check_async_handshake_failure(mp_obj_ssl_socket_t *sslsock, int
// The length of the string written (not including the terminated nul byte),
// or a negative err code.
if (ret > 0) {
sslsock->sock = MP_OBJ_NULL;
mbedtls_ssl_free(&sslsock->ssl);
// Close the transport while preserving the certificate error.
int close_error_code = 0;
ssl_socket_close_transport(sslsock, &close_error_code);
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("%s"), xcbuf);
}
}

sslsock->sock = MP_OBJ_NULL;
mbedtls_ssl_free(&sslsock->ssl);
// Close the transport while preserving the TLS error.
int close_error_code = 0;
ssl_socket_close_transport(sslsock, &close_error_code);
mbedtls_raise_error(*errcode);
}
}
Expand Down Expand Up @@ -966,15 +987,8 @@ static mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, i
mp_obj_t sock = self->sock;

if (request == MP_STREAM_CLOSE) {
// Clear the SSL context.
store_active_context(NULL);

if (sock == MP_OBJ_NULL) {
// Already closed socket, do nothing.
return 0;
}
self->sock = MP_OBJ_NULL;
mbedtls_ssl_free(&self->ssl);
// Release TLS state and close the owned transport exactly once.
return ssl_socket_close_transport(self, errcode);
} else if (request == MP_STREAM_POLL) {
if (sock == MP_OBJ_NULL || self->last_error != 0) {
// Closed or error socket, return NVAL flag.
Expand Down
18 changes: 15 additions & 3 deletions tests/extmod/ssl_poll.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def __init__(self):

self.write_buffers = []
self.last_poll_arg = None
self.close_call_count = 0

def readinto(self, buf):
if self.block_reads or len(self._other.write_buffers) == 0:
Expand Down Expand Up @@ -103,6 +104,7 @@ def ioctl(self, request, arg):
return ret

elif request == _MP_STREAM_CLOSE:
self.close_call_count += 1
return 0

raise NotImplementedError()
Expand All @@ -126,9 +128,9 @@ def assert_poll(s, i, arg, expected_arg, expected_ret):
def assert_raises(cb, *args, **kwargs):
try:
cb(*args, **kwargs)
raise AssertionError("should have raised")
except Exception as exc:
pass
except Exception:
return
raise AssertionError("should have raised")


client_io, server_io = _Pipe.new_pair()
Expand Down Expand Up @@ -194,6 +196,11 @@ def assert_raises(cb, *args, **kwargs):
assert_poll(
client_sock, client_io, _MP_STREAM_POLL_RD, None, _MP_STREAM_POLL_NVAL
) # Did not go to the socket
assert client_io.close_call_count == 1

# Closing the TLS socket again does not close the transport twice.
client_sock.close()
assert client_io.close_call_count == 1


# Errors propagates to poll:
Expand All @@ -208,3 +215,8 @@ def assert_raises(cb, *args, **kwargs):
assert_poll(
client_sock, client_io, _MP_STREAM_POLL_RD, None, _MP_STREAM_POLL_NVAL
) # Did not go to the socket
assert client_io.close_call_count == 1

# Closing after a fatal handshake error does not close the transport twice.
client_sock.close()
assert client_io.close_call_count == 1
Loading