Java does maintain memory safety despite having shared mutable state. Every field in Java would be considered atomic in C++11 or Rust but with an even weaker memory ordering than relaxed:
llvm.org/docs/Atomics.h
It doesn't do any more than preserving memory and type safety.
Conversation
Java has data races in the same sense that Rust has data races via atomics. It's not the kind of data race you get in C where it's totally unsafe with no guarantees and memory/type safety exists despite it. Java didn't take the shortcut that Go did where races can corrupt memory.
1
4
Technically yeah, but the fact that Rust requires you to opt in to making values atomic (as opposed to Java where you can't even opt out) makes a big difference in practice.
2
I don't think there is much hardware these days that don't follow the "no writes coming from nowhere" and "no reading half-updated values" rules which make broken Java/Go/etc code still memory-safe. Also IIRC one opts-in with volatile or AtomicType...
2
Go has multi-word values for slices and maps which are unsafe in the presence of data races. It may go beyond that too. Java enforces safety in the presence of data races. It has pervasive opportunities for races but the runtime / standard library have to preserve safety.
1
"unsafe" as in it could crash, or that you could read stray pointers and go into UB land? Coming from Java (not that I've written a lot, admittedly) it seems odd that races would do the latter, given that you don't read half-updated pointers, and the GC still traces through.
1
Data races can corrupt memory in Go. Slices have a pointer, length and capacity without synchronization and perform direct memory access. It's possible for a race to cause an out-of-bounds access to be permitted. Maps have the same kind of issue too.
2
4
As opposed to getting an out-of-bounds access panic, it can permit the out-of-bounds read or write due to a data race and corrupt memory. It can get a bit more complicated than that too. It's probably not going to happen much in practice but an attacker could trigger it...
1
There are often seemingly nearly impossible to exploit memory corruption bugs in C particularly those involving data races where an attacker still figures out how to exploit it. They can attempt to trigger conditions where it becomes more likely to succeed and can often retry.
1
1
For example, in this case, a failure is going to trigger a panic. For a server, that likely only kills the handler for a request or connection. The attacker can probably keep trying over and over again until it works. They can also trigger allocation of data via other requests.
1
They can set things up so that a bunch of objects are allocated for them to target with the writes via other requests at the same time and attempt over and over again to do an out-of-bounds write to corrupt a function pointer, etc. Definitely something that could be exploited.

