NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/58347: libc ldexp has signed integer overflow UB



>Number:         58347
>Category:       lib
>Synopsis:       libc ldexp has signed integer overflow UB
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Jun 17 15:25:00 +0000 2024
>Originator:     Taylor R Campbell
>Release:        current, 10, 9, ...
>Organization:
The NetLdexp Foundefinedbehaviation
>Environment:
>Description:
ldexp makes a feeble attempt to detect overflow, but it falls flat on its face in modern C where singed overflow is undefined behaviour:

    116 	/*
    117 	 * u.v is now normalized and oldexp has been adjusted if necessary.
    118 	 * Calculate the new exponent and check for underflow and overflow.
    119 	 */
    120 	newexp = oldexp + expon;
    121 
    122 	if (newexp >= DBL_EXP_INFNAN ||
    123 	    (oldexp >= 0 && expon >= DBL_EXP_INFNAN)) {
    124 		/*
    125 		 * The result overflowed; return +/-Inf.
    126 		 */
    127 		return overflow(val);

https://nxr.netbsd.org/xref/src/lib/libc/compat/gen/compat_ldexp_ieee754.c?r=1.8#116

This leads to t_ldexp failures on various architectures:

https://releng.netbsd.org/b5reports/amd64/2024/2024.06.16.19.21.46/test.html#lib_libm_t_ldexp_ldexp_overflow
https://releng.netbsd.org/b5reports/i386/2024/2024.06.16.19.21.46/test.html#lib_libm_t_ldexp_ldexp_overflow
https://releng.netbsd.org/b5reports/sparc/2024/2024.06.16.00.12.33/test.html#lib_libm_t_ldexp_ldexp_overflow
>How-To-Repeat:
1. build system with gcc12
2. atf-run /usr/tests/lib/libm/t_ldexp
>Fix:
Check for overflow safely by testing whether expon >= DBL_EXP_INFNAN - oldexp first _before_ adding oldexp + expon:

diff --git a/lib/libc/compat/gen/compat_ldexp_ieee754.c b/lib/libc/compat/gen/compat_ldexp_ieee754.c
index 8c8cb49a935c..f507a8cdd280 100644
--- a/lib/libc/compat/gen/compat_ldexp_ieee754.c
+++ b/lib/libc/compat/gen/compat_ldexp_ieee754.c
@@ -115,17 +115,31 @@ ldexp(double val, int expon)
 
 	/*
 	 * u.v is now normalized and oldexp has been adjusted if necessary.
-	 * Calculate the new exponent and check for underflow and overflow.
+	 * We have
+	 *
+	 *	0 <= oldexp <= DBL_EXP_INFNAN,
+	 *
+	 * but
+	 *
+	 *	INT_MIN <= expon <= INT_MAX.
+	 *
+	 * Check for underflow and overflow, and if none, calculate the
+	 * new exponent.
 	 */
-	newexp = oldexp + expon;
-
-	if (newexp >= DBL_EXP_INFNAN ||
-	    (oldexp >= 0 && expon >= DBL_EXP_INFNAN)) {
+	if (expon >= DBL_EXP_INFNAN - oldexp) {
 		/*
 		 * The result overflowed; return +/-Inf.
 		 */
 		return overflow(val);
-	} else if (newexp <= 0) {
+	}
+
+	/*
+	 * We now have INT_MIN <= oldexp + expon <= DBL_EXP_INFNAN <= INT_MAX,
+	 * so the arithmetic is safe.
+	 */
+	newexp = oldexp + expon;
+
+	if (newexp <= 0) {
 		/*
 		 * The output number is either denormal or underflows (see
 		 * comments in machine/ieee.h).



Home | Main Index | Thread Index | Old Index