python / python/cpython

`datetime` : Type Confusion in tzinfo_fromutc()

未关闭
#145,166 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

extension-modules type-crash
主要语言
Python
星标
77.2k
派生
35.9k
PR 合并指标
PR 指标待抓取

描述

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

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

阅读 Modules/_datetimemodule.c 中 tzinfo_fromutc() 和 add_datetime_timedelta() 附近的代码,然后使用 ASAN 构建运行提供的 reproducer。当非 datetime 子类的结果不再到达 datetime 字段访问,并且回归测试覆盖该崩溃场景时,即表示完成。PR gh-145181 已经关联。

由索引模型根据 Issue 内容生成。

评估

技术栈
c, python
领域
backend
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
停滞
描述清晰度
描述清楚
新手友好度
25/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。