Andrew Geissler | c723b72 | 2021-01-08 16:14:09 -0600 | [diff] [blame] | 1 | From 681900d29683722b1cb0a8e565a0585846ec5a61 Mon Sep 17 00:00:00 2001 |
| 2 | From: Florian Weimer <fweimer@redhat.com> |
| 3 | Date: Tue, 22 Sep 2020 19:07:48 +0200 |
| 4 | Subject: [PATCH] x86: Harden printf against non-normal long double values (bug |
| 5 | 26649) |
| 6 | |
| 7 | The behavior of isnan/__builtin_isnan on bit patterns that do not |
| 8 | correspond to something that the CPU would produce from valid inputs |
| 9 | is currently under-defined in the toolchain. (The GCC built-in and |
| 10 | glibc disagree.) |
| 11 | |
| 12 | The isnan check in PRINTF_FP_FETCH in stdio-common/printf_fp.c |
| 13 | assumes the GCC behavior that returns true for non-normal numbers |
| 14 | which are not specified as NaN. (The glibc implementation returns |
| 15 | false for such numbers.) |
| 16 | |
| 17 | At present, passing non-normal numbers to __mpn_extract_long_double |
| 18 | causes this function to produce irregularly shaped multi-precision |
| 19 | integers, triggering undefined behavior in __printf_fp_l. |
| 20 | |
| 21 | With GCC 10 and glibc 2.32, this behavior is not visible because |
| 22 | __builtin_isnan is used, which avoids calling |
| 23 | __mpn_extract_long_double in this case. This commit updates the |
| 24 | implementation of __mpn_extract_long_double so that regularly shaped |
| 25 | multi-precision integers are produced in this case, avoiding |
| 26 | undefined behavior in __printf_fp_l. |
| 27 | |
| 28 | Upstream-Status: Backport [git://sourceware.org/git/glibc.git] |
| 29 | CVE: CVE-2020-29573 |
| 30 | Signed-off-by: Zhixiong Chi <zhixiong.chi@windriver.com> |
| 31 | --- |
| 32 | sysdeps/i386/ldbl2mpn.c | 8 ++++ |
| 33 | 1 files changed, 8 insertions(+) |
| 34 | |
| 35 | diff --git a/sysdeps/i386/ldbl2mpn.c b/sysdeps/i386/ldbl2mpn.c |
| 36 | index ec8464eef7..23afedfb67 100644 |
| 37 | --- a/sysdeps/i386/ldbl2mpn.c |
| 38 | +++ b/sysdeps/i386/ldbl2mpn.c |
| 39 | @@ -115,6 +115,14 @@ __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size, |
| 40 | && res_ptr[N - 1] == 0) |
| 41 | /* Pseudo zero. */ |
| 42 | *expt = 0; |
| 43 | + else |
| 44 | + /* Unlike other floating point formats, the most significant bit |
| 45 | + is explicit and expected to be set for normal numbers. Set it |
| 46 | + in case it is cleared in the input. Otherwise, callers will |
| 47 | + not be able to produce the expected multi-precision integer |
| 48 | + layout by shifting. */ |
| 49 | + res_ptr[N - 1] |= (mp_limb_t) 1 << (LDBL_MANT_DIG - 1 |
| 50 | + - ((N - 1) * BITS_PER_MP_LIMB)); |
| 51 | |
| 52 | return N; |
| 53 | } |
| 54 | -- |
| 55 | 2.17.0 |
| 56 | |