|
|
0d8b67 |
This is a custom patch for RHEL 7 to fix CVE-2020-29573 and includes
|
|
|
0d8b67 |
parts of 41290b6e842a2adfbda77a49abfacb0db2d63bfb, and
|
|
|
0d8b67 |
681900d29683722b1cb0a8e565a0585846ec5a61.
|
|
|
0d8b67 |
|
|
|
0d8b67 |
We had a discussion[1] upstream about the treatment of unnormal long
|
|
|
0d8b67 |
double numbers in glibc and gcc and there is general consensus that
|
|
|
0d8b67 |
unnormal numbers (pseudos in general) ought to be treated like NaNs
|
|
|
0d8b67 |
without the guarantee that they will always be treated correctly in
|
|
|
0d8b67 |
glibc. That is, there is agreement that we should fix bugs and
|
|
|
0d8b67 |
security issues arising from such inputs but not guarantee glibc
|
|
|
0d8b67 |
behaviour with such inputs since the latter would involve extensive
|
|
|
0d8b67 |
coverage.
|
|
|
0d8b67 |
|
|
|
0d8b67 |
Now on to #1869380, this crash in printf manifests itself only in
|
|
|
0d8b67 |
RHEL-7 and not in any other Red Hat distribution because later
|
|
|
0d8b67 |
versions of glibc use __builtin_nan from gcc, which always recognizes
|
|
|
0d8b67 |
pseudos as NaN. Based on that and the recent consensus, the correct
|
|
|
0d8b67 |
way to fix #1869380 appears to be to treat unnormals as NaN instead of
|
|
|
0d8b67 |
fixing the unnormal representation as in this patch[2].
|
|
|
0d8b67 |
|
|
|
0d8b67 |
[1] https://sourceware.org/pipermail/libc-alpha/2020-November/119949.html
|
|
|
0d8b67 |
[2] https://sourceware.org/pipermail/libc-alpha/2020-September/117779.html
|
|
|
0d8b67 |
|
|
|
0d8b67 |
Co-authored-by: Siddhesh Poyarekar <siddhesh@redhat.com>
|
|
|
0d8b67 |
|
|
|
0d8b67 |
diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c
|
|
|
0d8b67 |
index d0e082494af6b0a3..60b143571065a082 100644
|
|
|
0d8b67 |
--- a/stdio-common/printf_fp.c
|
|
|
0d8b67 |
+++ b/stdio-common/printf_fp.c
|
|
|
0d8b67 |
@@ -151,6 +151,28 @@ static wchar_t *group_number (wchar_t *buf, wchar_t *bufend,
|
|
|
0d8b67 |
wchar_t thousands_sep, int ngroups)
|
|
|
0d8b67 |
internal_function;
|
|
|
0d8b67 |
|
|
|
0d8b67 |
+static __always_inline int
|
|
|
0d8b67 |
+isnanl_or_pseudo (long double in)
|
|
|
0d8b67 |
+{
|
|
|
0d8b67 |
+#if defined __x86_64__ || defined __i386__
|
|
|
0d8b67 |
+ union
|
|
|
0d8b67 |
+ {
|
|
|
0d8b67 |
+ long double f;
|
|
|
0d8b67 |
+ struct
|
|
|
0d8b67 |
+ {
|
|
|
0d8b67 |
+ uint64_t low;
|
|
|
0d8b67 |
+ uint64_t high;
|
|
|
0d8b67 |
+ } u;
|
|
|
0d8b67 |
+ } ldouble;
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+ ldouble.f = in;
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+ return __isnanl (in) || (ldouble.u.low & 0x8000000000000000) == 0;
|
|
|
0d8b67 |
+#else
|
|
|
0d8b67 |
+ return __isnanl (in);
|
|
|
0d8b67 |
+#endif
|
|
|
0d8b67 |
+}
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
|
|
|
0d8b67 |
int
|
|
|
0d8b67 |
__printf_fp_l (FILE *fp, locale_t loc,
|
|
|
0d8b67 |
@@ -335,7 +357,7 @@ __printf_fp_l (FILE *fp, locale_t loc,
|
|
|
0d8b67 |
|
|
|
0d8b67 |
/* Check for special values: not a number or infinity. */
|
|
|
0d8b67 |
int res;
|
|
|
0d8b67 |
- if (__isnanl (fpnum.ldbl))
|
|
|
0d8b67 |
+ if (isnanl_or_pseudo (fpnum.ldbl))
|
|
|
0d8b67 |
{
|
|
|
0d8b67 |
is_neg = signbit (fpnum.ldbl);
|
|
|
0d8b67 |
if (isupper (info->spec))
|
|
|
0d8b67 |
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
|
|
|
0d8b67 |
index c26533245e8a8103..f1da941dbbadadb3 100644
|
|
|
0d8b67 |
--- a/sysdeps/x86/Makefile
|
|
|
0d8b67 |
+++ b/sysdeps/x86/Makefile
|
|
|
0d8b67 |
@@ -18,3 +18,7 @@ sysdep-dl-routines += dl-get-cpu-features
|
|
|
0d8b67 |
tests += tst-get-cpu-features
|
|
|
0d8b67 |
tests-static += tst-get-cpu-features-static
|
|
|
0d8b67 |
endif
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+ifeq ($(subdir),math)
|
|
|
0d8b67 |
+tests += tst-ldbl-nonnormal-printf
|
|
|
0d8b67 |
+endif # $(subdir) == math
|
|
|
0d8b67 |
diff --git a/sysdeps/x86/tst-ldbl-nonnormal-printf.c b/sysdeps/x86/tst-ldbl-nonnormal-printf.c
|
|
|
0d8b67 |
new file mode 100644
|
|
|
0d8b67 |
index 0000000000000000..e4e3e428747488b9
|
|
|
0d8b67 |
--- /dev/null
|
|
|
0d8b67 |
+++ b/sysdeps/x86/tst-ldbl-nonnormal-printf.c
|
|
|
0d8b67 |
@@ -0,0 +1,49 @@
|
|
|
0d8b67 |
+/* Test printf with x86-specific non-normal long double value.
|
|
|
0d8b67 |
+ Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+ This file is part of the GNU C Library.
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+ The GNU C Library is free software; you can redistribute it and/or
|
|
|
0d8b67 |
+ modify it under the terms of the GNU Lesser General Public
|
|
|
0d8b67 |
+ License as published by the Free Software Foundation; either
|
|
|
0d8b67 |
+ version 2.1 of the License, or (at your option) any later version.
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
|
0d8b67 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
0d8b67 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
0d8b67 |
+ Lesser General Public License for more details.
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+ You should have received a copy of the GNU Lesser General Public
|
|
|
0d8b67 |
+ License along with the GNU C Library; if not, see
|
|
|
0d8b67 |
+ <https://www.gnu.org/licenses/>. */
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+#include <stdio.h>
|
|
|
0d8b67 |
+#include <string.h>
|
|
|
0d8b67 |
+#include <support/check.h>
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+/* Fill the stack with non-zero values. This makes a crash in
|
|
|
0d8b67 |
+ snprintf more likely. */
|
|
|
0d8b67 |
+static void __attribute__ ((noinline, noclone))
|
|
|
0d8b67 |
+fill_stack (void)
|
|
|
0d8b67 |
+{
|
|
|
0d8b67 |
+ char buffer[65536];
|
|
|
0d8b67 |
+ memset (buffer, 0xc0, sizeof (buffer));
|
|
|
0d8b67 |
+ asm ("" ::: "memory");
|
|
|
0d8b67 |
+}
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+static int
|
|
|
0d8b67 |
+do_test (void)
|
|
|
0d8b67 |
+{
|
|
|
0d8b67 |
+ fill_stack ();
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+ long double value;
|
|
|
0d8b67 |
+ memcpy (&value, "\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04", 10);
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+ char buf[30];
|
|
|
0d8b67 |
+ int ret = snprintf (buf, sizeof (buf), "%Lg", value);
|
|
|
0d8b67 |
+ TEST_COMPARE (ret, strlen (buf));
|
|
|
0d8b67 |
+ TEST_COMPARE_STRING (buf, "nan");
|
|
|
0d8b67 |
+ return 0;
|
|
|
0d8b67 |
+}
|
|
|
0d8b67 |
+
|
|
|
0d8b67 |
+#include <support/test-driver.c>
|