Skip to content

gh-43311: Add regression test for SSLError masking SMTPDataError in rset() - #154866

Open
zero-context wants to merge 2 commits into
python:mainfrom
zero-context:gh-43311-tls-rset-regression-test
Open

gh-43311: Add regression test for SSLError masking SMTPDataError in rset()#154866
zero-context wants to merge 2 commits into
python:mainfrom
zero-context:gh-43311-tls-rset-regression-test

Conversation

@zero-context

@zero-context zero-context commented Jul 29, 2026

Copy link
Copy Markdown

This is a ~20-year-old ticket (filed 2006 as bpo-1481032) that turns
out to already be fixed — just never got a regression test or a
closing comment. @soreavis confirmed this 10 days ago
(#43311 (comment))
with a real TLS reproducer, and I independently re-verified it (written
separately, not copied) before adding this test.

The original bug: when sendmail()'s message gets rejected and it
calls the automatic _rset() cleanup, an error from the now-dead
connection during that rset() could mask the original
SMTPDataError — the caller would see a confusing connection error
instead of the actual rejection reason.

Why it's already fixed: two unrelated later changes closed it
together. gh-63696 (bpo-17498, 2013) routes all three of
sendmail()'s error paths through _rset() consistently. Separately,
ssl.SSLError has subclassed OSError since Python 3.3, so
send()/getreply()'s existing except OSError: handlers already
normalize a dead-connection error into SMTPServerDisconnected before
_rset()'s own except SMTPServerDisconnected: pass ever sees it —
the exact case a 2014 comment on the issue said couldn't be tested at
the time.

One correction along the way: both the earlier comment's
reproducer and my first attempt at this test had the same bug — the
fake server responded to DATA with a rejection code directly, which
makes smtplib.data() raise SMTPDataError immediately, before
sendmail() ever reaches its own self._rset() call. That path can't
demonstrate the masking at all, since _rset() is never invoked. The
actual vulnerable path only exists when the server accepts DATA with
354, receives the full message, and then rejects on the final
response — that's the branch in sendmail() that calls self._rset()
before raising SMTPDataError. Fixed the test to use that scenario.

Validated the test is meaningful, not a tautology: temporarily
narrowed send()'s and getreply()'s except OSError: handlers to
simulate the pre-fix world. The test correctly fails — a
ConnectionResetError leaks out and replaces the SMTPDataError
confirming this test would have caught the original bug were it still
present.

No Misc/NEWS.d entry: test-only change per the devguide's own
exceptions list, patchcheck agrees.

Validation
  • New test: passes clean on current main.
  • Full test_smtplib.py: 86 tests, 0 failures.
  • test_ssl: 211 tests, 0 failures (also newly buildable in this
    environment — OpenSSL wasn't available here at all until now).
  • Tools/patchcheck/patchcheck.py: clean.

…r in rset()

pythongh-43311 (bpo-1481032, filed 2006) reported that when sendmail() gets a
rejected DATA command and calls the automatic _rset() cleanup, an
SSLError from the dead connection could mask the original
SMTPDataError. This was already fixed as a side effect of two later,
unrelated changes: pythongh-63696 (bpo-17498) routes all three sendmail()
error paths through _rset(), and ssl.SSLError has subclassed OSError
since Python 3.3, so send()/getreply() already normalize a
dead-connection SSLError into SMTPServerDisconnected before _rset()'s
own except-clause ever sees it.

No regression test previously covered the TLS-specific variant of this
scenario (the existing test__rest_from_mail_cmd is plaintext-only).
Adds one, verified against a deliberately broken build to confirm it
actually catches the original masking behavior (narrowing send()'s and
getreply()'s OSError handlers reproduces the exact 2006 symptom: a
ConnectionResetError leaks out and replaces the SMTPDataError).
@zero-context
zero-context requested a review from a team as a code owner July 29, 2026 10:48
@bedevere-app bedevere-app Bot added the tests Tests in the Lib/test dir label Jul 29, 2026
@bedevere-app

bedevere-app Bot commented Jul 29, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

Comment thread Lib/test/test_smtplib.py


@unittest.skipUnless(SUPPORTS_SMTP_SSL, 'SSL not supported')
class SMTPSSLRsetAfterDataErrorTests(unittest.TestCase):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like an un ecessary addition. Can't you just extend existing tests with existing infra? and please do not just ask an agent to do it. You must be able to explain the change yourself without any AI assistance.

@bedevere-app

bedevere-app Bot commented Jul 29, 2026

Copy link
Copy Markdown

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting changes tests Tests in the Lib/test dir

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants