summaryrefslogtreecommitdiffstatshomepage
path: root/tests/extmod/ssl_threads.py
diff options
context:
space:
mode:
authorAngus Gratton <angus@redyak.com.au>2024-10-24 13:28:37 +1100
committerDamien George <damien@micropython.org>2025-05-02 17:24:16 +1000
commit70ed3151933635429a66937bae2701958b6b47dd (patch)
treee343ac6e846e509dc7e4157a001136e78f55448e /tests/extmod/ssl_threads.py
parentbee1fd5e7887d48ad2b217f5c4746f6b518f3fd8 (diff)
downloadmicropython-70ed3151933635429a66937bae2701958b6b47dd.tar.gz
micropython-70ed3151933635429a66937bae2701958b6b47dd.zip
py/malloc: Add mutex for tracked allocations.
Fixes thread safety issue that could cause memory corruption on ports with (MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL) - currently only rp2 and unix have this configuration. Adds unit test for TLS sockets that exercises this code path. I wasn't able to make this fail on rp2, the race condition window is pretty narrow and may not have a direct impact on a quiet system. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
Diffstat (limited to 'tests/extmod/ssl_threads.py')
-rw-r--r--tests/extmod/ssl_threads.py57
1 files changed, 57 insertions, 0 deletions
diff --git a/tests/extmod/ssl_threads.py b/tests/extmod/ssl_threads.py
new file mode 100644
index 0000000000..4564abd3d8
--- /dev/null
+++ b/tests/extmod/ssl_threads.py
@@ -0,0 +1,57 @@
+# Ensure that SSL sockets can be allocated from multiple
+# threads without thread safety issues
+import unittest
+
+try:
+ import _thread
+ import io
+ import tls
+ import time
+except ImportError:
+ print("SKIP")
+ raise SystemExit
+
+
+class TestSocket(io.IOBase):
+ def write(self, buf):
+ return len(buf)
+
+ def readinto(self, buf):
+ return 0
+
+ def ioctl(self, cmd, arg):
+ return 0
+
+ def setblocking(self, value):
+ pass
+
+
+ITERS = 256
+
+
+class TLSThreads(unittest.TestCase):
+ def test_sslsocket_threaded(self):
+ self.done = False
+ # only run in two threads: too much RAM demand otherwise, and rp2 only
+ # supports two anyhow
+ _thread.start_new_thread(self._alloc_many_sockets, (True,))
+ self._alloc_many_sockets(False)
+ while not self.done:
+ time.sleep(0.1)
+ print("done")
+
+ def _alloc_many_sockets(self, set_done_flag):
+ print("start", _thread.get_ident())
+ ctx = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT)
+ ctx.verify_mode = tls.CERT_NONE
+ for n in range(ITERS):
+ s = TestSocket()
+ s = ctx.wrap_socket(s, do_handshake_on_connect=False)
+ s.close() # Free associated resources now from thread, not in a GC pass
+ print("done", _thread.get_ident())
+ if set_done_flag:
+ self.done = True
+
+
+if __name__ == "__main__":
+ unittest.main()