|
| 1 | ++++ |
| 2 | +path = "2025/11/27/warning-about-fn-pointer-casts" |
| 3 | +title = "New warning: casting function pointer" |
| 4 | +authors = ["Guillaume Gomez"] |
| 5 | ++++ |
| 6 | + |
| 7 | +# New warning: casting function pointer |
| 8 | + |
| 9 | +We recently added a new warning in the rust compiler when a function pointer is cast as a pointer in [#141470](https://github.com/rust-lang/rust/pull/141470). Let's dive into the reasons why we added it and what we plan to do next. |
| 10 | + |
| 11 | +## Why? |
| 12 | + |
| 13 | +In rust, it's possible to do: |
| 14 | + |
| 15 | +```rust |
| 16 | +let x = u16::max as usize; |
| 17 | +``` |
| 18 | + |
| 19 | +Which is fine, except that `u16::max` is a function, not a constant (the constant is named `u16::MAX`). And it can become catastrophic in cases like: |
| 20 | + |
| 21 | +```rust |
| 22 | +let x = [0; u16::max as usize]; |
| 23 | +``` |
| 24 | + |
| 25 | +Or: |
| 26 | + |
| 27 | +```rust |
| 28 | +if x.len() > u16::max as usize { |
| 29 | + // ... |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +We took the second example in particular as it was the root issue of [CVE-2025-1014](https://www.cve.org/CVERecord?id=CVE-2025-1014), which happened in the Firefox web browser (reported [here](https://bugzilla.mozilla.org/show_bug.cgi?id=1940804)). |
| 34 | + |
| 35 | +## Suggested fix |
| 36 | + |
| 37 | +Now let's take a look at what the suggested fix looks like: |
| 38 | + |
| 39 | +```rust |
| 40 | +let x = u16::max as usize; |
| 41 | +``` |
| 42 | + |
| 43 | +will suggest: |
| 44 | + |
| 45 | +```text |
| 46 | +warning: direct cast of function item into an integer |
| 47 | + --> src/main.rs:3:18 |
| 48 | + | |
| 49 | +3 | let x = u16::max as usize; |
| 50 | + | ^^^^^^^^ |
| 51 | + | |
| 52 | + = note: `#[warn(function_casts_as_integer)]` on by default |
| 53 | +help: first cast to a pointer `as *const ()` |
| 54 | + | |
| 55 | +3 | let x = u16::max as *const () as usize; |
| 56 | + | ++++++++++++ |
| 57 | +``` |
| 58 | + |
| 59 | +So the "fixed" code would look like this: |
| 60 | + |
| 61 | +```rust |
| 62 | +let x = u16::max as *const () as usize; |
| 63 | +``` |
| 64 | + |
| 65 | +Having the `as * const ()` part to be present in the code will force code readers to wonder why such a cast is there for what appears to be an integer cast. In short: to make it stand out so it doesn't go unnoticed. |
| 66 | + |
| 67 | +However, we're not completely satisfied with this because, although it attract readers attention on this part of the code, it's not really clear what's happening just from reading it. An alternative was to force having the function declaration in the cast: |
| 68 | + |
| 69 | +```rust |
| 70 | +let x = u16::max as *const fn() -> u16 as usize; |
| 71 | +``` |
| 72 | + |
| 73 | +However, we are planning to have another approach in the future to cast function pointers to integers so until ten, we decided to keep the simpler approach. |
| 74 | + |
| 75 | +## What's coming next |
| 76 | + |
| 77 | +We're planning to create two new traits: `FnPtr` and `FnStatic`. The one we're interested into here is `FnPtr` because it would add `as_ptr`, directly callable on functions/methods. So the previous suggested code would become: |
| 78 | + |
| 79 | +```rust |
| 80 | +let x = u16::max.as_ptr() as usize; |
| 81 | +``` |
| 82 | + |
| 83 | +So once this new trait has been implemented and stabilized, this is what the warning will suggest. |
| 84 | + |
| 85 | +You can follow progress of these new traits implementations by taking a look at its [tracking issue](https://github.com/rust-lang/rust/issues/148768). |
| 86 | + |
| 87 | +## A bit of history |
| 88 | + |
| 89 | +Because it's always interesting how something comes to be, we added this section. |
| 90 | + |
| 91 | +It all started from a `clippy` lint named `confusing_method_to_numeric_cast` (implemented in [this PR](https://github.com/rust-lang/rust-clippy/pull/13979)) which checked special cases like: |
| 92 | + |
| 93 | +```rust |
| 94 | +let _ = u16::max as usize; |
| 95 | +``` |
| 96 | + |
| 97 | +It was suggesting the constant equivalent. However, when we realizes that it might need to grow a lot when we added cases like `integer::max_value` or `integer::maximum`, we decided that having something more general directly in the rust compiler might be preferable. |
0 commit comments