Fix SSL handshake over-reading in STARTTLS#7417
Conversation
📝 WalkthroughWalkthroughThis change centralizes timeout error construction across SSL and OpenSSL modules using a shared helper function, and introduces TLS record boundary-aware reading in the SSL handshake path to prevent premature buffer consumption and potential application data loss. Changes
Sequence Diagram(s)sequenceDiagram
participant Socket as TCP Socket
participant Peek as Peek Buffer
participant Parser as TLS Parser
participant Reader as Record Reader
participant PyVM as Python VM
Socket->>Peek: peek(header_size=5)
Peek-->>Parser: raw_bytes[0:5]
Parser->>Parser: parse version + length
Parser-->>Reader: total_record_size
Reader->>Socket: recv(total_record_size)
Socket-->>Reader: complete_record_bytes
Reader->>PyVM: return exact bytes read
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
083f2bf to
1fe003d
Compare
During STARTTLS handshake, sock_recv(16KB) could consume application data that arrived alongside handshake records. The consumed data ended up in rustls's internal buffer where select() could not detect it, causing asyncore-based servers to miss readable events and the peer to time out. Use MSG_PEEK to find the TLS record boundary, then recv() only one complete record. Remaining data stays in the kernel TCP buffer, visible to select(). This matches OpenSSL's default no-read-ahead behaviour. Fixes flaky test_poplib (TestPOP3_TLSClass) failures.
1fe003d to
2602df0
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/stdlib/src/openssl.rs (1)
2862-2866: Minor style inconsistency: prefer.to_owned()for consistency.The rest of the file uses
.to_owned()for string literal conversions (e.g., lines 3000, 3051, 3242), while this change uses.to_string(). Both are functionally equivalent, but using.to_owned()would maintain consistency.🔧 Suggested fix for consistency
- return Err(socket::timeout_error_msg( - vm, - "The read operation timed out".to_string(), - ) - .upcast()); + return Err(socket::timeout_error_msg( + vm, + "The read operation timed out".to_owned(), + ) + .upcast());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/stdlib/src/openssl.rs` around lines 2862 - 2866, Replace the .to_string() call on the literal inside the timeout error return with .to_owned() for consistency with the rest of openssl.rs; specifically update the call in the expression that constructs the socket::timeout_error_msg (the return Err(... upcast()) statement that currently uses "The read operation timed out".to_string()) to use "The read operation timed out".to_owned().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@crates/stdlib/src/openssl.rs`:
- Around line 2862-2866: Replace the .to_string() call on the literal inside the
timeout error return with .to_owned() for consistency with the rest of
openssl.rs; specifically update the call in the expression that constructs the
socket::timeout_error_msg (the return Err(... upcast()) statement that currently
uses "The read operation timed out".to_string()) to use "The read operation
timed out".to_owned().
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 37b93a80-4151-4eba-8f46-f3bf90dbb5b8
📒 Files selected for processing (5)
crates/stdlib/src/openssl.rscrates/stdlib/src/ssl.rscrates/stdlib/src/ssl/compat.rscrates/vm/src/stdlib/_winapi.rscrates/vm/src/vm/vm_new.rs
During STARTTLS handshake, sock_recv(16KB) could consume application data that arrived alongside handshake records. The consumed data ended up in rustls's internal buffer where select() could not detect it, causing asyncore-based servers to miss readable events and the peer to time out.
Use MSG_PEEK to find the TLS record boundary, then recv() only one complete record. Remaining data stays in the kernel TCP buffer, visible to select(). This matches OpenSSL's default no-read-ahead behaviour.
Fixes flaky test_poplib (TestPOP3_TLSClass) failures.
Summary by CodeRabbit