Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 12 Aug 2019 09:16:29 -0700
From:      Greg Lewis <glewis@eyesbeyond.com>
To:        freebsd-hackers@freebsd.org
Subject:   Java stack overflow segfaults
Message-ID:  <20190812161629.GA99971@misty.eyesbeyond.com>

next in thread | raw e-mail | index | archive | help
Hi all,

I'm investigating an issue where, on FreeBSD, Java will crash rather than
throw a StackOverflowError given a simple test program with a function
that just calls itself over and over.  There's an example of such a test
in https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=222146

This affects, I suspect, every native version of Java in the ports tree,
although I've only tried openjdk8 and higher.  My investigation has mostly
focused on openjdk11.

To outline the situation, Java uses pthreads internally for threading.  It
doesn't use the pthreads own guard page(s), but instead creates it's own
guard area at the bottom of the stack (which grows downward) using
mprotect.  It then installs a signal handler and examines any SIGSEGV's
fault address to see if it falls within the guard area, and if so throws a
StackOverflowError.  This logic is the same across all of the OSes I've
looked at and works on OpenBSD, Linux, etc.  On FreeBSD though, the fault
address lies in the page above the guard zone, rather than in the guard
zone, which results in a crash rather than throwing StackOverflowError.

An diagram may help here:

--- <- Stack top
|
| Untouched memory + stack frames + etc.
|
|
| <-- SIGSEGV signal info fault address (< 1 page above guard zone) 
--- <- Start of JVM reserved zone / guard zone
|
| JVM Reserved page
|
--- <- Start of JVM yellow zone
|
| JVM Yellow pages
|
--- <- Start of JVM red zone
|
| JVM Red page
|
--- <- Stack bottom
|
| Pthread guard page(s)
|
---

On my FreeBSD 11.3/amd64 machine the JVM uses a total of four pages for the
guard zone (1 reserved, 2 yellow, 1 red).  The page size is 4K, and I see
the follow mprotect calls with truss:

mprotect(stack bottom address, 4K, PROT_NONE) (Just the red zone)
mprotect(stack bottom address, 16K, PROT_NONE) (The entire guard zone)
mprotect(top of red zone address, 12K, PROT_READ|PROT_WRITE) (Reserved + yellow)
mprotect(top of red zone address, 12K, PROT_NONE) (Reserved + yellow)

While I've committed a workaround for openjdk8, which just rounds down the
fault address, it isn't entirely satisfactory (it's a hack) and I wondered
if anyone had any insight into what may be going on.  I've done an analysis
of the sizes and addresses being used and used truss to check the parameters
to the mprotect calls, and everything appears to add up.

The same problem also occurs under FreeBSD 12.0/i386 and on aarch64, so it
doesn't appear to be either version or platform specific.  I've simplified
a little here, but am happy to provide additional details and code
references.

-- Greg



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20190812161629.GA99971>