[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);
}
//------------------------//