Hi!
We are a team of researchers studying the memory safety problem in Rust. As part of our ongoing research, we performed random testing on pxfm(version: 0.1.28) and found that the following code snippet is reported as undefined behavior by Miri:
use pxfm::*;
fn main() {
let x = f32::from_bits(0xffc0_0000); // NaN but has negative sign-bit
let _ = f_exp2m1f(x);
}
Here is part of the error message:
error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on NaN_f32 which cannot be represented in target type `i32`
--> /home/chenyl/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/num.rs:38:1
|
38 | impl_float_to_int!(f32 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `<f32 as std::convert::FloatToInt<i32>>::to_int_unchecked` at /home/chenyl/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/num.rs:30:30: 30:77
= note: inside `core::f32::<impl f32>::to_int_unchecked::<i32>` at /home/chenyl/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/num/f32.rs:1083:18: 1083:59
= note: inside `pxfm::exponents::exp2m1f::exp2m1f_gen::<pxfm::exponents::expf::GenericExpfBackend>` at /home/chenyl/test/pxfm-0.1.28/src/exponents/exp2m1f.rs:97:22: 97:50
= note: inside `pxfm::f_exp2m1f::{closure#0}::def_exp2f` at /home/chenyl/test/pxfm-0.1.28/src/exponents/exp2m1f.rs:162:21: 162:58
= note: inside `pxfm::f_exp2m1f` at /home/chenyl/test/pxfm-0.1.28/src/exponents/exp2m1f.rs:167:18: 167:22
note: inside `main`
--> src/main.rs:27:13
|
27 | let _ = f_exp2m1f(x);
| ^^^^^^^^^^^^
= note: this error originates in the macro `impl_float_to_int` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
After analyzing the miri report and the source code, I assumed that the undefined behaviour is rooted in exp2m1f_gen:
// x >= 128, or x is nan
if x.is_sign_positive() {
// x >= 128 and 2^x - 1 rounds to +inf, or x is +inf or nan
return x + f32::INFINITY;
}
In exp2m1f_gen(), although this function handles the NaN case, the input parameter may still be NaN, but the sign bit is negative, thus resulting in the undefined behaviour.
Possible fix
Explicitly process the NaN case, for example:
if x.is_nan() {
return x;
}
// x >= 128, or x is nan
if x.is_sign_positive() {
// x >= 128 and 2^x - 1 rounds to +inf, or x is +inf or nan
return x + f32::INFINITY;
}
Also, there is another API(f_exp10f) that incompletely process NaN case:
use pxfm::*;
fn main() {
let x = f32::from_bits(0x7fc0_0000);
let _ = f_exp10f(x);
}
The corresponding miri report is as follows:
error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on NaN_f64 which cannot be represented in target type `i32`
--> /home/chenyl/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/num.rs:39:1
|
39 | impl_float_to_int!(f64 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `<f64 as std::convert::FloatToInt<i32>>::to_int_unchecked` at /home/chenyl/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/num.rs:30:30: 30:77
= note: inside `core::f64::<impl f64>::to_int_unchecked::<i32>` at /home/chenyl/.rustup/toolchains/nightly-2025-12-06-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/num/f64.rs:1082:18: 1082:59
= note: inside `pxfm::exponents::exp10f::exp_b_range_reduc::<pxfm::exponents::expf::GenericExpfBackend>` at /home/chenyl/test/pxfm-0.1.28/src/exponents/exp10f.rs:95:22: 95:50
= note: inside `pxfm::exponents::exp10f::exp10f_gen::<pxfm::exponents::expf::GenericExpfBackend>` at /home/chenyl/test/pxfm-0.1.28/src/exponents/exp10f.rs:176:14: 176:44
= note: inside `pxfm::f_exp10f::{closure#0}::def_exp10f` at /home/chenyl/test/pxfm-0.1.28/src/exponents/exp10f.rs:232:21: 232:57
= note: inside `pxfm::f_exp10f` at /home/chenyl/test/pxfm-0.1.28/src/exponents/exp10f.rs:237:18: 237:22
note: inside `main`
--> src/main.rs:36:13
|
36 | let _ = f_exp10f(x);
| ^^^^^^^^^^^
= note: this error originates in the macro `impl_float_to_int` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
I think the root cause is in exp10f_gen:
// When |x| >= log10(2^128), or x is nan
if x_abs >= 0x421a209bu32 {
// When x < log10(2^-150) or nan
if x_u > 0xc2349e35u32 {
// exp(-Inf) = 0
if x.is_infinite() {
return 0.0;
}
// exp(nan) = nan
if x.is_nan() {
return x;
}
return 0.0;
}
// x >= log10(2^128) or nan
if x > 0. && (x_u >= 0x421a209bu32) {
// x is +inf or nan
return x + f32::INFINITY;
}
}
When x is 0x7fc00000, x_abs >= 0x421a209bu32 is true, x_u > 0xc2349e35u32 is false, but x > 0. is false. So x is not processed.
Possible fix
remove if x > 0. &&
We’d appreciate it if you could take a look and confirm whether this behavior indicates a real issue, or if it’s a false positive or an expected limitation of Miri.
Thank you very much for your time and for maintaining this great project!
Hi!
We are a team of researchers studying the memory safety problem in Rust. As part of our ongoing research, we performed random testing on pxfm(version: 0.1.28) and found that the following code snippet is reported as undefined behavior by Miri:
Here is part of the error message:
After analyzing the miri report and the source code, I assumed that the undefined behaviour is rooted in exp2m1f_gen:
In
exp2m1f_gen(), although this function handles the NaN case, the input parameter may still be NaN, but the sign bit is negative, thus resulting in the undefined behaviour.Possible fix
Explicitly process the NaN case, for example:
Also, there is another API(f_exp10f) that incompletely process NaN case:
The corresponding miri report is as follows:
I think the root cause is in
exp10f_gen:When x is 0x7fc00000,
x_abs >= 0x421a209bu32is true,x_u > 0xc2349e35u32is false, butx > 0.is false. So x is not processed.Possible fix
remove
if x > 0. &&We’d appreciate it if you could take a look and confirm whether this behavior indicates a real issue, or if it’s a false positive or an expected limitation of Miri.
Thank you very much for your time and for maintaining this great project!