libstdc++: fix UB when ellint phi argument is infinity
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap |
pending
|
Patch applied
|
| linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap |
fail
|
Build failed
|
Commit Message
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
Comments
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
>
Also, please CC the libstdc++ list (as required by the
https://gcc.gnu.org/lists.html docs) instead of individually CCing
maintainers.
On Wed, 8 Jul 2026 at 12:24, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> 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
> >
Hi Jonathan:
Thanks for the review, inlined reply :)
>> 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.
Oh, I don't realized std::isfinite was introduced at C++11, thanks for
the suggesion!
> Also, please CC the libstdc++ list (as required by the
> https://gcc.gnu.org/lists.html docs) instead of individually CCing
> maintainers.
thanks for the reminder, that's my first time to send patch to
libstdc++, I was contribute to gcc only before so I didn't notice the
rule.
>> +// 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.
I saw other's test has copyright and licence headers so I thought it's
necessary, yeah, I don't read that before I create testcase...:P
>> +// 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?
Hmm, long story and two part here:
1. I originally planned to file a bug, but after digging deeper, I
found that it only required adding one check, so I write the patch
instead of file a bug on bugzilla, so yeah this was suppose to replace
to a real bugzilla entry.
2. I admitted that I used AI assistance to convert this test case from
an internal report format to a libstdc++ test case format and to
proofread the commit message, but the modification is done by me after
I test with libstdc++ and boost code rather than AI generated.
>> +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?
Simplest way from my mind to prevent ellint_1f got optimized out,
another possible way might
be use global var, but that might be optimized out due to no use.
Also, I saw a few other places in the libstdc++/testsuite use
volatile, so I guess this should be OK to use :)
@@ -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::__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
{
new file mode 100644
@@ -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.
+//
+// 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)
+
+#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());
+ (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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}