|
> macOS on Apple silicon processors (M1, M2, and M3) includes a feature which controls how and when dynamically generated code can be either produced (written) or executed on a per-thread basis. […] With macOS 14.4, when a thread is operating in the write mode, if a memory access to a protected memory region is attempted, macOS will send the signal SIGKILL instead. This isn’t just any old thread triggering SIGKILL, it’s the JIT thread privileged to write to executable pages that is performing illegal memory accesses. That’s typically a sign of a bug, and allowing a thread with write access to executable pages to continue executing after that is a security risk. But I know of other language runtimes that take advantage of installing signal handlers for SIGBUS/SIGSEGV to detect when they overflow a page so they can allocate more memory, etc. This saves from having to do an explicit overflow check on every allocation. Those threads aren’t given privilege to write to executable memory, so they’re not seeing this issue… So this sounds like a narrow design problem the JVM is facing with their JIT thread. This blog doesn’t explain why their JIT thread needs to make illegal memory accesses instead of an explicit check. |
Because explicit checks on every memory access (pointer dereference) makes Java significantly slower, even with compiler optimisations to remove redundant checks[1]. Memory protection is a fundamental, very useful, hardware feature and it's perfectly reasonable for user space language runtimes to take advantage of it.
Or, to put it another way, SIGSEGV has been a part of Unix-family OSes for decades. It works perfectly fine on Linux and Windows and there's no reason it shouldn't work on macOS.
[1] (Many years ago I worked on a cross-platform implementation of the Java runtime and wrote much of the threads and signal handling code. We had an option to enable explicit memory checks, which got us up and running faster on new platforms where the SIGSEGV handlers hadn't been written yet. From memory this made everything something like 30-50% slower, so it was definitely worthwhile to implement SIGSEGV handling. In our case SIGSEGV handlers were used both as part of the garbage collector/memory management and to implement Java's NullPointerException)