From e75a1bbc233bf112b9eb98a20ad4bdf9bc14b2cf Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Tue, 23 Apr 2024 12:18:25 -0600 Subject: [PATCH] pow,powf(3),__ieee754_rem_pio2(f): Avoid negative integer left shift UB A compiler clever enough to know that z is positive with a non-zero biased exponent could, for example, optimize away the scalbnf(z,n) in pow() because behavior for left shift of negative values is undefined. `n` is negative when y*log2(|x|) < -0.5. i.e. |x^y| < sqrt(0.5) The intended behavior for operator<< in this code is to shift the two's complement representation of the first operand. In the pow() functions, the result is added to the IEEE 754 exponent of z = 2^y'. n may be negative enough to underflow the biased IEEE 754 exponent below zero, which is manifested in the sign bit of j (which would correspond to the IEEE 754 sign bit). The conversion from uint32_t to int32_t for out-of-int32_t-range values is implementation defined. The assumed behavior of interpreting the uint32_t value as a two's complement representation of a signed value is already assumed in many parts of the code, such as uses of GET_FLOAT_WORD() with signed integers. This code passes all the current tests, and makes some out of tree fuzzing tests pass again rather than hit UB (detailed in the commentary of the pull request). Signed-off-by: Karl Tomlinson Reviewed by: imp, steve kargl, dim Pull Request: https://github.com/freebsd/freebsd-src/pull/1137 --- lib/msun/src/e_pow.c | 6 +++++- lib/msun/src/e_powf.c | 6 +++++- lib/msun/src/e_rem_pio2.c | 2 +- lib/msun/src/e_rem_pio2f.c | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/msun/src/e_pow.c b/lib/msun/src/e_pow.c index 85afacdbb6a..85d1d551b6f 100644 --- a/lib/msun/src/e_pow.c +++ b/lib/msun/src/e_pow.c @@ -299,7 +299,11 @@ pow(double x, double y) r = (z*t1)/(t1-two)-(w+z*w); z = one-(r-z); GET_HIGH_WORD(j,z); - j += (n<<20); + /* + * sign bit of z is 0. + * sign bit of j will indicate sign of 0x3ff-biased exponent. + */ + j += (int32_t)((u_int32_t)n<<20); if((j>>20)<=0) z = scalbn(z,n); /* subnormal output */ else SET_HIGH_WORD(z,j); return s*z; diff --git a/lib/msun/src/e_powf.c b/lib/msun/src/e_powf.c index 2e7c37542a7..9f670bcd1ca 100644 --- a/lib/msun/src/e_powf.c +++ b/lib/msun/src/e_powf.c @@ -242,7 +242,11 @@ powf(float x, float y) r = (z*t1)/(t1-two)-(w+z*w); z = one-(r-z); GET_FLOAT_WORD(j,z); - j += (n<<23); + /* + * sign bit of z is 0. + * sign bit of j will indicate sign of 0x7f-biased exponent. + */ + j += (int32_t)((u_int32_t)n<<23); if((j>>23)<=0) z = scalbnf(z,n); /* subnormal output */ else SET_FLOAT_WORD(z,j); return sn*z; diff --git a/lib/msun/src/e_rem_pio2.c b/lib/msun/src/e_rem_pio2.c index 76ae8050d36..8a4006498a1 100644 --- a/lib/msun/src/e_rem_pio2.c +++ b/lib/msun/src/e_rem_pio2.c @@ -162,7 +162,7 @@ __ieee754_rem_pio2(double x, double *y) /* set z = scalbn(|x|,ilogb(x)-23) */ GET_LOW_WORD(low,x); e0 = (ix>>20)-1046; /* e0 = ilogb(z)-23; */ - INSERT_WORDS(z, ix - ((int32_t)(e0<<20)), low); + INSERT_WORDS(z, ix - ((int32_t)((u_int32_t)e0<<20)), low); for(i=0;i<2;i++) { tx[i] = (double)((int32_t)(z)); z = (z-tx[i])*two24; diff --git a/lib/msun/src/e_rem_pio2f.c b/lib/msun/src/e_rem_pio2f.c index 744060f1801..d9a3e2e34c4 100644 --- a/lib/msun/src/e_rem_pio2f.c +++ b/lib/msun/src/e_rem_pio2f.c @@ -67,7 +67,7 @@ __ieee754_rem_pio2f(float x, double *y) } /* set z = scalbn(|x|,ilogb(|x|)-23) */ e0 = (ix>>23)-150; /* e0 = ilogb(|x|)-23; */ - SET_FLOAT_WORD(z, ix - ((int32_t)(e0<<23))); + SET_FLOAT_WORD(z, ix - ((int32_t)((u_int32_t)e0<<23))); tx[0] = z; n = __kernel_rem_pio2(tx,ty,e0,1,0); if(hx<0) {*y = -ty[0]; return -n;}