5 ms·
>> To fix this, we would not change anything in unsafe. > You're supposed to. To me, it just sounds like we're disagreeing on definitions. You can view it as
by Capricorn2481 11mo ago
>> To fix this, we would not change anything in unsafe.
> You're supposed to.
To me, it just sounds like we're disagreeing on definitions. You can view it as unsafe corrupting the vec, I don't disagree with that, but it's the way the safe code coordinated the unsafe code that caused that problem.
I'm happy to be shown a different way. Would you kindly show me how you would fix this in the unsafe block only?
> Some coders might implicitly count the rest of the function as trusted code that's part of enforcing the invariants of the unsafe code. So there's an implied "unsafe" that fills up the rest of the function, as far as responsibility goes
That's essentially what I'm arguing.
> but if anything outside the function with the unsafe block is part of enforcing invariants, then that is clearly wrong
What is clearly wrong? I'm not saying none of it is wrong, I'm just saying unsafe allows you to make bugs outside of it. And yes, it can happen at arbitrary depth, not just in the function containing unsafe code.
- Dylan16807 11mo ago> Would you kindly show me how you would fix this in the unsafe block only? I'd probably hold a reference to the vector or something. > That's essentially what I'm arguing. The thing is it's only the safe code in the same function as the unsafe block that you could call responsible in any reasonable sense. In the original version the main() code cannot be blamed for dropping a vector 'early', because it didn't invoke this scheme that requires the vector to be kept alive, make_slice did. It was make_slice's responsibility to guarantee the underlying allocation stays alive, and it didn't do that. > What is clearly wrong? I'm not saying none of it is wrong, I'm just saying unsafe allows you to make bugs outside of it. And yes, it can happen at arbitrary depth, not just in the function containing unsafe code. If you're putting responsibility on code in the same function, you're not really treating it as safe code. There's an unsafe{} around the entire function body that you should have written if you were going for maximum clarity. If the depth goes any higher, you screwed up. It should be impossible for safe code outside the function to use your code and cause a memory error. If it's not impossible then that's a bug in the unsafe blocks (or the function with the unsafe blocks).
- Capricorn2481 11mo ago> I'd probably hold a reference to the vector or something In a way that only changes the unsafe block? > make_slice's responsibility to guarantee the underlying allocation stays alive, and it didn't do that We çan talk about the simpler code example. How can my unsafe block make sure an allocation stays alive past the unsafe block itself? > If you're putting responsibility on code in the same function, you're not really treating it as safe code. There's an unsafe{} around the entire function body that you should have written if you were going for maximum clarity I'm not talking about what should've been done, I'm saying this is something to be aware of on projects with multiple people who may not catch something like this. People think bugs cannot happen in safe code. I just showed it happening. The bug was not in unsafe, or in the drop, it was in println.
- Dylan16807 11mo ago> How can my unsafe block make sure an allocation stays alive past the unsafe block itself? Put it in another data structure or something? Listen, if you can't make the invariant work, then you need to change the function. An unfixable unsafe is not an excuse to allow errors > I'm saying this is something to be aware of on projects with multiple people who may not catch something like this. It's good to be aware but put the blame in the right place. It's the unsafe code that's actually at fault. If you are seeing corruption, look at the unsafe code first with an adversarial mindset. > The bug was not in unsafe, or in the drop, it was in println. N. O. Any unsafe code that outsources invariant enforcement that affects memory safety to safe code has bugs. There's wiggle room on "outsources" but that's the only wiggle room.
- Capricorn2481 11mo ago> It's good to be aware but put the blame in the right place. It's the unsafe code that's actually at fault. If you are seeing corruption, look at the unsafe code first with an adversarial mindset. Yeah, that's why my first comment in this thread was "Just having unsafe in your codebase means changing code outside the unsafe block could cause UB". You can caveat that with "that's bad code", but that's the reality. > An unfixable unsafe is not an excuse to allow errors Nobody is saying that, you're just moving goalposts now. Nobody is saying you should allow errors, I'm telling you that everyone, including you, will inevitably have to change the code around the unsafe block, because that's what has to enforce memory at the end of the day. You said you don't need to look at the safe code, I'm asking how would you fix the unsafe, and you haven't. That's fine, and I'm not faulting the language for it. >> The bug was not in unsafe, or in the drop, it was in println. > N. O. Of course it is. That line becomes instructions that access memory freed by the previous line, drop(vec). That's called a dangling pointer. You remove println and the slice is now dropped immediately. You remove drop and the println will work. The vector is not "corrupted" by unsafe. That's not how computers work. We just lose guarantees from Rust when using unsafe, including in safe code. Doesn't mean there's a bug. That's the whole point of unsafe, is to be trusted by the compiler. > Any unsafe code that outsources invariant enforcement that affects memory safety to safe code has bugs. All useful unsafe code outsources invariants to safe code. If we could verify the integrity of the memory, we wouldn't be using unsafe. Now you have been interchangeably using unsafe to mean the literal blocks and the surrounding code. But here is my point: If you are saying that "unsafe code" means "just the unsafe blocks," then yes, unsafe fundamentally relies on safe code to do the right thing. But if "unsafe code" means "everything that must uphold the invariant," then unsafe can span your entire codebase. Which is true. Just the presence of unsafe in your code base means you're looking at UB anywhere in the call stack if people don't pay attention. And that's been my whole point of this thread. The presence of unsafe means everyone now has to pay attention not just to the safe block, but all safe code interacting with that data, especially in multi-threaded scenarios.