`datetime` : Type Confusion in tzinfo_fromutc()
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 77.2k
- Forks
- 35.9k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
Crash report
What happened?
In tzinfo_fromutc(), the first call to add_datetime_timedelta() at line 4168 internally calls new_datetime_subclass_ex(), which invokes the subclass __new__() method. If the subclass __new__() returns an object that is not a datetime instance, the code proceeds to use it as a PyDateTime_DateTime * without any type check.
At line 4179, this non-datetime object is passed to add_datetime_timedelta() again with a direct (PyDateTime_DateTime *) cast:
// Line 4168 — first call, result can be any Python object
result = add_datetime_timedelta((PyDateTime_DateTime *)dt, delta, 1);
if (result == NULL)
goto Fail;
// Line 4179 — second call, result cast to PyDateTime_DateTime* without type check
Py_SETREF(result, add_datetime_timedelta((PyDateTime_DateTime *)result,
(PyDateTime_Delta *)dst, 1));
Inside add_datetime_timedelta() (line 6194–6217), the accessor macros GET_YEAR(), GET_MONTH(), GET_DAY() etc. read from fixed offsets relative to the PyDateTime_DateTime struct layout. When the object is actually a bytearray, these offsets correspond to entirely different fields (ob_alloc, ob_bytes, ob_start), resulting in garbage values.
import datetime
call_count = 0
class EvilDatetime(datetime.datetime):
def __new__(cls, *args, **kwargs):
global call_count
call_count += 1
if call_count > 1:
return bytearray(b'\x00' * 200)
return super().__new__(cls, *args, **kwargs)
class SimpleTZ(datetime.tzinfo):
def utcoffset(self, dt):
return datetime.timedelta(hours=2)
def dst(self, dt):
return datetime.timedelta(hours=1)
def tzname(self, dt):
return "Test"
tz = SimpleTZ()
call_count = 0
dt = EvilDatetime(2000, 1, 1, 12, 0, 0, tzinfo=tz)
tz.fromutc(dt) # Crash
Direct execution (ASAN build)
$ ./build-asan/python fromutc_type_confusion.py
python: ../Modules/_datetimemodule.c:770: normalize_y_m_d: Assertion `1 <= *m && *m <= 12' failed.
Exit code: 134
The interpreter aborts due to the assertion 1 <= *m && *m <= 12 in normalize_y_m_d() (line 770), because GET_MONTH() reads a zero value from the bytearray's ob_alloc field instead of the datetime's data[2] field.
Crash backtrace
(gdb) continue
Program received signal SIGABRT, Aborted.
(gdb) backtrace
#0 __pthread_kill_implementation (signo=6) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=6) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (signo=6) at ./nptl/pthread_kill.c:89
#3 __GI_raise (sig=6) at ../sysdeps/posix/raise.c:26
#4 __GI_abort () at ./stdlib/abort.c:79
#5 __assert_fail_base (assertion=0x55555611ad20 "1 <= *m && *m <= 12",
file=0x555556119940 "../Modules/_datetimemodule.c", line=770,
function=0x55555611fb40 "normalize_y_m_d") at ./assert/assert.c:96
#6 __assert_fail (assertion=0x55555611ad20 "1 <= *m && *m <= 12",
file=0x555556119940 "../Modules/_datetimemodule.c", line=770,
function=0x55555611fb40 "normalize_y_m_d") at ./assert/assert.c:105
#7 normalize_y_m_d (m=0x7ffff551ec30) at ../Modules/_datetimemodule.c:770
#8 normalize_date at ../Modules/_datetimemodule.c:831
#9 normalize_datetime at ../Modules/_datetimemodule.c:846
#10 add_datetime_timedelta (date=0x5080000d4b30, delta=0x50600005dd50, factor=1)
at ../Modules/_datetimemodule.c:6208
#11 tzinfo_fromutc at ../Modules/_datetimemodule.c:4179
CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS, Linux
Output from running 'python -VV' on the command line:
Python 3.15.0a6+
Linked PRs
- gh-145181
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Lies Modules/_datetimemodule.c rund um tzinfo_fromutc() und add_datetime_timedelta() und führe anschließend den bereitgestellten Reproducer mit einem ASAN-Build aus. Als abgeschlossen gilt die Änderung, wenn das Ergebnis der Nicht-datetime-Subklasse nicht mehr den Zugriff auf datetime-Felder erreicht und ein Regressionstest den Absturzfall abdeckt. PR gh-145181 ist bereits verlinkt.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- c, python
- Bereich
- backend
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Klar beschrieben
- Anfängerfreundlichkeit
- 25/100