[rvm-research] Potential issues with longjmp in bootImageRunner

Carl Ritson <[email protected]>
Newsgroups gmane.comp.java.jikes.rvm.devel
Message-ID <CAKDym5Nn0H_Pizu7FnHG4OdyNk11p55J6hRkqzmvvWL8Tf7W5Q@mail.gmail.com>
Hi,

In bootImageRunner/sys.C, the function sysThreadTerminate() uses
longjmp() to return from the VM stack to the thread stack at thread
termination.

In recent versions of glibc, longjmp() passes through a checking
function __longjmp_chk() which attempts to ensure that the jump is to
a valid stack frame.  It does this by asserting that the new stack
pointer is either greater than then current one, or contained on the
alternate signal stack.  As stacks grow downward (on x86 at least),
this ensures that longjmp() only ever unwinds the stack.  This
assertion is obviously flawed when an separately allocated stack is
used, such as in JikesRVM.

On a modern Linux kernel mmap() allocates memory top-down, so
successive allocations (tend to) fall on lower addresses.  Thus in
practice thread stacks are higher in memory than VM stacks, so the
__longjmp_chk() assertion succeeds.  However, a legacy mode of
bottom-up allocation is also supported, this can be activated by
setting the process personality (e.g. setarch -L) or having an
unlimited stack size in the process resource limits (e.g. ulimit -s
unlimited).  The second of these is obscure, but it what resulted in
my discovery of this issue.

In bottom-up mode thread stacks are allocated below the VM memory area
(at least on x86_64 32-bit mode), and hence __longjmp_chk fails.
For JikesRVM in production configuration on x86_64-linux the error
looks like the following (produced from DaCapo 2006 eclipse, on Ubuntu
12.04 LTS):

*** longjmp causes uninitialized stack frame ***:
dist/gc/production/JikesRVM terminated
======= Backtrace: =========
/lib32/libc.so.6(__fortify_fail+0x45)[0x557e0675]
/lib32/libc.so.6(+0x1035ca)[0x557e05ca]
/lib32/libc.so.6(__longjmp_chk+0x4b)[0x557e053b]
dist/gc/production/JikesRVM(sysLongDivide+0x0)[0x804e68b]
[0x64707099]

There are a number of approaches to dealing with this:
 1. assume allocation will always be top-down and ignore,
 2. explicitly allocate thread stacks (these can be passed to pthread_create),
 3. replace use of setjmp/longjmp with savecontext/swapcontext
(although these are deprecated in POSIX),
 4. implement a local version of longjmp without a check,
 5. bypass the __longjmp_chk by using glibc's internal __libc_longjmp().

Disregarding (1), (5) is the smallest change set.

The attached patch detects glibc via preprocessor macros and redirects
longjmp when glibc is used.
This appears to allow operations of JikesRVM with bottom-up allocation
on my test systems.

Kind regards,

Carl Ritson
[email protected]

------------------------------------------------------------------------------
Precog is a next-generation analytics platform capable of advanced
analytics on semi-structured data. The platform includes APIs for building
apps and a phenomenal toolset for data science. Developers can use
our toolset for easy data analysis & visualization. Get a free account!
http://www2.precog.com/precogplatform/slashdotnewsletter

_______________________________________________
Jikesrvm-researchers mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/jikesrvm-researchers
glibc-longjmp-patch.diff (application/octet-stream, 826 B)
diff -r 65aaf607fd8d tools/bootImageRunner/sys.C
--- a/tools/bootImageRunner/sys.C	Mon Mar 25 22:12:00 2013 +0100
+++ b/tools/bootImageRunner/sys.C	Wed Apr 10 16:59:57 2013 +0100
@@ -47,6 +47,17 @@
 #include <utime.h>
 #include <setjmp.h>
 
+#ifdef __GLIBC__
+/* use glibc internal longjmp to bypass fortify checks */
+extern "C" void __libc_longjmp (jmp_buf buf, int val) \
+                    __attribute__ ((__noreturn__));
+#define rvm_longjmp(buf, ret) \
+        __libc_longjmp(buf, ret)
+#else
+#define rvm_longjmp(buf, ret) \
+        longjmp(buf, ret)
+#endif /* !__GLIBC__ */
+
 #ifdef RVM_WITH_PERFEVENT
 #include <perfmon/pfmlib_perf_event.h>
 #include <err.h>
@@ -1354,7 +1365,7 @@
     if (jb==NULL) {
 	jb=&primordial_jb;
     }
-    longjmp(*jb,1);
+    rvm_longjmp(*jb,1);
 }
 
 //------------------------//
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.