https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111163

--- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
With this patch:

--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -507,6 +507,16 @@ namespace __format
          if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>)
            __is_neg = __t.is_negative();

+         if constexpr (chrono::__is_duration_v<_Tp>)
+           if constexpr (_Tp::period::num != 1)
+             {
+               // Check conversion to seconds will not overflow.
+               chrono::seconds::rep __s{};
+               if (__builtin_mul_overflow(__t.count(), _Tp::period::num,
&__s))
+                 __throw_format_error("chrono format error: integer overflow "
+                                      "converting duration to seconds");
+             }
+
          auto __print_sign = [&__is_neg, &__out] {
            if constexpr (chrono::__is_duration_v<_Tp>
                            || __is_specialization_of<_Tp, chrono::hh_mm_ss>)

The testcase throws:

terminate called after throwing an instance of 'std::format_error'
  what():  chrono format error: integer overflow converting duration to seconds
Aborted (core dumped)


This isn't ideal as there are some uses that wouldn't overflow, e.g.
std::format("{:%j}", std::chrono::days::max())
This doesn't require any conversion and so could be formatted. But I'm not sure
I care about this case, it's pretty unlikely to ever be needed.

If we did the overflow checks at the point of conversion then we'd be doing
them multiple times for a format string like "%H:%M:%S". But then I suppose
we're already converting the duration to hh_mm_ss three times there anyway. The
patch in PR 110739 comment 2 would help with that, as the _S_hms call might be
subject to common subexpression elimination, and the checks would only be done
once. We could also just call _S_hms once up-front when we have a type that can
make use of it, and then reuse the result as needed.


And the patch above doesn't handle all cases anyway, because this will still
overflow:
std::format("{:%S}", std::chrono::sys_days{std::chrono::days::max()});

So maybe we just need a checked duration_cast<days>, floor<days>, floor<weeks>,
and the check for conversion to seconds for use with hh_mm_ss.

Reply via email to