GeezerVison is showing github.com — Assertion `year >= 1' failed in 'days_before_year'` when unpickling datetime with year 0 · Issue #160358 · python/cpython
Assertion year >= 1' failed in 'days_before_year' when unpickling datetime with year 0 #160358DescriptionCrash report
_datetimemodule.c does not validate that MINYEAR <= year <= MAXYEAR when reconstructing a datetime.datetime object from serialized bytes (unpickling).
This allows instantiating an invalid datetime object with year == 0. Subsequent calls to methods such as .toordinal() hit the C-level assertion assert(year >= 1) in days_before_year(), crashing the interpreter process with SIGABRT in debug / -X dev builds.
Steps to Reproduce
Run the following script with ./python -X dev:
import datetime
import pickle
dt = datetime.datetime(2023, 10, 1)
data = pickle.dumps(dt)
# Replace year 2023 (0x07E7) with 0 (0x0000)
corrupted_data = data.replace(b'\x07\xe7', b'\x00\x00')
obj = pickle.loads(corrupted_data)
obj.toordinal()
Actual Behavior
The process aborts due to an assertion failure:
python: ./Modules/_datetimemodule.c:477: int days_before_year(int): Assertion `year >= 1' failed.
Fatal Python error: Aborted
Current thread 0x00007f8943602400 [python] (most recent call first):
File "<python-input-0>", line 11 in <module>
File "/home/boss/projects/oss/cpython/main/Lib/_pyrepl/console.py", line 231 in runcode
File "/home/boss/projects/oss/cpython/main/Lib/_pyrepl/console.py", line 288 in runsource
File "/home/boss/projects/oss/cpython/main/Lib/code.py", line 324 in push
File "/home/boss/projects/oss/cpython/main/Lib/_pyrepl/simple_interact.py", line 149 in run_multiline_interactive_console
File "/home/boss/projects/oss/cpython/main/Lib/_pyrepl/main.py", line 58 in interactive_console
Current thread's C stack trace (most recent call first):
Binary file "./python", at __interceptor_backtrace+0x4c [0x55a217378b2c]
Binary file "./python", at _Py_DumpStack+0x10c [0x55a21803e9c4]
Binary file "./python", at +0x1a523c1 [0x55a2180b83c1]
Binary file "/usr/lib/libc.so.6", at +0x3e6f0 [0x7f894323e6f0]
Binary file "/usr/lib/libc.so.6", at +0x9a11c [0x7f894329a11c]
Binary file "/usr/lib/libc.so.6", at gsignal+0x20 [0x7f894323e5d0]
Binary file "/usr/lib/libc.so.6", at abort+0x26 [0x7f8943225685]
Binary file "/usr/lib/libc.so.6", at +0x26704 [0x7f8943226704]
Binary file "/usr/lib/libc.so.6", at __assert_fail+0xc2 [0x7f89432361f2]
Binary file "./python", at +0x1ab4039 [0x55a21811a039]
Binary file "./python", at +0x115f759 [0x55a2177c5759]
Binary file "./python", at +0x111c356 [0x55a217782356]
Binary file "./python", at _Py_VectorCallInstrumentation_StackRefSteal+0x180 [0x55a217c96a58]
Binary file "./python", at +0x1671568 [0x55a217cd7568]
Binary file "./python", at _PyEval_EvalFrameDefault+0x4a1 [0x55a217c9a059]
Binary file "./python", at PyEval_EvalCode+0x2d9 [0x55a217c956e1]
Binary file "./python", at +0x1621beb [0x55a217c87beb]
Binary file "./python", at _Py_BuiltinCallFastWithKeywords_StackRef+0x155 [0x55a217c9773d]
Binary file "./python", at +0x16bcdf4 [0x55a217d22df4]
Binary file "./python", at _PyEval_EvalFrameDefault+0x4a1 [0x55a217c9a059]
Binary file "./python", at +0x162fc40 [0x55a217c95c40]
Binary file "./python", at +0x111f20a [0x55a21778520a]
Binary file "./python", at +0x1a1c6ca [0x55a2180826ca]
Binary file "./python", at +0x1a1ae13 [0x55a218080e13]
Binary file "./python", at Py_RunMain+0xd79 [0x55a21807f051]
Binary file "./python", at +0x1a19cf9 [0x55a21807fcf9]
Binary file "./python", at Py_BytesMain+0xd5 [0x55a21807ffbd]
Binary file "/usr/lib/libc.so.6", at +0x27781 [0x7f8943227781]
Binary file "/usr/lib/libc.so.6", at __libc_start_main+0x89 [0x7f89432278b9]
Binary file "./python", at _start+0x25 [0x55a217328fa5]
fish: Job 1, './python -X dev' terminated by signal SIGABRT (Abort)
Expected Behavior
pickle.loads() should validate year against MINYEAR (1) and MAXYEAR (9999) during state unpacking and raise ValueError: year 0 is out of range, consistent with the standard datetime(year, month, day) constructor.
Environment
- CPython version: 3.16.0a1+dev (
heads/main:ad101c3b307)
- OS: Linux
- Build:
CC=clang CXX=clang++ LDFLAGS='-fuse-ld=lld' ./configure --with-tail-call-interp --with-pydebug --with-address-sanitizer --with-undefined-behavior-sanitizer
CPython versions tested on:
CPython main branch, 3.16, 3.15, 3.14, 3.13, 3.12, 3.11
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.16.0a1+dev (heads/main:ad101c3b307, Oct 22 2026, 23:57:40) [Clang 23.1.3 ] Activitypicnixz commented on Oct 22, 2026 I honestly don't think it's relevant here. You need to change the pickle representation so I would expect that to crash. It's within the same realm as crashes due to code.__replace__,
I would appreicate if you refrain from submitting such issues in the future as they are mostly non-issues or outside of what we expect to be relevant (lots of similar issues for code.__replace__ were rejected as well).
Nonetheless, I'm leaving it at the discretion of @StanFromIreland but please don't open a PR yet. jelle-openai commented on Oct 23, 2026 This is also reproducible without pickling:
Python 3.12.13 (main, Mar 25 2026, 03:16:06) [Clang 22.1.1 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import datetime
>>>
>>> dt = datetime(bytes([0, 0, 1, 1]) + bytes(6))
>>> dt.ctime()
zsh: segmentation fault python3
Though this is an undocumented constructor format that appears to exist solely to support pickling. Still, I'd argue it shouldn't be possible to segfault Python with just passing a bytes object to the datetime constructor. picnixz commented on Oct 23, 2026 Ok, with the bytes approach, I agree that we shouldn't crash (it's possible that someone just passes the wrong object) StanFromIreland commented on Oct 23, 2026 In that case, yes please open a PR. changed the title [-]Crash (SIGABRT): Assertion `year >= 1' failed in `days_before_year` when unpickling datetime with year 0[/-] [+]Assertion `year >= 1' failed in 'days_before_year'` when unpickling datetime with year 0[/+] on Oct 23, 2026ashm-dev commented on Oct 25, 2026 ContributorAuthor More actions
9. In that case, yes please open a PR.
Got it, thanks. Currently capped at the PR limit, so I'll put it up once a slot frees up (if no one snags it first)
MetadataMetadataStatusShow more project fields
DevelopmentNo branches or pull requestsIssue actions
|