5 ms·
> As a normal part of the just-in-time compile and execute cycle, processes running on macOS may access memory in protected memory regions. Prior to the macOS 1
by karolist 3y ago
> As a normal part of the just-in-time compile and execute cycle, processes running on macOS may access memory in protected memory regions. Prior to the macOS 14.4 update, in certain circumstances, the macOS kernel would respond to these protected memory accesses by sending a signal, SIGBUS or SIGSEGV, to the process.
> 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.
What is bizarre to me is that Oracle relied on receiving SIGSEGV as normal mode of operation. That should have been a hint where things are going, no?
- deleted 3y ago[deleted]
- cataphract 3y ago> What is bizarre to me is that Oracle relied on receiving SIGSEGV as normal mode of operation. That should have been a hint where things are going, no? Not bizarre at all, this how the runtime has always operated, as anyone one who's ever attached a debugger to a Java process knows. The SIGSEGV handler is also responsible to handling NullPointerExceptions IIRC.
- fniephaus 3y agoCorrect: > ... the JVM can intercept the resulting SIGSEGV ("Signal: Segmentation Fault"), look at the return address for that signal, and figure out where that access was made in the generated code. Once it figures that bit out, it can then know where to dispatch the control to handle this case — in most cases, throwing NullPointerException or branching somewhere. https://shipilev.net/jvm/anatomy-quarks/25-implicit-null-checks/#_theory https://shipilev.net/jvm/anatomy-quarks/25-implicit-null-che...
- Vogtinator 3y agoIt's documented and part of the interface for POSIX: > Write attempts to memory that was mapped without write access, or any access to > memory mapped PROT_NONE, shall result in a SIGSEGV signal. > > References to unmapped addresses shall result in a SIGSEGV signal. How a SIGSEGV can be handled by the program to continue execution normally need some OS specific code. For Linux there's also userfaultfd to suit this need better.
- wahern 3y ago> How a SIGSEGV can be handled by the program to continue execution normally need some OS specific code A JVM's use of SIGSEGV might include platform-dependent details for recovery. But for simple application usages (e.g. eliding inlined bounds checks in a performance critical loop operating on an array) longjmp can suffice for recovery. POSIX very carefully defines async-safety and longjmp to permit jumping out of a signal handler and resuming normal execution, provided certain constraints are met, such as that the signal did not interrupt a non-async-signal-safe function.
- zozbot234 3y ago> ...such as that the signal did not interrupt a non-async-signal-safe function. So you have to disable signals prior to doing anything "non-async-signal-safe" and re-enable them thereafter? That's a pretty big "but"...
- vardump 3y agoThat's actually a pretty normal way to do things. Optimization for JITting, let the CPU hardware to do the heavy lifting instead of putting conditional jumps everywhere. It's useful for other things as well. I've used SIGSEGV to emulate hardware interrupts. Normal execution wouldn't trap and there's no need for tests + branches (= normally no slowdown), but when an interrupt occurs a specific often accessed page is marked unreadable.
- mort96 3y agoWhich other JITs behave like this? AFAIK neither V8 nor Spidermonkey nor LuaJIT rely on segfaults as a normal part of their operation?
- silon42 3y agoPersonally I'd expect this would affect the GC more than the JIT. But I'm not surprised that the JVM uses every trick for speed.
- quotemstr 3y agoAndroid ART uses this mode of operation too. There's absolutely nothing wrong with relying on SIGSEGV and other synchronous signals in this manner and POSIX should make it easier and safer instead of trying to pretend signals are bad and useless.
- fransje26 3y agoThat you can doesn't necessarily mean that you should. https://xkcd.com/1172/ https://xkcd.com/1172/
- arghwhat 3y agoDespite what the other commenters are saying, it is bizarre. 1. There is very little you can safely do in a signal handler. For a threaded application, that pretty much boils entirely down to setting a bit and leaving it at that. If they did anything more, the behavior is undefined. 2. The memory state that a program receiving a SIGSEGV in is often undefined/garbage, and attempting to execute further at this point is at best unsafe, at worst trampling on state further, continuing execution in a broken state and destroying all evidence that would be useful for debugging - whereas a coredump preserves the state at the time the issue occurs. There are cases where you need to catch SIGBUS, such as if an anonymous file has been truncated after you mmap'ed it.
- fweimer 3y agoThe signal comes from a safe fetch, which is just a read that allows ignoring the fault as if it never happened. Such a signal is deliver synchronously, so the usual restrictions for asynchronous signal handlers do not apply. The code in question takes into account that the value read might be garbage. See the big comment here: https://github.com/openjdk/jdk/commit/29397d29baac3b29083b1b5d6b2cb06e456af0c3 https://github.com/openjdk/jdk/commit/29397d29baac3b29083b1b... On current CPUs and operating systems, this is not an optimization, so the code was removed earlier this year: https://bugs.openjdk.org/browse/JDK-8320317 https://bugs.openjdk.org/browse/JDK-8320317
- arghwhat 3y agoThe "safe fetch" code relies on a signal handler (either here https://github.com/openjdk/jdk/blob/48717d63cc58f693f0917e61eafd672cd6af02ed/src/hotspot/os/posix/vmError_posix.cpp#L80 https://github.com/openjdk/jdk/blob/48717d63cc58f693f0917e61... or here https://github.com/openjdk/jdk/blob/3c70f26b2f3fa9bc143e2506af30f9b1daf20022/src/hotspot/os/posix/signals_posix.cpp#L557C5-L557C26 https://github.com/openjdk/jdk/blob/3c70f26b2f3fa9bc143e2506...), which is considered asynchronous delivery (i.e., delivered mid-execution, see `man 7 signal`) - which is why the `async-signal-safe` manpage simply states that it is functions that can safely be called within a signal handler. This is opposed to calling `sigwait` or similar to actively suspend and wait for a signal, which is not possible to do here. Granted, it may be that the stars align and their implementation works in practice, but that does not make it any less bizarre.
- sp1rit 3y ago>> As a normal part of the just-in-time compile and execute cycle This means a workaround is running java with -Djava.compiler=NONE, no?
- przemelek 3y agoI was thinking more about -Xint, or in Docker, or x86 JVM, but my guess is that somebody already tested it ;-) Other thing is that one of developers in my team who is on M1 and 14.4 is able to run Java app, so...
- PhilipRoman 3y agoA better choice would be -Xrs which keeps optimizations enabled, but disables use of SEGV.
- zozbot234 3y agoThis disables use of all signal handlers, which means Java apps will also e.g. fail to quit cleanly in response to issuing SIGQUIT, or hitting ^C at the terminal. Better than "no workaround whatsoever" but far from ideal!
- josefx 3y agoI think it is used to avoid doing null checks on every pointer access.