Let rand = main as usize (2022)
codeandbitters.com
codeandbitters.com
I think the general philosophy is that unsafe only demarcates potentially unsound code whereas casting between different pointers isn't technically unsound even though it can cause unsoundness in unsafe code if done incorrectly. I agree with the author that casting between unrelated pointer types should probably be considered unsafe but would probably require a new edition which would mean Rust 2027 at the earliest (assuming someone is motivated enough to push it through the bureaucracy).
The broader topic of whether it is safe, or wise, to cast between pointers and integers in general is an area of active research. Ralf Jung's blog is required reading on this topic: https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html
Oddly enough, it doesn't even allow it in unsafe code, not with a normal cast. You have to use transmute. I believe this is due to concerns about targets where function and data pointers have different representations.
https://rust-lang.github.io/unsafe-code-guidelines/layout/fu...
In other words `0usize as fn ()` is insta-undefined behavior, and you can't have that in safe code.
As I understand it, unsafe pretty much says "what you are doing here may violate memory safety". Casting doesn't do that, only dereferencing. If you'd like to increase the scope to also include "things that might violate memory safety for another code block", then shouldn't compile either:
let mut foo = unsafe { int_ref as *const u32 as usize };
foo = foo + 1;
let bar = unsafe {*(foo as *const u32)}
the mutation of foo is also "unsafe" under this definition, and the compiler shouldn't let you modify pointers in any manner.Of course unsafe code can generate unsoundness in safe code. The main difference is that unsoundness would be more bounded somewhere between unsafe blocks as you’ve written which improves code review and the speed with with issues are found.
I’ll also note that the +=1 is also potentially unsound in release builds since Rust doesn’t do overflow checks at runtime (although since it presumably originates from a valid address that’s not possible in practice). It’s the one practical tradeoff Rust chose to make to allow UB in sounds code so that code wasn’t overly verbose while retaining good performance at runtime.
You're confusing it with overflow of signed integers.
No - I guess I should have been more clear but I don't think `unsafe` demarcates the boundary between sound and unsound. I think what happens in unsafe are things that potentially memory unsafe or thread unsafe. Pointer casts are not included in that - I feel that would only provide a false sense of safety.
As for false sense of safety or not, that’s a value judgement whereas we can actually derive metrics about it (eg. build a version of the compiler that require it be annotated unsafe and then investigate now illegal call sites to count how many errors per instance there turned out to be).
That said, I think `as` is generally a code smell and the one large professional Rust project I’ve worked on banned it in CI via clippy.
I don't think this is true in general. Unsafe is used pretty frequently for things that are themselves memory safe but may violate invariants which can cause memory unsoundness in other places. An example would be `std::str::from_utf8_unchecked` which is not itself memory unsafe. But various safe methods on `str` are memory safe ONLY if the str contains valid UTF8
If a pointer cast performed in safe code can cause unsoundness in unsafe code elsewhere, that's a bug in the unsafe code. All bets are off if your unsafe code is that trusting of data it receives from safe code.
This is a good argument for why pointer casting should be safe - it forces the point and pushes you to find the right abstraction. No pointer cast done in safe code should ever be able to cause unsoundness.
Converting from pointer to integer (as in the given example) cannot possibly lead to unsafe code that would not have already been unsafe with an arbitrary integer value. There's nothing unsafe about accessing an address without dereferencing it.
Casting to a pointer from an integer should probably be considered generally unsafe.
The pointer can't be assumed to be valid anyway without other guarantees. It could have been valid at some point, and then freed at another point, and is now dangling.
You'll notice that std::ptr::null and std::ptr::dangling are also safe functions. This is intentional - the language designers are telling you that you cannot rely on the fact that a piece of data is of a pointer type to trust that it's valid.
>The following language level features cannot be used in the safe subset of Rust: > Dereferencing a raw pointer. > Reading or writing a mutable or external static variable. > Accessing a field of a union, other than to assign to it. > Calling an unsafe function (including an intrinsic or foreign function). > Implementing an unsafe trait.
It also calls out the behavior in noted in this specific post in "Behavior not considered unsafe"[2]:
> Exposing randomized base addresses through pointer leaks
[1]: https://doc.rust-lang.org/reference/unsafety.html
[2]: https://doc.rust-lang.org/reference/behavior-not-considered-...
How about a new lint instead?
As long as dereferencing a raw pointer is considered unsafe, you're fine. Casting it has no actual effect at the machine level.
For example, in theory you can make pointer dereferencing safe (!), and make every operation that might create a invalid pointer unsafe. Rust chose to do this the other way around, probably out of usability reasons.
Also on Linux there's the AT_RANDOM entry in the aux vector, which provides any program with 16 random bytes.
It's not debatable at all, ASLR is a significant barrier to attacks.
Quote from a random hacking book:
> By doing so, it makes it significantly harder for an attacker to predict the location of specific processes and data, such as the stack, heap, and libraries, thereby mitigating certain types of exploits, particularly buffer overflows.
https://book.hacktricks.xyz/binary-exploitation/common-binar...
you do appear to be debating it...
But alternative facts?
that is a position statement, not a fact.
i don't care for, as is a very common theme here on HN, techbro-splaining that "it's a settled/widely known/etc. fact" when it is an opinion.
actually, that's really all the author pointed out - didn't say it wasn't valuable, just said it could be debated. thus goading, of course, a snarky response (including a "random" quote from an unnamed book) about what ASLR does and therefore there can be no debate.
i might agree with you, i might not agree you. but presenting facts, opinions, and arguments to support to reject a position sounds like a debate to me.
what i will also say is that any universally qualified statement about the value of a security hardening feature, risk of a vulnerability, etc. is always wrong until the threat model and all other engineering factors are properly weighed. what is "significant" to situation A may be "security theater" in situation B.
[0] https://lists.debian.org/debian-security-announce/2008/msg00...
Also on Windows, randomized address space layout changes only on reboot.
Can it?
let rand = if(fork() == 0) {main as usize} else {std::process::exit(0)}
(For those who wonder: I know this code has ‘some’ issues)Ran the slightly modified:
fn main() {
if fork() == 0 {
dbg!(main as usize);
} else {
dbg!(main as usize);
}
}
which got me, [src/main.rs:7:9] main as usize = 105397413561856
[src/main.rs:5:9] main as usize = 105397413561856No, that's fundamentally impossible.
Also, if you print your addrs in hex: '0x5fdbbf654600' you can see its aligned to some place. if you'd do number >> 8 it will be '0x005fdbbf6546' which might be more useful if you don't want the least significant bits to be all unset in your random value.