CVE-2024-32650
Description
Rustls is a modern TLS library written in Rust. rustls::ConnectionCommon::complete_io could fall into an infinite loop based on network input. When using a blocking rustls server, if a client send a close_notify message immediately after client_hello, the server's complete_io will get in an infinite loop. This vulnerability is fixed in 0.23.5, 0.22.4, and 0.21.11.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
rustlscrates.io | >= 0.23.0, < 0.23.5 | 0.23.5 |
rustlscrates.io | >= 0.22.0, < 0.22.4 | 0.22.4 |
rustlscrates.io | >= 0.21.0, < 0.21.11 | 0.21.11 |
Patches
56e938bcfe82acomplete_io: bail out if progress is impossible
2 files changed · +43 −0
rustls/src/conn.rs+5 −0 modified@@ -553,6 +553,11 @@ impl<Data> ConnectionCommon<Data> { loop { let until_handshaked = self.is_handshaking(); + if !self.wants_write() && !self.wants_read() { + // We will make no further progress. + return Ok((rdlen, wrlen)); + } + while self.wants_write() { wrlen += self.write_tls(io)?; }
rustls/tests/api.rs+38 −0 modified@@ -6335,6 +6335,44 @@ fn test_complete_io_errors_if_close_notify_received_too_early() { ); } +#[test] +fn test_complete_io_with_no_io_needed() { + let (mut client, mut server) = make_pair(KeyType::Rsa2048); + do_handshake(&mut client, &mut server); + client + .writer() + .write_all(b"hello") + .unwrap(); + client.send_close_notify(); + transfer(&mut client, &mut server); + server.process_new_packets().unwrap(); + server + .writer() + .write_all(b"hello") + .unwrap(); + server.send_close_notify(); + transfer(&mut server, &mut client); + client.process_new_packets().unwrap(); + + // neither want any IO: both directions are closed. + assert!(!client.wants_write()); + assert!(!client.wants_read()); + assert!(!server.wants_write()); + assert!(!server.wants_read()); + assert_eq!( + client + .complete_io(&mut FakeStream(&[])) + .unwrap(), + (0, 0) + ); + assert_eq!( + server + .complete_io(&mut FakeStream(&[])) + .unwrap(), + (0, 0) + ); +} + struct FakeStream<'a>(&'a [u8]); impl<'a> io::Read for FakeStream<'a> {
f45664fbded0Don't specially handle unauthenticated close_notify alerts
1 file changed · +2 −2
rustls/src/common_state.rs+2 −2 modified@@ -431,8 +431,8 @@ impl CommonState { } // If we get a CloseNotify, make a note to declare EOF to our - // caller. - if alert.description == AlertDescription::CloseNotify { + // caller. But do not treat unauthenticated alerts like this. + if self.may_receive_application_data && alert.description == AlertDescription::CloseNotify { self.has_received_close_notify = true; return Ok(()); }
2123576840aaRegression test for `complete_io` infinite loop bug
1 file changed · +51 −0
rustls/tests/api.rs+51 −0 modified@@ -6306,6 +6306,57 @@ fn test_server_fips_service_indicator_includes_require_ems() { assert!(!server_config.fips()); } +#[test] +fn test_complete_io_errors_if_close_notify_received_too_early() { + let mut server = ServerConnection::new(Arc::new(make_server_config(KeyType::Rsa2048))).unwrap(); + let client_hello_followed_by_close_notify_alert = b"\ + \x16\x03\x01\x00\xc8\x01\x00\x00\xc4\x03\x03\xec\x12\xdd\x17\x64\ + \xa4\x39\xfd\x7e\x8c\x85\x46\xb8\x4d\x1e\xa0\x6e\xb3\xd7\xa0\x51\ + \xf0\x3c\xb8\x17\x47\x0d\x4c\x54\xc5\xdf\x72\x00\x00\x1c\xea\xea\ + \xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\ + \x00\x9c\x00\x9d\x00\x2f\x00\x35\x00\x0a\x01\x00\x00\x7f\xda\xda\ + \x00\x00\xff\x01\x00\x01\x00\x00\x00\x00\x16\x00\x14\x00\x00\x11\ + \x77\x77\x77\x2e\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x2e\x6f\x72\ + \x67\x00\x17\x00\x00\x00\x23\x00\x00\x00\x0d\x00\x14\x00\x12\x04\ + \x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\ + \x01\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x12\x00\x00\x00\x10\ + \x00\x0e\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31\x2e\x31\ + \x75\x50\x00\x00\x00\x0b\x00\x02\x01\x00\x00\x0a\x00\x0a\x00\x08\ + \x1a\x1a\x00\x1d\x00\x17\x00\x18\x1a\x1a\x00\x01\x00\ + \x15\x03\x03\x00\x02\x01\x00"; + + let mut stream = FakeStream(client_hello_followed_by_close_notify_alert); + assert_eq!( + server + .complete_io(&mut stream) + .unwrap_err() + .kind(), + io::ErrorKind::UnexpectedEof + ); +} + +struct FakeStream<'a>(&'a [u8]); + +impl<'a> io::Read for FakeStream<'a> { + fn read(&mut self, b: &mut [u8]) -> io::Result<usize> { + let take = core::cmp::min(b.len(), self.0.len()); + let (taken, remain) = self.0.split_at(take); + b[..take].copy_from_slice(taken); + self.0 = remain; + Ok(take) + } +} + +impl<'a> io::Write for FakeStream<'a> { + fn write(&mut self, b: &[u8]) -> io::Result<usize> { + Ok(b.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + } // test_for_each_provider! #[derive(Default, Debug)]
ebcb4782f23bcomplete_io: bail out if progress is impossible
2 files changed · +43 −0
rustls/src/conn.rs+5 −0 modified@@ -378,6 +378,11 @@ impl<Data> ConnectionCommon<Data> { loop { let until_handshaked = self.is_handshaking(); + if !self.wants_write() && !self.wants_read() { + // We will make no further progress. + return Ok((rdlen, wrlen)); + } + while self.wants_write() { wrlen += self.write_tls(io)?; }
rustls/tests/api.rs+38 −0 modified@@ -4976,6 +4976,44 @@ fn test_complete_io_errors_if_close_notify_received_too_early() { ); } +#[test] +fn test_complete_io_with_no_io_needed() { + let (mut client, mut server) = make_pair(KeyType::Rsa); + do_handshake(&mut client, &mut server); + client + .writer() + .write_all(b"hello") + .unwrap(); + client.send_close_notify(); + transfer(&mut client, &mut server); + server.process_new_packets().unwrap(); + server + .writer() + .write_all(b"hello") + .unwrap(); + server.send_close_notify(); + transfer(&mut server, &mut client); + client.process_new_packets().unwrap(); + + // neither want any IO: both directions are closed. + assert!(!client.wants_write()); + assert!(!client.wants_read()); + assert!(!server.wants_write()); + assert!(!server.wants_read()); + assert_eq!( + client + .complete_io(&mut FakeStream(&[])) + .unwrap(), + (0, 0) + ); + assert_eq!( + server + .complete_io(&mut FakeStream(&[])) + .unwrap(), + (0, 0) + ); +} + struct FakeStream<'a>(&'a [u8]); impl<'a> io::Read for FakeStream<'a> {
5374108df698complete_io: bail out if progress is impossible
2 files changed · +43 −0
rustls/src/conn.rs+5 −0 modified@@ -391,6 +391,11 @@ impl<Data> ConnectionCommon<Data> { loop { let until_handshaked = self.is_handshaking(); + if !self.wants_write() && !self.wants_read() { + // We will make no further progress. + return Ok((rdlen, wrlen)); + } + while self.wants_write() { wrlen += self.write_tls(io)?; }
rustls/tests/api.rs+38 −0 modified@@ -5855,6 +5855,44 @@ fn test_complete_io_errors_if_close_notify_received_too_early() { ); } +#[test] +fn test_complete_io_with_no_io_needed() { + let (mut client, mut server) = make_pair(KeyType::Rsa); + do_handshake(&mut client, &mut server); + client + .writer() + .write_all(b"hello") + .unwrap(); + client.send_close_notify(); + transfer(&mut client, &mut server); + server.process_new_packets().unwrap(); + server + .writer() + .write_all(b"hello") + .unwrap(); + server.send_close_notify(); + transfer(&mut server, &mut client); + client.process_new_packets().unwrap(); + + // neither want any IO: both directions are closed. + assert!(!client.wants_write()); + assert!(!client.wants_read()); + assert!(!server.wants_write()); + assert!(!server.wants_read()); + assert_eq!( + client + .complete_io(&mut FakeStream(&[])) + .unwrap(), + (0, 0) + ); + assert_eq!( + server + .complete_io(&mut FakeStream(&[])) + .unwrap(), + (0, 0) + ); +} + struct FakeStream<'a>(&'a [u8]); impl<'a> io::Read for FakeStream<'a> {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-6g7w-8wpp-frhjghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-32650ghsaADVISORY
- github.com/rustls/rustls/commit/2123576840aa31043a31b0770e6572136fbe0c2dnvdWEB
- github.com/rustls/rustls/commit/5374108df698e78c3e9ef8265cac311556be24afghsaWEB
- github.com/rustls/rustls/commit/6e938bcfe82a9da7a2e1cbf10b928c7eca26426envdWEB
- github.com/rustls/rustls/commit/ebcb4782f23b4edf9b10a7065d9e8d4362439d9cghsaWEB
- github.com/rustls/rustls/commit/f45664fbded03d833dffd806503d3c8becd1b71envdWEB
- github.com/rustls/rustls/security/advisories/GHSA-6g7w-8wpp-frhjnvdWEB
- rustsec.org/advisories/RUSTSEC-2024-0336.htmlghsaWEB
News mentions
0No linked articles in our index yet.