On Wed, 8 Jul 2026 at 03:45, Kito Cheng wrote:
>
> When phi is +/-infinity, the periodicity-reduction step computes
> std::floor(infinity) which returns infinity, then casts it to int.
> That cast is undefined behavior per [conv.fpint] because infinity is
> outside the range of int.  A conforming compiler is free to exploit
> this UB; in practice, Clang eliminates the loop-exit check and
> produces an infinite loop.  GCC currently does not exploit this
> particular UB, but the cast is still undefined and should be fixed.
>
> Fix by checking std::isfinite(phi) before the floor+cast and throwing
> std::domain_error for non-finite phi.  Boost guards against this by
> an overflow check (phi >= max_value<T>()), where -infinity is first
> negated to +infinity by the sign-handling block[1].
>
> [1] 
> https://www.boost.org/doc/libs/1_85_0/boost/math/special_functions/ellint_1.hpp
>
> libstdc++-v3/ChangeLog:
>
>         * include/tr1/ell_integral.tcc (__ellint_1): Throw domain_error
>         when phi is not finite.
>         (__ellint_2): Likewise.
>         (__ellint_3): Likewise.
>         * testsuite/special_functions/11_ellint_1/check_inf.cc: New test.
>         * testsuite/special_functions/12_ellint_2/check_inf.cc: New test.
>         * testsuite/special_functions/13_ellint_3/check_inf.cc: New test.
> ---
>  libstdc++-v3/include/tr1/ell_integral.tcc     |  6 +-
>  .../11_ellint_1/check_inf.cc                  | 87 +++++++++++++++++++
>  .../12_ellint_2/check_inf.cc                  | 86 ++++++++++++++++++
>  .../13_ellint_3/check_inf.cc                  | 86 ++++++++++++++++++
>  4 files changed, 262 insertions(+), 3 deletions(-)
>  create mode 100644 
> libstdc++-v3/testsuite/special_functions/11_ellint_1/check_inf.cc
>  create mode 100644 
> libstdc++-v3/testsuite/special_functions/12_ellint_2/check_inf.cc
>  create mode 100644 
> libstdc++-v3/testsuite/special_functions/13_ellint_3/check_inf.cc
>
> diff --git a/libstdc++-v3/include/tr1/ell_integral.tcc 
> b/libstdc++-v3/include/tr1/ell_integral.tcc
> index e78d4e538a6..e2aead73b3b 100644
> --- a/libstdc++-v3/include/tr1/ell_integral.tcc
> +++ b/libstdc++-v3/include/tr1/ell_integral.tcc
> @@ -223,7 +223,7 @@ namespace tr1
>
>        if (__isnan(__k) || __isnan(__phi))
>          return std::numeric_limits<_Tp>::quiet_NaN();
> -      else if (std::abs(__k) > _Tp(1))
> +      else if ((std::abs(__k) > _Tp(1)) || !std::isfinite(__phi))

std::isfinite was added in C++11 so isn't available when using
-std=c++98 -D__STDCPP_WANT_MATH_SPEC_FUNCS__

Since isnan was already used above, we only need std::isinf. Please
use __builtin_isinf(__phi) instead.


>          std::__throw_domain_error(__N("Bad argument in __ellint_1."));
>        else
>          {
> @@ -437,7 +437,7 @@ namespace tr1
>
>        if (__isnan(__k) || __isnan(__phi))
>          return std::numeric_limits<_Tp>::quiet_NaN();
> -      else if (std::abs(__k) > _Tp(1))
> +      else if ((std::abs(__k) > _Tp(1)) || !std::isfinite(__phi))
>          std::__throw_domain_error(__N("Bad argument in __ellint_2."));
>        else
>          {
> @@ -705,7 +705,7 @@ namespace tr1
>
>        if (__isnan(__k) || __isnan(__nu) || __isnan(__phi))
>          return std::numeric_limits<_Tp>::quiet_NaN();
> -      else if (std::abs(__k) > _Tp(1))
> +      else if ((std::abs(__k) > _Tp(1)) || !std::isfinite(__phi))
>          std::__throw_domain_error(__N("Bad argument in __ellint_3."));
>        else
>          {
> diff --git 
> a/libstdc++-v3/testsuite/special_functions/11_ellint_1/check_inf.cc 
> b/libstdc++-v3/testsuite/special_functions/11_ellint_1/check_inf.cc
> new file mode 100644
> index 00000000000..c7efdb468bc
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/special_functions/11_ellint_1/check_inf.cc
> @@ -0,0 +1,87 @@
> +// { dg-do run { target c++17 } }
> +// { dg-options "-D__STDCPP_WANT_MATH_SPEC_FUNCS__" }
> +
> +// Copyright (C) 2026 Free Software Foundation, Inc.

Please read 
https://gcc.gnu.org/onlinedocs/libstdc++/manual/test.html#test.new_tests
and remove the copyright and licence headers from the new tests.

> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +//
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +//
> +// You should have received a copy of the GNU General Public License along
> +// with this library; see the file COPYING3.  If not see
> +// <http://www.gnu.org/licenses/>.
> +
> +// 8.1.11 ellint_1 - non-finite phi must not produce UB (PR libstdc++/XXXXX)

Was this patch produced by AI?
Why is there a bugzilla reference to a a non-existent bug?

> +
> +#include <cmath>
> +#include <limits>
> +#include <stdexcept>
> +#include <testsuite_hooks.h>
> +
> +void
> +test01()
> +{
> +  // +infinity phi should throw domain_error, not loop forever.
> +  bool caught = false;
> +  try
> +    {
> +      volatile float r = std::ellint_1f(0.5F,
> +                               std::numeric_limits<float>::infinity());

Why is this volatile?

> +      (void) r;
> +    }
> +  catch (const std::domain_error&)
> +    {
> +      caught = true;
> +    }
> +  VERIFY(caught);
> +}
> +
> +void
> +test02()
> +{
> +  bool caught = false;
> +  try
> +    {
> +      volatile double r = std::ellint_1(0.5,
> +                               std::numeric_limits<double>::infinity());
> +      (void) r;
> +    }
> +  catch (const std::domain_error&)
> +    {
> +      caught = true;
> +    }
> +  VERIFY(caught);
> +}
> +
> +void
> +test03()
> +{
> +  bool caught = false;
> +  try
> +    {
> +      volatile long double r = std::ellint_1l(0.5L,
> +                       std::numeric_limits<long double>::infinity());
> +      (void) r;
> +    }
> +  catch (const std::domain_error&)
> +    {
> +      caught = true;
> +    }
> +  VERIFY(caught);
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +  test02();
> +  test03();
> +  return 0;
> +}
> diff --git 
> a/libstdc++-v3/testsuite/special_functions/12_ellint_2/check_inf.cc 
> b/libstdc++-v3/testsuite/special_functions/12_ellint_2/check_inf.cc
> new file mode 100644
> index 00000000000..03b8d80dba7
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/special_functions/12_ellint_2/check_inf.cc
> @@ -0,0 +1,86 @@
> +// { dg-do run { target c++17 } }
> +// { dg-options "-D__STDCPP_WANT_MATH_SPEC_FUNCS__" }
> +
> +// Copyright (C) 2026 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +//
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +//
> +// You should have received a copy of the GNU General Public License along
> +// with this library; see the file COPYING3.  If not see
> +// <http://www.gnu.org/licenses/>.
> +
> +// 8.1.12 ellint_2 - non-finite phi must not produce UB (PR libstdc++/XXXXX)
> +
> +#include <cmath>
> +#include <limits>
> +#include <stdexcept>
> +#include <testsuite_hooks.h>
> +
> +void
> +test01()
> +{
> +  bool caught = false;
> +  try
> +    {
> +      volatile float r = std::ellint_2f(0.5F,
> +                               std::numeric_limits<float>::infinity());
> +      (void) r;
> +    }
> +  catch (const std::domain_error&)
> +    {
> +      caught = true;
> +    }
> +  VERIFY(caught);
> +}
> +
> +void
> +test02()
> +{
> +  bool caught = false;
> +  try
> +    {
> +      volatile double r = std::ellint_2(0.5,
> +                               std::numeric_limits<double>::infinity());
> +      (void) r;
> +    }
> +  catch (const std::domain_error&)
> +    {
> +      caught = true;
> +    }
> +  VERIFY(caught);
> +}
> +
> +void
> +test03()
> +{
> +  bool caught = false;
> +  try
> +    {
> +      volatile long double r = std::ellint_2l(0.5L,
> +                       std::numeric_limits<long double>::infinity());
> +      (void) r;
> +    }
> +  catch (const std::domain_error&)
> +    {
> +      caught = true;
> +    }
> +  VERIFY(caught);
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +  test02();
> +  test03();
> +  return 0;
> +}
> diff --git 
> a/libstdc++-v3/testsuite/special_functions/13_ellint_3/check_inf.cc 
> b/libstdc++-v3/testsuite/special_functions/13_ellint_3/check_inf.cc
> new file mode 100644
> index 00000000000..25b1f909d46
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/special_functions/13_ellint_3/check_inf.cc
> @@ -0,0 +1,86 @@
> +// { dg-do run { target c++17 } }
> +// { dg-options "-D__STDCPP_WANT_MATH_SPEC_FUNCS__" }
> +
> +// Copyright (C) 2026 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +//
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +//
> +// You should have received a copy of the GNU General Public License along
> +// with this library; see the file COPYING3.  If not see
> +// <http://www.gnu.org/licenses/>.
> +
> +// 8.1.13 ellint_3 - non-finite phi must not produce UB (PR libstdc++/XXXXX)
> +
> +#include <cmath>
> +#include <limits>
> +#include <stdexcept>
> +#include <testsuite_hooks.h>
> +
> +void
> +test01()
> +{
> +  bool caught = false;
> +  try
> +    {
> +      volatile float r = std::ellint_3f(0.5F, 0.5F,
> +                               std::numeric_limits<float>::infinity());
> +      (void) r;
> +    }
> +  catch (const std::domain_error&)
> +    {
> +      caught = true;
> +    }
> +  VERIFY(caught);
> +}
> +
> +void
> +test02()
> +{
> +  bool caught = false;
> +  try
> +    {
> +      volatile double r = std::ellint_3(0.5, 0.5,
> +                               std::numeric_limits<double>::infinity());
> +      (void) r;
> +    }
> +  catch (const std::domain_error&)
> +    {
> +      caught = true;
> +    }
> +  VERIFY(caught);
> +}
> +
> +void
> +test03()
> +{
> +  bool caught = false;
> +  try
> +    {
> +      volatile long double r = std::ellint_3l(0.5L, 0.5L,
> +                       std::numeric_limits<long double>::infinity());
> +      (void) r;
> +    }
> +  catch (const std::domain_error&)
> +    {
> +      caught = true;
> +    }
> +  VERIFY(caught);
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +  test02();
> +  test03();
> +  return 0;
> +}
> --
> 2.54.0
>

Reply via email to