Skip to content

Commit 12f3646

Browse files
authored
ssl: store SSL socket owners as weak references (RustPython#8423)
Assisted-by: Codex:gpt-5.6-sol
1 parent 95f9d17 commit 12f3646

3 files changed

Lines changed: 31 additions & 11 deletions

File tree

Lib/test/test_asyncio/test_ssl.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,6 @@ async def client(addr):
738738
asyncio.wait_for(client(srv.addr),
739739
timeout=support.SHORT_TIMEOUT))
740740

741-
@unittest.expectedFailure # TODO: RUSTPYTHON; - gc.collect() doesn't release SSLContext properly
742741
def test_create_connection_memory_leak(self):
743742
HELLO_MSG = b'1' * self.PAYLOAD_SIZE
744743

@@ -1617,7 +1616,6 @@ async def test():
16171616
else:
16181617
self.fail('Unexpected ResourceWarning: {}'.format(cm.warning))
16191618

1620-
@unittest.expectedFailure # TODO: RUSTPYTHON; - gc.collect() doesn't release SSLContext properly
16211619
def test_handshake_timeout_handler_leak(self):
16221620
s = socket.socket(socket.AF_INET)
16231621
s.bind(('127.0.0.1', 0))

Lib/test/test_ssl.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1479,7 +1479,6 @@ def dummycallback(sock, servername, ctx):
14791479
ctx.set_servername_callback(None)
14801480
ctx.set_servername_callback(dummycallback)
14811481

1482-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: Expected 'mock' to not have been called. Called 1 times.
14831482
def test_sni_callback_on_dead_references(self):
14841483
# See https://github.com/python/cpython/issues/146080.
14851484
c_ctx = make_test_context()

crates/stdlib/src/ssl.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ mod _ssl {
4848
VirtualMachine,
4949
builtins::{
5050
PyBaseExceptionRef, PyByteArray, PyBytesRef, PyListRef, PyStrRef, PyType,
51-
PyTypeRef, PyUtf8StrRef,
51+
PyTypeRef, PyUtf8StrRef, PyWeak,
5252
},
5353
convert::IntoPyException,
5454
function::{
@@ -1920,7 +1920,12 @@ mod _ssl {
19201920
connection: PyMutex::new(None),
19211921
handshake_done: PyMutex::new(false),
19221922
session_was_reused: PyMutex::new(false),
1923-
owner: PyRwLock::new(args.owner.into_option()),
1923+
owner: PyRwLock::new(
1924+
args.owner
1925+
.into_option()
1926+
.map(|o| o.downgrade(None, vm))
1927+
.transpose()?,
1928+
),
19241929
// Filter out Python None objects - only store actual SSLSession objects
19251930
session: PyRwLock::new(args.session.into_option().filter(|s| !vm.is_none(s))),
19261931
incoming_bio: None,
@@ -1997,7 +2002,12 @@ mod _ssl {
19972002
connection: PyMutex::new(None),
19982003
handshake_done: PyMutex::new(false),
19992004
session_was_reused: PyMutex::new(false),
2000-
owner: PyRwLock::new(args.owner.into_option()),
2005+
owner: PyRwLock::new(
2006+
args.owner
2007+
.into_option()
2008+
.map(|o| o.downgrade(None, vm))
2009+
.transpose()?,
2010+
),
20012011
// Filter out Python None objects - only store actual SSLSession objects
20022012
session: PyRwLock::new(args.session.into_option().filter(|s| !vm.is_none(s))),
20032013
incoming_bio: Some(args.incoming),
@@ -2377,7 +2387,7 @@ mod _ssl {
23772387
#[pytraverse(skip)]
23782388
session_was_reused: PyMutex<bool>,
23792389
// Owner (SSLSocket instance that owns this _SSLSocket)
2380-
owner: PyRwLock<Option<PyObjectRef>>,
2390+
owner: PyRwLock<Option<PyRef<PyWeak>>>,
23812391
// Session for resumption
23822392
session: PyRwLock<Option<PyObjectRef>>,
23832393
// MemoryBIO mode (optional)
@@ -2734,7 +2744,19 @@ mod _ssl {
27342744
return Ok(());
27352745
};
27362746

2737-
let ssl_sock = self.owner.read().clone().unwrap_or_else(|| vm.ctx.none());
2747+
let ssl_sock = self
2748+
.owner
2749+
.read()
2750+
.as_ref()
2751+
.and_then(|owner| owner.upgrade())
2752+
.ok_or_else(|| {
2753+
super::compat::SslError::create_ssl_error_with_reason(
2754+
vm,
2755+
Some("SSL"),
2756+
"CALLBACK_FAILED",
2757+
"[SSL: CALLBACK_FAILED] callback failed",
2758+
)
2759+
})?;
27382760
let server_name_py: PyObjectRef = match sni_name {
27392761
Some(name) => vm.ctx.new_str(name.to_string()).into(),
27402762
None => vm.ctx.none(),
@@ -3955,12 +3977,13 @@ mod _ssl {
39553977

39563978
#[pygetset]
39573979
fn owner(&self) -> Option<PyObjectRef> {
3958-
self.owner.read().clone()
3980+
self.owner.read().as_ref().and_then(|owner| owner.upgrade())
39593981
}
39603982

39613983
#[pygetset(setter)]
3962-
fn set_owner(&self, owner: PyObjectRef, _vm: &VirtualMachine) {
3963-
*self.owner.write() = Some(owner);
3984+
fn set_owner(&self, owner: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
3985+
*self.owner.write() = Some(owner.downgrade(None, vm)?);
3986+
Ok(())
39643987
}
39653988

39663989
#[pygetset]

0 commit comments

Comments
 (0)