|
|
Subscribe / Log in / New account

Narrowing conversion

Narrowing conversion

Posted Jul 21, 2021 23:05 UTC (Wed) by tialaramex (subscriber, #21167)
Parent article: The Sequoia seq_file vulnerability

> Clearly the integer conversion at the heart of the exploit needed fixing; one wonders how many other size_t-to-int problems of that sort still linger in the kernel.

This is called an implicit narrowing conversion. Many modern languages outright forbid this, requiring that the programmer *explicitly* choose to perform a narrowing conversion or (perhaps better) even requiring that they explicitly decide what happens when the big thing won't fit in the small thing.

You can imagine that in kernel code, some sort of error, or panic should likely be the choice, if the programmer did not instead decide to approach the problem in a different way to avoid a narrowing conversion. Apparently out-of-box Windows MSVC programs warn if you do an implicit narrowing conversion but alas GCC does not, even under -Wall, although -Wconversion exists.

Since it's the topic du jour, in Rust you can't have implicit narrowing conversion, but it does provide an explicit conversion that can silently lose information in narrowing, programmers might write:

let signedInt = sixtyFourBitNumber as s32;

Whereas perhaps they ought to write:

let signedInt: s32 = sixtyFourBitNumber.try_into().unwrap(); // panic if it doesn't fit

or indeed:

let signedInt: s32 = sixtyFourBitNumber.try_into()? // only compiles if the function we do this in has a suitable error return

let signedInt: s32 = sixtyFourBitNumber.try_into().unwrap_or(SAFE_DEFAULT); // we're confident SAFE_DEFAULT is fine


to post comments

Narrowing conversion

Posted Jul 22, 2021 0:08 UTC (Thu) by roc (subscriber, #30627) [Link] (9 responses)

"as" is one of Rust's warts. You shouldn't have the same syntax for both lossy and non-lossy conversions.

Narrowing conversion

Posted Jul 22, 2021 0:41 UTC (Thu) by Gaelan (guest, #145108) [Link] (5 responses)

For non-lossy conversions, there's the alternate syntax u64::from(some_u32), but it's still not great.

Narrowing conversion

Posted Jul 22, 2021 1:21 UTC (Thu) by roc (subscriber, #30627) [Link] (3 responses)

From and TryFrom let you express infallible and fallible scalar conversions. There probably should be a third trait+method to explicitly request a truncating/sign-modifying conversion. But "as" for scalar conversions is a footgun.

I'm not sure what the best way to proceed from here is. One option would be to introduce that trait+method for truncating conversions, and deprecate or ban lossy "as" scalar conversions in a future edition. But obviously a lot of existing code would be affected.

Narrowing conversion

Posted Jul 23, 2021 0:47 UTC (Fri) by khim (subscriber, #9252) [Link] (2 responses)

> But obviously a lot of existing code would be affected.

Isn't it something Rust editions are supposed to cover?

Narrowing conversion

Posted Jul 23, 2021 7:50 UTC (Fri) by Fowl (subscriber, #65667) [Link] (1 responses)

The code still would want to be upgraded to a newer edition at some point.

Narrowing conversion

Posted Jul 23, 2021 9:54 UTC (Fri) by khim (subscriber, #9252) [Link]

Not really. In Rust, unlike in C++, editions are supposed to coexist. You can write one crate in Rust 2015, another in Rust 2018, and third in Rust 2021.

Sure, if you are actively developing your code then you would probably want to upgrade it, but there are no pressure to do that ASAP.

Narrowing conversion

Posted Jul 23, 2021 16:40 UTC (Fri) by jezuch (subscriber, #52988) [Link]

I guess it's "not great" by design, like the new-style casts in C++ are ugly by design to discourage casting in general.

FWIW, AFAICT Rust doesn't have implicit *widening* conversion either. Turns out, it too often turns into a footgun.

Narrowing conversion

Posted Jul 22, 2021 8:30 UTC (Thu) by Wol (subscriber, #4433) [Link] (2 responses)

> You shouldn't have the same syntax for both lossy and non-lossy conversions.

If it is (clearly) changing the length of the storage field - as in between int32 and int64, why not? Only an extreme novice would think that you could stuff an int64 into an int32 and not run at least the risk of truncation. Okay if it's not obvious the target is an int32 then it's a more fuzzy issue.

Cheers,
Wol

Narrowing conversion

Posted Jul 22, 2021 9:53 UTC (Thu) by atnot (subscriber, #124910) [Link]

I think just deferring to experience isn't very useful here. Adding explicit conversions quickly becomes muscle memory that isn't given the thought it should be given. You might just not consider whether it is lossy or not in that moment. Since languages should make it easier to do the right thing than the wrong thing, requiring different mechanisms for lossy and lossless conversions would give a crucial opportunity to think about how it should really be handled.

Narrowing conversion

Posted Jul 22, 2021 11:56 UTC (Thu) by roc (subscriber, #30627) [Link]

When you read "foobar as u32" it's not obvious whether that is (potentially) lossy or not. OK, with rust-analyzer I just have to hover "foobar" to see what type it is, but it would be so simple to just require different syntax for lossy vs non-lossy conversions.

Narrowing conversion

Posted Jul 28, 2021 16:33 UTC (Wed) by sandsmark (guest, #62172) [Link] (1 responses)

> Since it's the topic du jour, in Rust you can't have implicit narrowing conversion, but it does provide an explicit conversion that can silently lose information in narrowing, programmers might write:

Again proving that the safety of Rust is just a subset of C++ with -Werror -Weverything. :-P

(And I'd say C++ discourages it even more because casting is uglier/more verbose, but people tend to just cast everything just to get rid of warnings/errors so you basically end up with implicit casting anyways.)

Narrowing conversion

Posted Jul 28, 2021 17:07 UTC (Wed) by mathstuf (subscriber, #69389) [Link]

That's…quite a narrow view of what Rust provides. I'm feeling forgetful…what warning flag tells me when my code has data races in C++? When I write `return make_string().c_str();`?


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds