On Tue, 13 Oct 2026 at 21:09, Jonathan Wakely <[email protected]> wrote:
>
> A very negative time point will stay negative when info.begin is
> subtracted from it, which means that a negative value will be passed to
> vector::reserve. That will convert to a huge size_t value, and the
> vector will throw length_error. This commit clamps the time point
> argument to be no less than the start of the zone's first info, so that
> `tp - info.begin` will be non-negative.
>
> This commit also changes it to chrono::abs(tp - info.begin).  This is
> not strictly necessary now that tp is not less than info.begin, but it
> makes the code more robust. Even if a later change makes it possible for
> the value to be negative again, we will not pass a negative value to
> vector::reserve.
>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/127815
>         * src/c++20/tzdb.cc (time_zone::_M_get_sys_info): Clamp
>         parameter to start of first info. Use chrono::abs for argument
>         to vector::reserve.
>         * testsuite/std/time/time_zone/127815.cc: New test.
> ---
>
> Tested x86_64-linux.
>
>  libstdc++-v3/src/c++20/tzdb.cc                |  8 +++-
>  .../testsuite/std/time/time_zone/127815.cc    | 40 +++++++++++++++++++
>  2 files changed, 46 insertions(+), 2 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/std/time/time_zone/127815.cc
>
> diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc
> index fddfa38b32d7..67a80074381a 100644
> --- a/libstdc++-v3/src/c++20/tzdb.cc
> +++ b/libstdc++-v3/src/c++20/tzdb.cc
> @@ -1085,7 +1085,11 @@ namespace std::chrono
>      sys_info info;
>
>      if (i == infos.begin())
> -      info.begin = sys_days(year::min()/January/1);
> +      {
> +       info.begin = sys_days(year::min()/January/1);
> +       if (tp < info.begin)
> +         tp = info.begin;
> +      }
>      else
>        info.begin = i[-1].until();
>
> @@ -1106,7 +1110,7 @@ namespace std::chrono
>        __throw_runtime_error("std::chrono::time_zone::get_info: invalid 
> data");
>
>      vector<pair<sys_info, string_view>> new_infos;
> -    if (int n = ceil<years>(tp - info.begin).count())
> +    if (int n = ceil<years>(chrono::abs(tp - info.begin)).count())
>        new_infos.reserve(std::min(100, n * 2));
>      long result_index = -1;
>      int num_after = 4; // number of sys_info to generate past tp.
> diff --git a/libstdc++-v3/testsuite/std/time/time_zone/127815.cc 
> b/libstdc++-v3/testsuite/std/time/time_zone/127815.cc
> new file mode 100644
> index 000000000000..043b2affc15d
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/time/time_zone/127815.cc
> @@ -0,0 +1,40 @@
> +// { dg-do run { target c++20 } }
> +// { dg-require-effective-target tzdb }
> +
> +#include <chrono>
> +#include <fstream>
> +#include <testsuite_hooks.h>
> +
> +namespace __gnu_cxx
> +{
> +  // libstdc++ hook: read tzdata.zi from this directory instead of the 
> system's.
> +  const char* zoneinfo_dir_override() { return "."; }
> +}
> +
> +static void
> +test_pr127815()
> +{
> +  using namespace std::chrono;
> +  const sys_seconds ymin{sys_days{year::min() / January / 1}};
> +
> +  static int version = 0;
> +
> +  for (const char* name : {"Plain", "Test"})
> +    for (sys_seconds t : {sys_seconds::min(), ymin - 1s})
> +    {
> +      std::ofstream("tzdata.zi")
> +       << "# version test" << ++version << "\n"
> +          "R X 2000 ma - Mar lastSu 2 1 D\n"
> +          "R X 2000 ma - O lastSu 2 0 S\n"
> +          "Z Test -6 X T%sT\n"
> +          "Z Plain -6 - PST\n";
> +
> +      sys_info info = locate_zone(name)->get_info(t);

Oops, this was supposed to be:
    sys_info info = reload_tzdb().locate_zone(name)->get_info(t);
so that we get a new tzdb on each iteration, with no infos expanded
yet. The static variable ensures the library sees a new tzdata.zi file
each time and re-loads it.

> +      VERIFY(info.abbrev == name[0] + std::string("ST"));
> +    }
> +}
> +
> +int main()
> +{
> +  test_pr127815();
> +}
> --
> 2.56.0
>

Reply via email to