Avoid UB when checking if float fits in int32
This commit is contained in:
parent
2f51cbc4e6
commit
0068db8a11
3 changed files with 48 additions and 23 deletions
41
quickjs.c
41
quickjs.c
|
@ -10797,6 +10797,7 @@ static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
|
|||
ret = v << ((e - 1023) - 52);
|
||||
/* take the sign into account */
|
||||
if (u.u64 >> 63)
|
||||
if (ret != INT64_MIN)
|
||||
ret = -ret;
|
||||
} else {
|
||||
ret = 0; /* also handles NaN and +inf */
|
||||
|
@ -10872,6 +10873,7 @@ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
|
|||
ret = v >> 32;
|
||||
/* take the sign into account */
|
||||
if (u.u64 >> 63)
|
||||
if (ret != INT32_MIN)
|
||||
ret = -ret;
|
||||
} else {
|
||||
ret = 0; /* also handles NaN and +inf */
|
||||
|
@ -11968,6 +11970,45 @@ static double js_pow(double a, double b)
|
|||
}
|
||||
}
|
||||
|
||||
// Special care is taken to not invoke UB when checking if the result fits
|
||||
// in an int32_t. Leans on the fact that the input is integral if the lower
|
||||
// 52 bits of the equation 2**e * (f + 2**52) are zero.
|
||||
static BOOL float_is_int32(double d)
|
||||
{
|
||||
uint64_t u, m, e, f;
|
||||
JSFloat64Union t;
|
||||
|
||||
t.d = d;
|
||||
u = t.u64;
|
||||
|
||||
// special case -0
|
||||
m = 1ull << 63;
|
||||
if (u == m)
|
||||
return FALSE;
|
||||
|
||||
e = (u >> 52) & 0x7FF;
|
||||
if (e > 0)
|
||||
e -= 1023;
|
||||
|
||||
// too large, nan or inf?
|
||||
if (e > 30)
|
||||
return FALSE;
|
||||
|
||||
// fractional or subnormal if low bits are non-zero
|
||||
f = 0xFFFFFFFFFFFFFull & u;
|
||||
m = 0xFFFFFFFFFFFFFull >> e;
|
||||
return 0 == (f & m);
|
||||
}
|
||||
|
||||
JSValue JS_NewFloat64(JSContext *ctx, double d)
|
||||
{
|
||||
if (float_is_int32(d)) {
|
||||
return JS_MKVAL(JS_TAG_INT, (int32_t)d);
|
||||
} else {
|
||||
return __JS_NewFloat64(ctx, d);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BIGNUM
|
||||
|
||||
JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
|
||||
|
|
22
quickjs.h
22
quickjs.h
|
@ -539,30 +539,10 @@ static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val)
|
|||
return v;
|
||||
}
|
||||
|
||||
JSValue JS_NewFloat64(JSContext *ctx, double d);
|
||||
JSValue JS_NewBigInt64(JSContext *ctx, int64_t v);
|
||||
JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);
|
||||
|
||||
static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
|
||||
{
|
||||
JSValue v;
|
||||
int32_t val;
|
||||
union {
|
||||
double d;
|
||||
uint64_t u;
|
||||
} u, t;
|
||||
u.d = d;
|
||||
val = (int32_t)d;
|
||||
t.d = val;
|
||||
/* -0 cannot be represented as integer, so we compare the bit
|
||||
representation */
|
||||
if (u.u == t.u) {
|
||||
v = JS_MKVAL(JS_TAG_INT, val);
|
||||
} else {
|
||||
v = __JS_NewFloat64(ctx, d);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline JS_BOOL JS_IsNumber(JSValueConst v)
|
||||
{
|
||||
int tag = JS_VALUE_GET_TAG(v);
|
||||
|
|
|
@ -332,6 +332,10 @@ function test_number()
|
|||
assert(+" 123 ", 123);
|
||||
assert(+"0b111", 7);
|
||||
assert(+"0o123", 83);
|
||||
assert(parseFloat("2147483647"), 2147483647);
|
||||
assert(parseFloat("2147483648"), 2147483648);
|
||||
assert(parseFloat("-2147483647"), -2147483647);
|
||||
assert(parseFloat("-2147483648"), -2147483648);
|
||||
assert(parseFloat("0x1234"), 0);
|
||||
assert(parseFloat("Infinity"), Infinity);
|
||||
assert(parseFloat("-Infinity"), -Infinity);
|
||||
|
|
Loading…
Reference in a new issue