Live data from Hacker News

Memory safety is necessary, not sufficient

steveklabnik.com

31–40 of 162 posts

Re: Memory safety is necessary, not sufficient

#31

Nobody has been able explain to me what would be lost if we defined data races to yield one of the values that had been written to the memory in the past, instead of being undefined. It is not as if any optimizer can see that you are racing and delete the code path that has it.

A lot of extra CPU time wasted while caches synchronize even though the other CPU isn't running code that uses it. Most of the time the typical racey code works without locks, making everything not race means tode without a potential race still gets locks.

Above I'm treating atomics and mutexs above.

Re: Memory safety is necessary, not sufficient

#32
post #27

I think it’s worth emphasizing that the C spec’s love of undefined behavior—if you do X by accident, anything can happen—and the apparently massive amount of memory-unsafe software that has been written that will just allocate 16 bytes on the stack and then read from a file descriptor until it encounters a null byte… are examples of things that aren’t considered remotely sane or reasonable to a modern programmer or l…

uh. No. Rust unsafe gives rust behavior a lot like C. If you at all break the rather subtle rules, then essentially anything can and will happen. So for example, there was recently a thread where someone had code that checked if a value was in range to safely coerce it directly to an enum then did so. But because of eager evaluation of an argument the unsafe cast happened first. From this the compiler reasoned that t…

> As a result, I don't think it can be taken for granted that rust as a whole is an advancement in software integrity-- it may be, but it's something that ought to be formally studied. In some cases rust might be replacing memory safety bugs with an even greater number of other defects which, depending on the application, may be worse.

I’m sorry, but without any supporting evidence for this claim, this is just FUD. Everything that we’ve seen in case studies of people reimplementing stuff in Rust indicates that memory safety and logic bugs are derived compared to something like C.

Re: Memory safety is necessary, not sufficient

#33

I think it’s worth emphasizing that the C spec’s love of undefined behavior—if you do X by accident, anything can happen—and the apparently massive amount of memory-unsafe software that has been written that will just allocate 16 bytes on the stack and then read from a file descriptor until it encounters a null byte… are examples of things that aren’t considered remotely sane or reasonable to a modern programmer or l…

C gives you a level of control and responsibility not found in other languages. That's a choice, not something that is inherently worse. It may be worse for what you are doing. Most people don't value the level of control that C gives you and would rather chose another language and that is fine. But having a language available with this level of control is valuable, even if few people chose to use it. Most UB in the C standard is there for a very good reason.

Re: Memory safety is necessary, not sufficient

#34

Nobody has been able explain to me what would be lost if we defined data races to yield one of the values that had been written to the memory in the past, instead of being undefined. It is not as if any optimizer can see that you are racing and delete the code path that has it.

The optimizer assumes that a non-atomic non-volatile value written to memory stays the same, until some code that could modify it is executed. This allows a lot of obvious optimizations, like hoisting needlessly repeated computation out of loops, removal of redundant loads, and optimizations of common subexpressions and arithmetic.

If a value in memory could suddenly revert to something else, then

   if obj.x == 1 {
       print(obj.x)
   }
could print "2", and such paradoxes can lead to unsafety:

   if obj.x 
Defining that values in memory can't be trusted would mean giving up on a lot of optimizations, and require implementations to emit a lot of mostly-useless copying of values to protect them from being unexpectedly modified.

Re: Memory safety is necessary, not sufficient

#35
I'm not sure I understand what this piece is trying to say about Python memory safety. Conventionally, in software security, Python is considered a memory-safe language. The piece makes the case that Python isn't memory safe when you FFI into a C library. But neither is Rust, nor is it when you use `unsafe`. What matters in both case is how little unsafe code you end up writing.

Memory safety is a software security concern. You can squint and make it about reliability or resiliency, but the reason we talk about memory safety is (to a first approximation) browser vulnerabilities.

The piece goes on to discuss data races. I'm a little keyed up on software security essays that bring data race safety into the discussion. I have a hard time not reading them as shibboleths for "Rust is the only safe language", which is manifestly false.

The vulnerabilities endemic to memory-safe languages (logic and higher-level vulnerabilities like SQLI, metacharacter quoting, filesystem traversal, and cryptography bugs) are common both to languages like Python and Java and also to Rust --- the only super-common class of vulnerability endemic to languages like Python and Java that Rust avoids is deserialization (you avoid deserialization vulnerabilities by not building hypercapable serialization formats).

Data races are a common source of reliability bugs. They're a meaningful software engineering concern. In exotic scenarios (userspace-sandboxed attacker-controlled code), they can constitute practical security vulnerabilities. But in the main, data races have not empirically proved out as a source of exploited vulnerabilities. If you have a fixed budget to transition from a C codebase that would allow you to migrate to Python now, or if you saved up, to Rust next year, and all you care about is security, then ceteris paribus you should do the Python thing. The data races aren't going to burn you.

Re: Memory safety is necessary, not sufficient

#36
post #35

I'm not sure I understand what this piece is trying to say about Python memory safety. Conventionally, in software security, Python is considered a memory-safe language. The piece makes the case that Python isn't memory safe when you FFI into a C library. But neither is Rust, nor is it when you use `unsafe`. What matters in both case is how little unsafe code you end up writing. Memory safety is a software security c…

Are you grouping kernel exploits in with user space sandboxes? Lots of local roots come from data races which I would not call exotic.

And there's always https://portswigger.net/research/smashing-the-state-machine for web stuff.

Re: Memory safety is necessary, not sufficient

#37
post #27

I think it’s worth emphasizing that the C spec’s love of undefined behavior—if you do X by accident, anything can happen—and the apparently massive amount of memory-unsafe software that has been written that will just allocate 16 bytes on the stack and then read from a file descriptor until it encounters a null byte… are examples of things that aren’t considered remotely sane or reasonable to a modern programmer or l…

uh. No. Rust unsafe gives rust behavior a lot like C. If you at all break the rather subtle rules, then essentially anything can and will happen. So for example, there was recently a thread where someone had code that checked if a value was in range to safely coerce it directly to an enum then did so. But because of eager evaluation of an argument the unsafe cast happened first. From this the compiler reasoned that t…

> If you at all break the rather subtle rules, then essentially anything can and will happen.

If by subtle rules you mean your invariants, that is missing fundamental assumptions.

It's akin to making a building without foundation and load bearing structures.

> So for example, there was recently a thread where someone had code that checked if a value was in range to safely coerce it directly to an enum then did so. But because of eager evaluation of an argument the unsafe cast happened first

You mean this: https://notgull.net/cautionary-unsafe-tale/

However note the UB goes away if you never use any unsafe code. Or if you expand your unsafe to encompas some safe code.

Issue it had was safe code was invalidating the invariants unsafe code was relying on. Iirc alignment.

Re: Memory safety is necessary, not sufficient

#38
post #6

I think it's underappreciated that Rust's `unsafe{}` doesn't exist in isolation. Rust has facilities for building safe abstractions on top of it, and has a culture of taking this abstraction layer seriously. Danger of unsafe features and FFI is usually conditional — you can use a pointer only until some point, or only on a single thread, etc. A use of unsafe in Rust doesn't become "be careful!" kryptonite spreading a…

> They don't have "you can use it only within this scope" (they may use a closure/callback to give access to an unsafe object, but these aren't hermetic, so that's a convention not a guarantee).

You can use the same trick as Haskell's STRef to prevent reusing a "leaked" unsafe object, although it's cumbersome enough in Java that you may not want to.

Re: Memory safety is necessary, not sufficient

#39
post #35

I'm not sure I understand what this piece is trying to say about Python memory safety. Conventionally, in software security, Python is considered a memory-safe language. The piece makes the case that Python isn't memory safe when you FFI into a C library. But neither is Rust, nor is it when you use `unsafe`. What matters in both case is how little unsafe code you end up writing. Memory safety is a software security c…

Are you grouping kernel exploits in with user space sandboxes? Lots of local roots come from data races which I would not call exotic. And there's always https://portswigger.net/research/smashing-the-state-machine for web stuff.

Right, these aren't data races; they're distributed systems races, more akin to tempfile races from the 1990s than to memory corruption.

Re: Memory safety is necessary, not sufficient

#40
post #37
post #27

Earlier quoted context omitted.

uh. No. Rust unsafe gives rust behavior a lot like C. If you at all break the rather subtle rules, then essentially anything can and will happen. So for example, there was recently a thread where someone had code that checked if a value was in range to safely coerce it directly to an enum then did so. But because of eager evaluation of an argument the unsafe cast happened first. From this the compiler reasoned that t…

> If you at all break the rather subtle rules, then essentially anything can and will happen. If by subtle rules you mean your invariants, that is missing fundamental assumptions. It's akin to making a building without foundation and load bearing structures. > So for example, there was recently a thread where someone had code that checked if a value was in range to safely coerce it directly to an enum then did so. Bu…

> If by subtle rules you mean your invariants, that is missing fundamental assumptions.

> It's akin to making a building without foundation and load bearing structures.

That's exactly how C approaches UB too.

> However note the UB goes away if you never use any unsafe code. Or if you expand your unsafe to encompas some safe code.

Right. The problem is that's untenable; any nontrivial program will have unsafe somewhere, and unsafe can cause failures arbitrarily far away from the incorrect code. The whole point of the article is that you need to be able to draw an actual boundary between the unsafe parts and the safe parts of your program and review the unsafe parts in isolation. If Rust doesn't give you more and better support in doing that than C does, then it's not really making a difference.

Post reply on HN