dfa500
commit 9333498794cde1d5cca518badf79533a24114b6f
dfa500
Author: Joseph Myers <joseph@codesourcery.com>
dfa500
Date:   Wed Feb 12 23:31:56 2020 +0000
dfa500
dfa500
    Avoid ldbl-96 stack corruption from range reduction of pseudo-zero (bug 25487).
dfa500
dfa500
    Bug 25487 reports stack corruption in ldbl-96 sinl on a pseudo-zero
dfa500
    argument (an representation where all the significand bits, including
dfa500
    the explicit high bit, are zero, but the exponent is not zero, which
dfa500
    is not a valid representation for the long double type).
dfa500
dfa500
    Although this is not a valid long double representation, existing
dfa500
    practice in this area (see bug 4586, originally marked invalid but
dfa500
    subsequently fixed) is that we still seek to avoid invalid memory
dfa500
    accesses as a result, in case of programs that treat arbitrary binary
dfa500
    data as long double representations, although the invalid
dfa500
    representations of the ldbl-96 format do not need to be consistently
dfa500
    handled the same as any particular valid representation.
dfa500
dfa500
    This patch makes the range reduction detect pseudo-zero and unnormal
dfa500
    representations that would otherwise go to __kernel_rem_pio2, and
dfa500
    returns a NaN for them instead of continuing with the range reduction
dfa500
    process.  (Pseudo-zero and unnormal representations whose unbiased
dfa500
    exponent is less than -1 have already been safely returned from the
dfa500
    function before this point without going through the rest of range
dfa500
    reduction.)  Pseudo-zero representations would previously result in
dfa500
    the value passed to __kernel_rem_pio2 being all-zero, which is
dfa500
    definitely unsafe; unnormal representations would previously result in
dfa500
    a value passed whose high bit is zero, which might well be unsafe
dfa500
    since that is not a form of input expected by __kernel_rem_pio2.
dfa500
dfa500
    Tested for x86_64.
dfa500
dfa500
Revised for RHEL 8.3.0.
dfa500
dfa500
diff -Nrup a/sysdeps/ieee754/ldbl-96/e_rem_pio2l.c b/sysdeps/ieee754/ldbl-96/e_rem_pio2l.c
dfa500
--- a/sysdeps/ieee754/ldbl-96/e_rem_pio2l.c	2018-08-01 01:10:47.000000000 -0400
dfa500
+++ b/sysdeps/ieee754/ldbl-96/e_rem_pio2l.c	2020-04-03 13:05:02.609844427 -0400
dfa500
@@ -209,6 +209,18 @@ __ieee754_rem_pio2l (long double x, long
dfa500
       y[1] = y[0];
dfa500
       return 0;
dfa500
     }
dfa500
+  
dfa500
+  if ((i0 & 0x80000000) == 0)
dfa500
+    {
dfa500
+      /* Pseudo-zero and unnormal representations are not valid
dfa500
+        representations of long double.  We need to avoid stack
dfa500
+        corruption in __kernel_rem_pio2, which expects input in a
dfa500
+        particular normal form, but those representations do not need
dfa500
+        to be consistently handled like any particular floating-point
dfa500
+        value.  */
dfa500
+      y[1] = y[0] = __builtin_nanl ("");
dfa500
+      return 0;
dfa500
+    }
dfa500
 
dfa500
   /* Split the 64 bits of the mantissa into three 24-bit integers
dfa500
      stored in a double array.  */
dfa500
diff -Nrup a/sysdeps/ieee754/ldbl-96/Makefile b/sysdeps/ieee754/ldbl-96/Makefile
dfa500
--- a/sysdeps/ieee754/ldbl-96/Makefile	2018-08-01 01:10:47.000000000 -0400
dfa500
+++ b/sysdeps/ieee754/ldbl-96/Makefile	2020-04-03 13:03:20.233546734 -0400
dfa500
@@ -17,5 +17,6 @@
dfa500
 # <http://www.gnu.org/licenses/>.
dfa500
 
dfa500
 ifeq ($(subdir),math)
dfa500
-tests += test-canonical-ldbl-96 test-totalorderl-ldbl-96
dfa500
+tests += test-canonical-ldbl-96 test-totalorderl-ldbl-96 test-sinl-pseudo
dfa500
+CFLAGS-test-sinl-pseudo.c += -fstack-protector-all
dfa500
 endif
dfa500
diff -Nrup a/sysdeps/ieee754/ldbl-96/test-sinl-pseudo.c b/sysdeps/ieee754/ldbl-96/test-sinl-pseudo.c
dfa500
--- a/sysdeps/ieee754/ldbl-96/test-sinl-pseudo.c	1969-12-31 19:00:00.000000000 -0500
dfa500
+++ b/sysdeps/ieee754/ldbl-96/test-sinl-pseudo.c	2020-04-03 13:05:37.857952212 -0400
dfa500
@@ -0,0 +1,41 @@
dfa500
+/* Test sinl for pseudo-zeros and unnormals for ldbl-96 (bug 25487).
dfa500
+   Copyright (C) 2020 Free Software Foundation, Inc.
dfa500
+   This file is part of the GNU C Library.
dfa500
+
dfa500
+   The GNU C Library is free software; you can redistribute it and/or
dfa500
+   modify it under the terms of the GNU Lesser General Public
dfa500
+   License as published by the Free Software Foundation; either
dfa500
+   version 2.1 of the License, or (at your option) any later version.
dfa500
+
dfa500
+   The GNU C Library is distributed in the hope that it will be useful,
dfa500
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
dfa500
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
dfa500
+   Lesser General Public License for more details.
dfa500
+
dfa500
+   You should have received a copy of the GNU Lesser General Public
dfa500
+   License along with the GNU C Library; if not, see
dfa500
+   <https://www.gnu.org/licenses/>.  */
dfa500
+
dfa500
+#include <math.h>
dfa500
+#include <math_ldbl.h>
dfa500
+#include <stdint.h>
dfa500
+
dfa500
+static int
dfa500
+do_test (void)
dfa500
+{
dfa500
+  for (int i = 0; i < 64; i++)
dfa500
+    {
dfa500
+      uint64_t sig = i == 63 ? 0 : 1ULL << i;
dfa500
+      long double ld;
dfa500
+      SET_LDOUBLE_WORDS (ld, 0x4141,
dfa500
+                        sig >> 32, sig & 0xffffffffULL);
dfa500
+      /* The requirement is that no stack overflow occurs when the
dfa500
+        pseudo-zero or unnormal goes through range reduction.  */
dfa500
+      volatile long double ldr;
dfa500
+      ldr = sinl (ld);
dfa500
+      (void) ldr;
dfa500
+    }
dfa500
+  return 0;
dfa500
+}
dfa500
+
dfa500
+#include <support/test-driver.c>