< prev index next >

src/hotspot/os/linux/os_linux.cpp

Print this page

        

@@ -100,11 +100,10 @@
 # include <poll.h>
 # include <fcntl.h>
 # include <string.h>
 # include <syscall.h>
 # include <sys/sysinfo.h>
-# include <gnu/libc-version.h>
 # include <sys/ipc.h>
 # include <sys/shm.h>
 # include <link.h>
 # include <stdint.h>
 # include <inttypes.h>

@@ -150,12 +149,12 @@
 Mutex* os::Linux::_createThread_lock = NULL;
 pthread_t os::Linux::_main_thread;
 int os::Linux::_page_size = -1;
 bool os::Linux::_supports_fast_thread_cpu_time = false;
 uint32_t os::Linux::_os_version = 0;
-const char * os::Linux::_glibc_version = NULL;
-const char * os::Linux::_libpthread_version = NULL;
+const char * os::Linux::_glibc_version = "unknown";
+const char * os::Linux::_libpthread_version = "unknown";
 
 static jlong initial_time_count=0;
 
 static int clock_tics_per_sec = 100;
 

@@ -603,21 +602,25 @@
 #if !defined(_CS_GNU_LIBC_VERSION) || \
     !defined(_CS_GNU_LIBPTHREAD_VERSION)
   #error "glibc too old (< 2.3.2)"
 #endif
 
-  size_t n = confstr(_CS_GNU_LIBC_VERSION, NULL, 0);
-  assert(n > 0, "cannot retrieve glibc version");
-  char *str = (char *)malloc(n, mtInternal);
-  confstr(_CS_GNU_LIBC_VERSION, str, n);
-  os::Linux::set_glibc_version(str);
+  size_t n;
+
+  n = confstr(_CS_GNU_LIBC_VERSION, NULL, 0);
+  if (n > 0) {
+    char* str = (char *)malloc(n, mtInternal);
+    confstr(_CS_GNU_LIBC_VERSION, str, n);
+    os::Linux::set_glibc_version(str);
+  }
 
   n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, 0);
-  assert(n > 0, "cannot retrieve pthread version");
-  str = (char *)malloc(n, mtInternal);
-  confstr(_CS_GNU_LIBPTHREAD_VERSION, str, n);
-  os::Linux::set_libpthread_version(str);
+  if (n > 0) {
+    char* str = (char *)malloc(n, mtInternal);
+    confstr(_CS_GNU_LIBPTHREAD_VERSION, str, n);
+    os::Linux::set_libpthread_version(str);
+  }
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // thread stack expansion
 

@@ -2983,24 +2986,40 @@
 
 // Something to do with the numa-aware allocator needs these symbols
 extern "C" JNIEXPORT void numa_warn(int number, char *where, ...) { }
 extern "C" JNIEXPORT void numa_error(char *where) { }
 
+static void* dlvsym_if_available(void* handle, const char* name, const char* version) {
+  typedef void* (*dlvsym_func_type)(void* handle, const char* name, const char* version);
+  static dlvsym_func_type dlvsym_func;
+  static bool initialized = false;
+
+  if (!initialized) {
+    dlvsym_func = (dlvsym_func_type)dlsym(RTLD_NEXT, "dlvsym");
+    initialized = true;
+  }
+
+  if (dlvsym_func != NULL) {
+    void *f = dlvsym_func(handle, name, version);
+    if (f != NULL) {
+      return f;
+    }
+  }
+
+  return dlsym(handle, name);
+}
+
 // Handle request to load libnuma symbol version 1.1 (API v1). If it fails
 // load symbol from base version instead.
 void* os::Linux::libnuma_dlsym(void* handle, const char *name) {
-  void *f = dlvsym(handle, name, "libnuma_1.1");
-  if (f == NULL) {
-    f = dlsym(handle, name);
-  }
-  return f;
+  return dlvsym_if_available(handle, name, "libnuma_1.1");
 }
 
 // Handle request to load libnuma symbol version 1.2 (API v2) only.
 // Return NULL if the symbol is not defined in this particular version.
 void* os::Linux::libnuma_v2_dlsym(void* handle, const char* name) {
-  return dlvsym(handle, name, "libnuma_1.2");
+  return dlvsym_if_available(handle, name, "libnuma_1.2");
 }
 
 bool os::Linux::libnuma_init() {
   if (sched_getcpu() != -1) { // Requires sched_getcpu() support
     void *handle = dlopen("libnuma.so.1", RTLD_LAZY);

@@ -5002,10 +5021,67 @@
 }
 
 extern void report_error(char* file_name, int line_no, char* title,
                          char* format, ...);
 
+// Some linux distributions (notably: Alpine Linux) include the
+// grsecurity in the kernel by default. Of particular interest from a
+// JVM perspective is PaX (https://pax.grsecurity.net/), which adds
+// some security features related to page attributes. Specifically,
+// the MPROTECT PaX functionality
+// (https://pax.grsecurity.net/docs/mprotect.txt) prevents dynamic
+// code generation by disallowing a (previously) writable page to be
+// marked as executable. This is, of course, exactly what HotSpot does
+// for both JIT compiled method, as well as for stubs, adapters, etc.
+//
+// Instead of crashing "lazily" when trying to make a page executable,
+// this code probes for the presence of PaX and reports the failure
+// eagerly.
+static void check_pax(void) {
+  // Zero doesn't generate code dynamically, so no need to perform the PaX check
+#ifndef ZERO
+  size_t size = os::Linux::page_size();
+
+  void* p = ::mmap(NULL, size, PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+  if (p == MAP_FAILED) {
+    vm_exit_out_of_memory(size, OOM_MMAP_ERROR, "failed to allocate memory for PaX check.");
+  }
+
+  int res = ::mprotect(p, size, PROT_WRITE|PROT_EXEC);
+  if (res == -1) {
+    vm_exit_during_initialization("Failed to mark memory page as executable",
+                                  "Please check if grsecurity/PaX is enabled in your kernel.\n"
+                                  "\n"
+                                  "For example, you can do this by running (note: you may need root privileges):\n"
+                                  "\n"
+                                  "    sysctl kernel.pax.softmode\n"
+                                  "\n"
+                                  "If PaX is included in the kernel you will see something like this:\n"
+                                  "\n"
+                                  "    kernel.pax.softmode = 0\n"
+                                  "\n"
+                                  "In particular, if the value is 0 (zero), then PaX is enabled.\n"
+                                  "\n"
+                                  "PaX includes security functionality which interferes with the dynamic code\n"
+                                  "generation the JVM relies on. Specifically, the MPROTECT functionality as\n"
+                                  "described on https://pax.grsecurity.net/docs/mprotect.txt is not compatible\n"
+                                  "with the JVM. If you want to allow the JVM to run you will have to disable PaX.\n"
+                                  "You can do this on a per-executable basis using the paxctl tool, for example:\n"
+                                  "\n"
+                                  "    paxctl -cm bin/java\n"
+                                  "\n"
+                                  "Please note that this modifies the executable binary in-place, so you may want\n"
+                                  "to make a backup of it first. Also note that you have to repeat this for other\n"
+                                  "executables like javac, jar, jcmd, etc.\n"
+                                  );
+
+  }
+
+  ::munmap(p, size);
+#endif
+}
+
 // this is called _before_ most of the global arguments have been parsed
 void os::init(void) {
   char dummy;   // used to get a guess on initial stack address
 
   clock_tics_per_sec = sysconf(_SC_CLK_TCK);

@@ -5037,10 +5113,12 @@
 
   // retrieve entry point for pthread_setname_np
   Linux::_pthread_setname_np =
     (int(*)(pthread_t, const char*))dlsym(RTLD_DEFAULT, "pthread_setname_np");
 
+  check_pax();
+
   os::Posix::init();
 
   initial_time_count = javaTimeNanos();
 
   // Always warn if no monotonic clock available
< prev index next >