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::__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.
+//
+// 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;
+}
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