Hacker News new | ask | show | jobs
by riscy 822 days ago
> 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.

5 comments

> "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)

As Linus famously said: Shut. Up. Don’t break userspace and then blame the user.

https://lkml.org/lkml/2012/12/23/75

Linux did break adobe flash when it used memmove like memcopy after fixing a kernel bug. Can't think of any other examples though.
AFAIR it was glibc change and Linux kernel had nothing to do with it.
memmove and memcpy don't use a syscall, that would be very inefficient. How would a kernel fix change the behavior?
Well, everyone breaks Adobe Flash... that's the whispered exception at the end of the rule: "don't break userspace and then blame the user (unless that user is Adobe Flash... F--- Adobe Flash)" ;P.
I feel like preventing illegal writes to protected memory is less "breaking user space" and more "protecting all space".

This is like arguing to allow the guy who can't drive and just pin-balls his way down the freeway bouncing of other cars, because to prevent him from driving would be to take away his personal freedoms.

There was no conceivable version of a road system where that behavior would ever be okay. However, it's not only conceivable but, apparently standard practice in systems programming, to "Try and Fail" instead of "Only Proceed if allowed".

So, if we want a tortured metaphor what JVM is doing is like trying to pass a turnstile to see if the pass is still valid so that on the happy path it saves the extra check. Now Apple decided that instead of just showing a red X and letting you buy a new pass, in the future you get shot in the back of the head if you try with an invalid pass.

> There was no conceivable version of a road system where that behavior would ever be okay.

That doesn't mean it doesn't happen.

Are you suggesting that the approach taken here was at one point a documented acceptable approach, according to Apple?

According to UNIX, which MacOS is certified of being.
Can't find it in SuSv3 (but that's thing, like all UNIX manuals, is a monstrosity): https://pubs.opengroup.org/onlinepubs/9299959899/toc.pdf
So MacOS is trying to be smart, changes their API, and now we're blaming the JVM for doing something we don't understand?

At least they could have provided a path back to the old behavior.

That’s how MS works which leads to compatibility, but less stability. Historically with Apple, it’s their way or the highway. Less compatibility, but the OS is more stable.
If the OS changes its fundamental behaviors it is less stable not more.. It might be more secure, though I am not convinced in this case.
Does not seems stable, when all Java based applications are crashing.
> when all Java based applications are crashing

That probably needs to be qualified with "Oracle JVM Java applications" because I've had many hours of running Minecraft on macOS 14.4 under Zulu's JVM (a pre-14.4 release which means it doesn't have any workarounds) without any issues.

Almost all Java builds including Zulu are made from the same OpenJDK code base. The fact that you didn't experience crashes does not tell anything. May be you were lucky or this particular bug was not manifesting for this particular application.
> or this particular bug was not manifesting for this particular application.

"all Java based applications are crashing" is a false statement either way.

Only Java is unstable because it’s not following Apple’s rules. Everything else, including the apps that follow the rules, are stable. That’s how it’s always been for the Apple ecosystem.
Java is not breaking any rules. macOS has memory management provisions for JITs and virtual machines just like every other OS. SIGSEGV has been part of Unix for decades and works perfectly fine on other OSes. And still works "most of the time" on macOS. Apple broke something here, and I'm sure they will fix it pretty soon.
Sorry, that's not how security works.
macOS is trying to keep its systems safe. Can’t leave the back door open for few who were used to it.
this reads like a description dumbed down meant to be read by someone that does not know the technical details, and on top of that, with an agenda to mislead(in this circumstance) :)
The problem is that Apple closed the front door too.
What is it a backdoor to?
It said it affected back to Java 8, so seems like this design has been there for a while, and since older versions are EOL, any Java level fix would not be patched back.
I wonder what that means for the Android SDK, which AFAIK requires an ancient Java8 runtime for the command line SDK tools on macOS.
Android team has been forced to accept Kotlin without the Java ecosystem is an oxymoron, thus not only is ART updatable since Android 12, Java 17 LTS is now the latest supported version.

And on the SDK side, they need to use whatever InteliJ requires.

Java 8 is not EOL.
It is not, if you pay for support.
No, you can just download Adoptium, Azul and probably other builds. They're up to date and will be for few years. Not sure about Oracle, but wouldn't recommend it anyway.
They now seem to work with Java 11.
If something works on OS release version 1 then it should still work on OS release version 2.

Or in apple vernacular, it should just work.