Skip to content

Commit

Permalink
Fix crash when stringifying pre 1970 dates on Windows
Browse files Browse the repository at this point in the history
`gmtime*` on Windows fails on dates pre 1970, and because we didn't
check the return code, we would then pass invalid `tm` struct to
`strftime` causing it to assert.

Closes #2944
  • Loading branch information
horenmar committed Jan 5, 2025
1 parent 506276c commit f387caf
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/catch2/catch_tostring.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,11 @@ struct ratio_string<std::milli> {

#ifdef _MSC_VER
std::tm timeInfo = {};
gmtime_s(&timeInfo, &converted);
const auto err = gmtime_s(&timeInfo, &converted);
if ( err ) {
return "gmtime from provided timepoint has failed. This "
"happens e.g. with pre-1970 dates using Microsoft libc";
}
#else
std::tm* timeInfo = std::gmtime(&converted);
#endif
Expand Down
23 changes: 23 additions & 0 deletions tests/SelfTest/IntrospectiveTests/ToString.tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
// SPDX-License-Identifier: BSL-1.0

#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_template_test_macros.hpp>

#include <chrono>

enum class EnumClass3 { Value1, Value2, Value3, Value4 };

struct UsesSentinel {
Expand Down Expand Up @@ -95,3 +98,23 @@ TEMPLATE_TEST_CASE( "Stringifying char arrays with statically known sizes",
TestType no_null_terminator[3] = { 'a', 'b', 'c' };
CHECK( ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s );
}

TEST_CASE( "#2944 - Stringifying dates before 1970 should not crash" ) {
using Catch::Matchers::Equals;
using Days = std::chrono::duration<int32_t, std::ratio<86400>>;
using SysDays = std::chrono::time_point<std::chrono::system_clock, Days>;
Catch::StringMaker<std::chrono::system_clock::time_point> sm;

// Check simple date first
const SysDays post1970{ Days{ 1 } };
auto converted_post = sm.convert( post1970 );
REQUIRE( converted_post == "1970-01-02T00:00:00Z" );

const SysDays pre1970{ Days{ -1 } };
auto converted_pre = sm.convert( pre1970 );
REQUIRE_THAT(
converted_pre,
Equals( "1969-12-31T00:00:00Z" ) ||
Equals( "gmtime from provided timepoint has failed. This "
"happens e.g. with pre-1970 dates using Microsoft libc" ) );
}

0 comments on commit f387caf

Please sign in to comment.