5 ms·
Good definition. I've seen Go beginners trying to append to a slice from multiple goroutines. It works as well as calling push_back on the same vector from mult
by kccqzy 2y ago
Good definition. I've seen Go beginners trying to append to a slice from multiple goroutines. It works as well as calling push_back on the same vector from multiple threads in C++. It can easily corrupt GC state and lead to segfaults. The beginner didn't use any advanced trickery or the unsafe package. Therefore Go is not a memory safe language.
- sshine 2y ago> Therefore Go is not a memory safe language. Interesting. To quote the NSA [1], "Some examples of memory safe languages are Python, Java, C#, Go, Delphi/Object Pascal, Swift, Ruby, Rust, and Ada. Memory safe languages provide differing degrees of memory usage protections, so available code hardening defenses, such as compiler options, tool analysis, and operating system configurations, should be used for their protections as well." The narrow definition of memory safety here is: Go has garbage collection, so you won't have memory leaks or use-after-free. Go is powerful enough that beginners can cause segfaults by accidentally abusing internals, okay. I'm not sure this is a very redeeming property of Go: Being able to crash the GC, without the flexibility of manual memory management. But I'm not sure I'd categorize it as "not memory safe" for the same reason C/C++ aren't (a trade-off). Because I don't believe that you can generally leverage this for the kinds of memory exploits made in C/C++. I recall that some ML dialects (Standard ML and OCaml) have a library function Obj.magic : 'a -> 'b which escapes the type system. Using this can easily cause segfaults. Does that mean Standard ML and OCaml are not memory safe? Generally, no, they're extremely safe if you avoid that feature, which is most likely. This is arguably less safe than Go, since you most likely won't accidentally run that function. [1]: https://media.defense.gov/2022/Nov/10/2003112742/-1/-1/0/CSI_SOFTWARE_MEMORY_SAFETY.PDF https://media.defense.gov/2022/Nov/10/2003112742/-1/-1/0/CSI...
- kccqzy 2y agoI'm trying to provide some commentary to OP's original term of "ordinary code" three comments above. While this term is inherently ambiguous and subjective, my personal opinion is that appending to slices simultaneously from multiple goroutines count as "ordinary code" but Obj.magic does not.
- tptacek 2y agoYes. And: you will run into correctness bugs quickly if you mutate shared references in Go code. It's only my contention that you won't create a security vulnerability, in the colloquial understanding of the term (ie: a panic doesn't count).
- lll-o-lll 2y agoIf I can induce a race that corrupts a data structure so that it leaks data back to me that I shouldn’t have access to, does that count?
- melodyogonna 2y agoHow can you do that?
- tsimionescu 2y agoYou can, though it's much harder than in C or C++ or unsafe Rust for this to be exploitable. A data race on an interface value can give you a corrupted interface value, overwriting the vtable with struct contents. This can happen to lead to arbitrary code execution if you're unlucky enough, though in most cases it would be a SIGSEGV. It's also very hard for an attacker to craft a payload that can be guaranteed to reach this, though with a microservixe architecture with automatic restarts of failed services, they might get a lot of tries.
- kaba0 2y agoI mean, a very serious security vulnerability is/was row hammering, where an attacker was waiting on flipping a bit they have no access to by continuously flipping neighboring ones. Compared to that a race condition is "trivial" to exploit.
- deleted 2y ago[deleted]
- consteval 2y agoI don't think that's ordinary code. In any language, if you don't use a thread safe container and mutate it from multiple threads you'll get problems. This isn't an issue of memory safety but rather thread safety. You have to check the documentation for thread safe operations or otherwise use a lock. This goes for C#, Java, Go, you name it - the one singular exception being Rust. But, even Rust does not fully get around it.
- capitol_ 2y agoCould you share more of your thoughts on why that kind of memory corruption wouldn't be exploitable? Do go have something in place that prevents it?
- Cthulhu_ 2y agoTo add to Go being memory safe, it automatically blanks/zeroes memory, unlike C.
- everybodyknows 2y ago> corrupt GC state I understand this to mean the runtime's internal state, not visible to user code. If so, in general we should expect almost any sort of crash mode to be possible. Seems fair enough to call this "memory-unsafe".
- tptacek 2y agoYou'll be using an idiosyncratic definition the rest of the industry does not use, but you do you. What I think is happening here is another instance of a pattern that recurs all the time in communities like this: a term of art was created, "memory safety", to address the concept of languages that don't have buffer overflows, integer overflows, use-after-frees, double frees, controllable uninitialized pointers, and all the other memory lifecycle vulnerabilities. People unfamiliar with the state of the art heard the term, liked it, and have axiomatically derived their own definition for it. They like their definition better, and are not open to the idea that the term exists to serve a purpose orthogonal to their arguments. Another recent instance of the same phenomenon: "zero trust". Just as happened in the Zero Trust Wars of 2022, people, hearing the industry definition and intent of the term, scramble to reconcile their axiomatic definition with the state of the art, convincing themselves they were right all along. The problem they have in this particular argument is: where are the vulnerabilities? Go is not a niche language. It is a high-profile target and has been for over a decade. I saw Go security talks at OWASP Chicago(!) in 2012(!). People have all sorts of hypotheses about how a memory corruption vulnerability --- not "memory corruption", but a vulnerability stemming from it, implying valuable attacker control over the result of whatever bad thing happened --- might sneak into a Go program. Practitioners hear those axiomatic arguments, try to reconcile them with empirical reality, and: it just doesn't hold up. Just for whatever it's worth to hear this, if at Black Hat 2025 someone does to Go what James Kettle does to web frameworks ever year and introduces a widespread repeatable pattern of memory exploitability in Go race conditions, about half of my message board psyche will be really irritated (I'll have been wrong!), but the other half of my message board psyche will be fucking thrilled (there will be so much to talk about!) and all of my vulnerability researcher psyche will be doing somersaults (there will be so many new targets to hit!). On net, I'm rooting for myself being wrong. But if I had to bet: we're not going to see that talk, not at BH 2025, or 2026, or 2027. I'm probably not wrong about this.
- Thaxll 2y agoAppending from multiple goroutine to an in un-synchronized slice is "memory safe", it's completely different from c/c++. It behave exactly like Java or C# which are also memory safe.
- kaba0 2y agoNot at all. Java or C# can end up in a logical bug from that, but they will never corrupt their runtime. So in java you can just try-catch whatever bad stuff happens there, and go on afterwards. Go programs can literally segfault from a data race. That's no memory safety.
- tsimionescu 2y agoI'm not sure of C#, but Java has stronger memory guarantees than Go, even in the presence of a data race. In Java, all primitive types (including Object pointers) are atomically modified. And since all Java writes are primitives (Java doesn't have structs), you can never corrupt a data structure at the Java level. Of course, you can still corrupt it at a logical level (break an invariant established in the constructor), but not at the language level. Go has a guarantee that word-sized reads/writes are atomic, but Go has plenty of larger objects than that. In particular, interface values are "fat pointers" and exceed the word-size on all platforms, so interface writes are not atomic. Which means another thread can observe an interface value having a vtable from one object but data from another, and can then execute a method from one object on data from another object, potentially re-interpreting fields as values of other types.
- tucnak 2y ago> Which means another thread can observe an interface value having a vtable from one object but data from another, and can then execute a method from one object on data from another object, potentially re-interpreting fields as values of other types. If this were the case, then surely someone could construct a program with goroutines, loops and a handful of interface variables—that would predictably fail, right? I wouldn't know how to make one. Could you, or ChatGPT for that matter, make one for demo's sake?
- deleted 2y ago[deleted]