The definition I'm giving is the same as the ISRG's definition at MemorySafety.org. It's the thing everybody is talking about when they talk about memory safety.
The claim being made here is "big if true", because it would imply a lot more languages than Go "aren't memory safe", despite decades without memory corruption exploits.
It's remarkably easy to segfault Go applications with data races. Any object with multiple words (so a slice that's an array pointer, a size, and a capacity. Or a fat pointer with the object pointer and the vtable pointer), can be read in an inconsistent state from two threads which can cause out of bounds reads and writes. And this comes up all the time with how heavily the language encourages concurrency.
It's just difficult to actually exploit because of other considerations that practically add a lot of runtime entropy.
The was for a time a vogue for "zero trust networking" and I'm fond of pointing out that the same thing happened there: people would come up with their own axiomatic derivation of what "zero trust" meant, but in reality it was a term of art meaning "non-Google implementations of BeyondCorp".
Terms of art are kryptonite for message board nerds.
And yeah, we tend to use the PLT definitions when we're talking about literal semantics of programming languages. Nothing in this thread mandates a security focused sub-definition.
And even Rob Pike described Go as "not purely memory safe", in a slide that obviously references this exact data race behavior. https://go.dev/talks/2012/splash.slide#49 They considered this a practical tradeoff for simplicity versus the major managed languages defining what happens during data races in a way that doesn't allow you to break memory safety.
Iny experience, the ultra security focused view isn't what most people think of when they hear memory safety. It's one aspect, but one among many. For instance debugability is much nicer when you can't break the object model and get a nice trace out of the system versus when you're trying to find memory corruption with gdb or something.
That said, even the ISRG definitions I've found don't list protection from exploits. It does include out of bounds memory accesses in what makes a memory unsafe language, which would discount Go. Yes, they explicitly list Go as a memory safe language, but there's a good chance that they simply don't know about this behavior.
As someone who used to freelance in exploit research, the go behavior doesn't seem insurmountable for finding an exploit on its own. Frankly it's all the other ecosystem stuff that makes it harder. The fact that go code has a habit of being deployed multiple times a day, you a lot of times don't have access to the binaries, there's generally no dynamic (on Linux) so you have no relatively stable code to find gadgets in, etc. (Although there are aspects of the language like the relative simplicity of the compiler that do help you in some of those regards).
So whats your point here? Haskell?
You said that because you assumed Go is memory-safe in all conditions.
> Go is memory-safe
Yes, but only if there's no data race.
Go is not like Java. Java doesn't guarantee no data race, but when it happens, it's still memory-safe.
I fail to see how a racy Java program is more memory safe than a racy Go program?
Go slices are fat pointers to undecorated memory. The slice itself is a 3-tuple of pointer, length, and capacity. If you append to a slice that's already at capacity, the Go runtime will allocate new memory for you and return a new 3-tuple. If you assign that result to a variable that's also being accessed by another goroutine, the latter can observe the slice in an inconsistent state. It can, for example, see the old pointer but with the new length, allowing out-of-bounds access. None of this requires unsafe code.
The same issue applies to string and interface variables, which are also fat pointers.
Shared Go slices are a bad mix in concurrent code. This is a given. But its also not a fair comparison, you should instead compare java arrays to go arrays, not slices.
This goes for slices, strings and maps. Those a usually wrapped in a mutex, or used with sync primitives like sync.Map.
The point is that Java does not have this problem in the language or the standard library. Of course, you should not write racy Go code; the language provides ample ways to avoid the race, such as channels; and the race detector will generally find such racy code, provided you turn it on. However, the issue is that this race leads to memory-safety violations; it can occur especially in code written by novice Go programmers, and it's well acknowledged by the language authors [1].
The same can be said of C or C++ constructs (and many "anti-Rust" people have historically said that) -- the point is that their use is not enforced by the language and so bugs can lead to panics.
I write a fair amount of Go and Rust so I really don't think either language's flaws are fatal, but it comes off as weirdly defensive to redefine memory and data safety to be "well if you use it properly it's safe". It's totally fine to say this is a problem the Go language did not find important enough to require compile time enforcement and so solving it is done by convention and testing with the race detector (which a similar answer C and C++ give to this problem).
> I have never seen real Go code (i.e. not code written purposefully to be exploitable) that was exploitable due to a data race.
And from tptacek in that same discussion [2]:
> The fact is that Go doesn't admit memory corruption vulnerabilities, and the way you know that is the fact that there are practically zero exploits for memory corruption vulnerabilities targeting pure Go programs, despite the popularity of the language.
Race conditions in general are another matter, and aren’t generally considered a requirement (though you can certainly create nasty bugs).
Thread a = new Thread(() -> x++);
Thread b = new Thread(() -> x++);
a.start();
b.start();Go does not have that.
(Of course also Java may suffer from memory safety issues on system boundaries to unsafe code and due to JVM bugs.)