python / python/cpython

time/datetime.fromisoformat: C accelerator accepts a fractional seconds component with no decimal mark

Đang mở
#155,175 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

extension-modules type-bug
Ngôn ngữ chính
Python
Star
77.2k
Fork
35.9k
Chỉ số merge pull request
Chỉ số pull request đang chờ

Mô tả

Bug report

Bug description:

In the basic ISO 8601 time format, the C implementation of
time.fromisoformat() / datetime.fromisoformat() accepts digits following
HHMMSS and silently interprets them as a fractional seconds component, even
though no decimal mark (. or ,) is present. ISO 8601 requires a decimal
sign to introduce a fraction, and the pure-Python implementation correctly
rejects these strings.

>>> from datetime import time, datetime
>>> time.fromisoformat('12345678')
datetime.time(12, 34, 56, 780000)          # expected ValueError
>>> time.fromisoformat('123456789')
datetime.time(12, 34, 56, 789000)          # expected ValueError
>>> datetime.fromisoformat('2020-01-01T12345678')
datetime.datetime(2020, 1, 1, 12, 34, 56, 780000)   # expected ValueError

The pure-Python implementation rejects all of the above:

>>> import _pydatetime
>>> _pydatetime.time.fromisoformat('12345678')
Traceback (most recent call last):
  ...
ValueError: Invalid isoformat string: '12345678'

The same parser is used for the UTC offset, so a malformed offset is accepted
too, with the extra digits silently discarded:

>>> time.fromisoformat('12:34:56+00000000')
datetime.time(12, 34, 56, tzinfo=datetime.timezone.utc)   # expected ValueError

Two further details show the leniency is unintended rather than deliberate:

  • The equivalent extended-format string is correctly rejected —
    time.fromisoformat('12:34:5678') raises ValueError.
  • The behaviour is not even self-consistent across lengths. '1234567'
    (7 digits) raises ValueError, while '12345678' (8 digits) is accepted.

This matters because the failure is silent and produces a wrong value rather
than an error: a truncated or corrupted timestamp string parses successfully
into a time/datetime that differs from the input.

Cause

In parse_hh_mm_ss_ff() (Modules/_datetimemodule.c), the HH/MM/SS loop
breaks when it sees a decimal mark, but in the basic format it can also fall
out of the loop normally after SS with characters still unconsumed (the
!has_separator branch does --p). The code following the loop then parses
whatever remains as a fraction, without ever checking that a decimal mark
introduced it.

The pure-Python _parse_hh_mm_ss_ff() in Lib/_pydatetime.py performs that
check explicitly:

if pos < len_str:
    if tstr[pos] not in '.,':
        raise ValueError("Invalid microsecond separator")
Documentation

fromisoformat() is documented to accept "any valid ISO 8601 format" subject to
an explicit list of exceptions; a fraction without a decimal sign is not among
them.

This is the same class of C/pure-Python divergence as gh-152157 (empty fraction
before a timezone designator) and gh-152079.

Affected versions

Reproduced directly on 3.12.3 and on main (3.16.0a0). I did not have 3.13,
3.14 or 3.15 interpreters to hand, but comparing parse_hh_mm_ss_ff() across
the branches shows the cause is present in all of them: the
else if (!has_separator) { --p; } branch and the unguarded "Parse fractional
components" block that follows the loop are unchanged on every active branch.
The 3.14, 3.15 and main copies of the function are identical; the 3.12 and
3.13 copies differ only in other validation paths (decimal mark on
hour/minute, empty fraction, microsecond separator).

CPython versions tested on:

3.12, 3.16, CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-155177

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu với parse_hh_mm_ss_ff() trong Modules/_datetimemodule.c và so sánh việc kiểm tra hợp lệ của nó với _parse_hh_mm_ss_ff() trong Lib/_pydatetime.py. Tái hiện các ví dụ về định dạng cơ bản và offset không đúng định dạng trong báo cáo, sau đó thêm kiểm thử hồi quy để các đầu vào không có dấu thập phân bị triển khai C từ chối một cách nhất quán.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
c, python
Lĩnh vực
backend
Loại issue
Lỗi
Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
25/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.