From a56158b78e3ffcb7be7af2951375adb5dc4ac2e0 Mon Sep 17 00:00:00 2001 From: ostermine Date: Sat, 10 Oct 2026 16:39:20 +0300 Subject: [PATCH] fix crash in create_zip for files without modification time MemoryDevice::lastWriteTime returns file_time_type::min(). Converting it to the system clock overflows, and on Windows std::localtime returns nullptr for the resulting time, which was dereferenced (access violation in zip_files test on MSVC). Unknown or out of range time is now written as 1980-01-01 00:00, the earliest MS-DOS date. --- src/io/devices/ZipFileDevice.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/io/devices/ZipFileDevice.cpp b/src/io/devices/ZipFileDevice.cpp index 2c52439ae..025c1c335 100644 --- a/src/io/devices/ZipFileDevice.cpp +++ b/src/io/devices/ZipFileDevice.cpp @@ -58,11 +58,22 @@ namespace { } uint32_t to_ms_dos_timestamp(const file_time_type& fileTime) { + // 1980-01-01 00:00, the earliest MS-DOS date; used for unknown time + // (devices without timestamps return file_time_type::min()) + constexpr uint32_t DOS_EPOCH = (0 << 9 | 1 << 5 | 1) << 16; + if (fileTime == file_time_type::min()) { + return DOS_EPOCH; + } auto timePoint = time_point_cast( fileTime - file_time_type::clock::now() + system_clock::now() ); std::time_t timeT = system_clock::to_time_t(timePoint); - std::tm tm = *std::localtime(&timeT); + // localtime returns nullptr for out of range time on Windows + const std::tm* tmPtr = std::localtime(&timeT); + if (tmPtr == nullptr || tmPtr->tm_year < 80) { + return DOS_EPOCH; + } + const std::tm& tm = *tmPtr; uint16_t date = (tm.tm_year - 80) << 9 | (tm.tm_mon + 1) << 5 | tm.tm_mday; uint16_t time = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec / 2); return (date << 16) | time;