Live data from Hacker News

There is no memory safety without thread safety

ralfj.de

491–500 of 517 posts

Re: There is no memory safety without thread safety

#491
post #199

Earlier quoted context omitted.

> People talk as if "memory safety" was a PLT axiom. It's not; it's a software security term of art. It's been in usage for PLT for at least twenty years[1]. You are at least two decades late to the party. Software is memory-safe if (a) it never references a memory location outside the address space allocated by or that entity, and (b) it never executes intstruction outside code area created by the compiler and linke…

...by that definition, can a C program be memory safe as long as it doesn't have any relevant bugs, despite the choice of language? (I realize that in practice, most people are not aware of every bug that exists in their program.)

There is a huge difference between "a C program can be memory safe if it is proven to be so by an external static analysis tool" and "the Java/Rust language is memory safe except for JNI resp unsafe sections of the code".

Re: There is no memory safety without thread safety

#492
post #485
post #481

Earlier quoted context omitted.

True, "have a garbage collector" was never the formal definition, it was more "automatic memory management". But this predates the work on Rust's ownership system and while there were theories of static automatic memory management, all practical examples of automatic memory management were some form of garbage collection. If you go to the original 2009 announcement presentation for Go [1], not only is "memory-safety"…

Thanks! I added a reference to that in the blog post. Interestingly, in 2012 Rob Pike explicitly said that Go is "not purely memory safe" because "sharing is legal": https://go.dev/talks/2012/splash.slide#49 . However it is not entirely clear what he means by that (I was not able to find a recording of the talk), but it seems likely he's referring to this very issue. > "The program should not be able to derive a bad…

Hey! Cards on the table I'm not in love w/ your post, but mostly I'm curious about what discussion or outcome you were hoping for with it. Doesn't this boil down to "bad things will happen if you have data races in your code, so don't have data races in your code". Does it really matter what those bad things are?

Re: There is no memory safety without thread safety

#493
post #490
post #486

Earlier quoted context omitted.

> The C definition is that "undefined behavior" can have arbitrary concrete behavior, not that a compiler can assume it does not happen. What is the difference between those? How does a compiler that assumes UB never happens violate the requirement that UB can have arbitrary concrete behavior? If we look at a simple example like optimizing "x + y > x" (signed arithmetic, y known to be positive) to "true" -- that will…

"arbitrary concrete behavior" means that at this point anything can happen on the real machine. This implies that everything before this point has to behave according to the specification. "is impossible" is stronger, as the whole program could behave erratically. But having partial correctness is important in a lot of scenarios and this is why we want to have it and in "UB" it is the former and not "impossible". In…

> "arbitrary concrete behavior" means that at this point anything can happen on the real machine. This implies that everything before this point has to behave according to the specification. "is impossible" is stronger, as the whole program could behave erratically. But having partial correctness is important in a lot of scenarios and this is why we want to have it and in "UB" it is the former and not "impossible".

So that rules out "time-traveling UB", but it would still permit optimizing "x+y FWIW I agree we shouldn't let UB time-travel. We should say that all observable events until the point of UB must be preserved. AFAIK that is e.g. what CompCert does. But I would still describe that as "the compiler may assume that UB does not happen" (and CompCert makes use of that assumption for its optimizations), so I don't understand the distinction you are making.

> In the ISO C standard, we use "unspecified" for a non-deterministic choice among clearly specified alternatives. So this is well understood.

Except for "unspecified value" which apparently can be very different from just non-deterministically choosing any value (https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_451.htm). If "unspecified" always meant "normal non-deterministic choice", then this would clearly be a miscompilation: https://godbolt.org/z/9Gqqs7axj.

Quite a few places in the standard just say "result/behavior is unspecified", so the set of alternatives is often not very clear IMO. In particular, when it says that under some condition "the result is unspecified", and let's say the result has integer type, does that mean it non-deterministically picks some "normal" integer value, or can it be an "unspecified value" that behaves more like LLVM undef in that it is distinct from every "normal" value and can violate basic properties like "x == x"?

Re: There is no memory safety without thread safety

#494
post #335

Earlier quoted context omitted.

The problem Rust has is that it’s not enough to be memory safe, because lots of languages are memory safe and have been for decades. Hence the focus on fearless concurrency or other small-scale idioms like match in an attempt to present Rust as an overall better language compared to other safe languages like Go, which is proving to be a solid competitor and is much easier to learn and understand.

Except that Swift also has safe concurrency now. It's not just Rust. Golang is actually a very nice language for problems where you're inherently dependent on high-performance GC and concurrency, so there's no need to present it as "better" for everything. Nevertheless its concurrency model is far from foolproof when compared to e.g. Swift.

But that’s bad news for Rust adoption… Worst case for Rust is it takes just some (not all) marketshare from C and C++, because Swift, Golang, Java, Python, TypeScript, etc have cornered the rest.

Re: There is no memory safety without thread safety

#495
post #341

Earlier quoted context omitted.

This comment highlights a very important philosophical difference between the Rust community and the communities of other languages: - in other languages, it’s understood that perhaps the language is vulnerable to certain errors and one should attempt to mitigate them. But more importantly, those errors are one class of bug and bugs can happen. Set up infra to detect and recover. - in Rust the code must be safe, must…

> ... it's understood that perhaps the language is vulnerable to certain errors and one should attempt to mitigate them. But more importantly, those errors are one class of bug and bugs can happen. Set up infra to detect and recover. > in Rust the code must be safe, must be written in a certain way, must be proven correct to the largest extent possible at compile time. Only for the Safe Rust subset. Rust has the 'uns…

Well yes, only in the safe subset, but the safe subset is the alpha and omega of Rust.

Woe to those that use unsafe, for the scorn of the community shall be upon them. :)

Re: There is no memory safety without thread safety

#496
post #487

Earlier quoted context omitted.

Runtime-switched tasks cannot lead to memory unsafety unless multiple OS threads are involved, because that's the only case where torn writes are possible. And a typical configuration of Go will not be running multiple OS threads unless multiple hardware threads (aka "logical cores", "virtual processors" etc.) are available.

Thread-safety isn’t just about word tearing. It’s also about state invariants in your program. Even if all your threads are green threads on the same OS thread, you still have to guard critical sections that check-and-modify, or that temporarily break the invariants of state that is observable by other threads, when there’s anything in-between that may lead to a context switch. Otherwise, while you may be free of wor…

I think my contention is that a lot of programmers don't think about thread-safety in those terms - if I write a purely single-threaded program, thread-safety seems like the kind of thing that I don't have to care about (when in reality, the fact that various OS-provided features like signals exist, mean that all software has to deal with concurrency issues)

Re: There is no memory safety without thread safety

#497

Earlier quoted context omitted.

> But I struggle to be as productive You should calculate TCO in productivity. Can you write Python/Go etc. faster? Sure! Can you operate these in production with the same TCO as Rust? Absolutely not. Most of the time the person debugging production issues and data races is different than the one who wrote the code. This gives the illusion of productivity being better with Python/Go. After spending 20+ years around p…

TCO? Tail call optimization? But that doesn't make sense in this context. hm.

https://en.wikipedia.org/wiki/Total_cost_of_ownership

Re: There is no memory safety without thread safety

#498
post #330

Earlier quoted context omitted.

> A segfault is not guaranteed, it’s just one of the more likely possibilities. Is it? It will depend on the code, but my gut feeling is that you typically would get a few (if not lot of) unnoticed non-segfaulting issues before you get the segfaulting one that tells you straight in your face that you have a problem.

It probably depends on how exactly the corruption happens. If you overwrite a pointer with an integer value, then the integer is statistically unlikely to correspond to a valid memory address. On the other hand, if you overwrite a pointer with a pointer, or an integer with an integer, all bets are off.

> If you overwrite a pointer with an integer value, then the integer is statistically unlikely to correspond to a valid memory address

On 64-bit systems, and even then, it depends on the system’s memory layout (I think most integer values in programs are < 2³²)

Re: There is no memory safety without thread safety

#499

Earlier quoted context omitted.

I don't think that's the (only) reason Go became popular. The huge thing about Go is the runtime: it's the only language runtime available today, at least in any language with a large org behind it, that offers (a) GC, (b) fast start-up time, (c) static types, (d) fast execution, and (e) multi-threading. This is a killer combination for any team looking to write code for auto-scalable microservices, to run for exampl…

F# does that too. > .NET has similar problems s/has/had/ https://blog.washi.dev/posts/tinysharp/ The issue is that some people still fighting against the concepts ML family languages (primarily SML) introduced. Go implemented go routines and channels from CSP ( https://en.wikipedia.org/wiki/Communicating_sequential_proce... ) but dragged a lot on influence from C (understandable) into the language. I think Rust opted…

> The issue is that some people still fighting against the concepts ML family languages

To be fair, everyone was fighting against ML concepts at the time. Ruby on Rails was "in" and "doing situps" was "out". Go was built for the time it was created. It was, quite explicitly as told at its launch announcement, made to be a "dynamically typed" language with static type performance. It is unlikely it would have had a static type system at all if they knew how to achieve the same performance optimizations without a type system.

> I think Rust opted for the best combinations

But built in another time. Ruby on Rails was "out" and static typing (ML style in particular) was "in" by the time Rust finally got around to showing up to the party. Looking back, it may not seem like there was much time between the creation of Go and the creation of Rust, but on the tech scale it was created eons later. The fashion of tech can change on a dime — as captured in the humorous fable about JS having a new "must-use" framework every week.

The fashion trends will change again at some point. They always do.

Re: There is no memory safety without thread safety

#500

Earlier quoted context omitted.

> But: everybody understands that. Everybody does not understand that otherwise there would be zero of these issues in shipping code. This is the problem with the C++ crowd hoping to save their language. Maybe they'll finally figure out some --disallow-all-ub-and-be-memory-safe-and-thread-safe flag but at the moment it's still insanely trivial to make a mistake and return a reference to some value on the stack or any…

Again: if you want to make that claim about correctness bugs, that's fine, I get it. But if you're trying to claim that naive Go code has memory safety security bugs: no, that is simply not true.

I'm not understanding - if you're able to produce segfaults by, presumably, writing to memory out of bounds, what's stopping a vulnerability? Surely, if I can write past an array's bounds, then I can do a buffer overflow of some sort in some situations?
Post reply on HN