Skip to content

Potential Undefined Behavior Reported by Miri: In exp2m1f_gen and exp10f_gen #87

@yilin0518

Description

@yilin0518

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions