Skip to content

gh-145166: Fix crash in tzinfo.fromutc() when subclass __new__ returns non-datetime#145181

Open
HELLPUSYY666 wants to merge 1 commit intopython:mainfrom
HELLPUSYY666:fix-tzinfo-fromutc-type-confusion
Open

gh-145166: Fix crash in tzinfo.fromutc() when subclass __new__ returns non-datetime#145181
HELLPUSYY666 wants to merge 1 commit intopython:mainfrom
HELLPUSYY666:fix-tzinfo-fromutc-type-confusion

Conversation

@HELLPUSYY666
Copy link

Problem

In tzinfo_fromutc(), the result of add_datetime_timedelta() is cast
to PyDateTime_DateTime* without a type check. If a datetime subclass
__new__ returns a non-datetime object, the interpreter crashes with
SIGABRT due to out-of-bounds memory reads inside normalize_y_m_d().

Fix

  • Added PyDateTime_Check() after both calls to add_datetime_timedelta()
    in Modules/_datetimemodule.c - raises TypeError instead of crashing
  • Added equivalent isinstance() checks in Lib/_pydatetime.py
  • Added regression test in Lib/test/datetimetester.py

Testing

Regression test passes for both TestDateTimeTZ_Pure and TestDateTimeTZ_Fast.

@python-cla-bot
Copy link

python-cla-bot bot commented Feb 24, 2026

All commit authors signed the Contributor License Agreement.

CLA signed

@HELLPUSYY666 HELLPUSYY666 force-pushed the fix-tzinfo-fromutc-type-confusion branch from f339b73 to 32d0457 Compare February 24, 2026 18:05
@raminfp
Copy link
Contributor

raminfp commented Feb 25, 2026

This fix in add_datetime_timedelta() also fixes the same type confusion crash in datetime.utctimetuple(), since it also calls add_datetime_timedelta() internally. However, there is no test coverage for that case.

Could you add a test for utctimetuple as well? Something like:

def test_utctimetuple_subclass_new_returns_non_datetime(self):
    call_count = 0

    class EvilDatetime(self.theclass):
        def __new__(cls, *args, **kwargs):
            nonlocal call_count
            call_count += 1
            if call_count > 1:
                return bytearray(b'\x00' * 200)
            return super().__new__(cls, *args, **kwargs)

    class SimpleTZ(tzinfo):
        def utcoffset(self, dt): return timedelta(hours=5)
        def dst(self, dt):       return timedelta(0)
        def tzname(self, dt):    return "Test"

    tz = SimpleTZ()
    dt = EvilDatetime(2000, 6, 15, 12, 0, 0, tzinfo=tz)
    with self.assertRaises(TypeError):
        dt.utctimetuple()

@HELLPUSYY666 HELLPUSYY666 force-pushed the fix-tzinfo-fromutc-type-confusion branch from 32d0457 to afa85ee Compare February 25, 2026 04:19
@HELLPUSYY666
Copy link
Author

This fix in add_datetime_timedelta() also fixes the same type confusion crash in datetime.utctimetuple(), since it also calls add_datetime_timedelta() internally. However, there is no test coverage for that case.

Could you add a test for utctimetuple as well? Something like:

def test_utctimetuple_subclass_new_returns_non_datetime(self):
    call_count = 0

    class EvilDatetime(self.theclass):
        def __new__(cls, *args, **kwargs):
            nonlocal call_count
            call_count += 1
            if call_count > 1:
                return bytearray(b'\x00' * 200)
            return super().__new__(cls, *args, **kwargs)

    class SimpleTZ(tzinfo):
        def utcoffset(self, dt): return timedelta(hours=5)
        def dst(self, dt):       return timedelta(0)
        def tzname(self, dt):    return "Test"

    tz = SimpleTZ()
    dt = EvilDatetime(2000, 6, 15, 12, 0, 0, tzinfo=tz)
    with self.assertRaises(TypeError):
        dt.utctimetuple()

@raminfp Thanks for good test, i added this to my PR

@raminfp
Copy link
Contributor

raminfp commented Feb 25, 2026

It was great, but I think this utctimetuple should also be mentioned in the NEWS.

@HELLPUSYY666 HELLPUSYY666 force-pushed the fix-tzinfo-fromutc-type-confusion branch from afa85ee to 6efe847 Compare February 25, 2026 11:53
@HELLPUSYY666
Copy link
Author

It was great, but I think this utctimetuple should also be mentioned in the NEWS.

@raminfp Done!

@HELLPUSYY666
Copy link
Author

@StanFromIreland Hi, could you please review this PR and merge it if everything looks good?

@StanFromIreland
Copy link
Member

Please don't force push, see the devguide for more information.

Also, please note that this is a volunteer project, I try to review within ~48 hours, please don't ping me (unless it has been a long time, i.e. a month, since my review).

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants