8197429: Increased stack guard causes segfaults on x86-32
authoraph
Fri, 16 Feb 2018 09:43:26 +0000
changeset 49407 7fa5375fa6fd
parent 49406 f654b37c58a1
child 49408 6171c007410a
8197429: Increased stack guard causes segfaults on x86-32 Reviewed-by: dholmes
make/test/JtregNativeHotspot.gmk
src/hotspot/os/linux/os_linux.cpp
src/hotspot/os/linux/os_linux.hpp
src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp
test/hotspot/jtreg/runtime/StackGap/T.java
test/hotspot/jtreg/runtime/StackGap/exestack-gap.c
test/hotspot/jtreg/runtime/StackGap/testme.sh
--- a/make/test/JtregNativeHotspot.gmk	Thu Mar 15 15:38:23 2018 +0100
+++ b/make/test/JtregNativeHotspot.gmk	Fri Feb 16 09:43:26 2018 +0000
@@ -97,6 +97,7 @@
 BUILD_HOTSPOT_JTREG_NATIVE_SRC += \
     $(TOPDIR)/test/hotspot/jtreg/runtime/execstack \
     $(TOPDIR)/test/hotspot/jtreg/runtime/jsig \
+    $(TOPDIR)/test/hotspot/jtreg/runtime/StackGap \
     $(TOPDIR)/test/hotspot/jtreg/runtime/StackGuardPages
 endif
 
@@ -128,6 +129,7 @@
     BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libtest-rw := -z noexecstack
     BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libtest-rwx := -z execstack
     BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeinvoke := -ljvm -lpthread
+    BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exestack-gap := -ljvm -lpthread
     BUILD_TEST_invoke_exeinvoke.c_OPTIMIZATION := NONE
     BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeFPRegs := -ldl
 endif
--- a/src/hotspot/os/linux/os_linux.cpp	Thu Mar 15 15:38:23 2018 +0100
+++ b/src/hotspot/os/linux/os_linux.cpp	Fri Feb 16 09:43:26 2018 +0000
@@ -629,6 +629,10 @@
   }
 }
 
+void os::Linux::expand_stack_to(address bottom) {
+  _expand_stack_to(bottom);
+}
+
 bool os::Linux::manually_expand_stack(JavaThread * t, address addr) {
   assert(t!=NULL, "just checking");
   assert(t->osthread()->expanding_stack(), "expand should be set");
--- a/src/hotspot/os/linux/os_linux.hpp	Thu Mar 15 15:38:23 2018 +0100
+++ b/src/hotspot/os/linux/os_linux.hpp	Fri Feb 16 09:43:26 2018 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -218,6 +218,8 @@
   // none present
 
  private:
+  static void expand_stack_to(address bottom);
+
   typedef int (*sched_getcpu_func_t)(void);
   typedef int (*numa_node_to_cpus_func_t)(int node, unsigned long *buffer, int bufferlen);
   typedef int (*numa_max_node_func_t)(void);
--- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp	Thu Mar 15 15:38:23 2018 +0100
+++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp	Fri Feb 16 09:43:26 2018 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -834,6 +834,28 @@
 void os::workaround_expand_exec_shield_cs_limit() {
 #if defined(IA32)
   size_t page_size = os::vm_page_size();
+
+  /*
+   * JDK-8197429
+   *
+   * Expand the stack mapping to the end of the initial stack before
+   * attempting to install the codebuf.  This is needed because newer
+   * Linux kernels impose a distance of a megabyte between stack
+   * memory and other memory regions.  If we try to install the
+   * codebuf before expanding the stack the installation will appear
+   * to succeed but we'll get a segfault later if we expand the stack
+   * in Java code.
+   *
+   */
+  if (os::is_primordial_thread()) {
+    address limit = Linux::initial_thread_stack_bottom();
+    if (! DisablePrimordialThreadGuardPages) {
+      limit += JavaThread::stack_red_zone_size() +
+        JavaThread::stack_yellow_zone_size();
+    }
+    os::Linux::expand_stack_to(limit);
+  }
+
   /*
    * Take the highest VA the OS will give us and exec
    *
@@ -852,6 +874,16 @@
   char* hint = (char*)(Linux::initial_thread_stack_bottom() -
                        (JavaThread::stack_guard_zone_size() + page_size));
   char* codebuf = os::attempt_reserve_memory_at(page_size, hint);
+
+  if (codebuf == NULL) {
+    // JDK-8197429: There may be a stack gap of one megabyte between
+    // the limit of the stack and the nearest memory region: this is a
+    // Linux kernel workaround for CVE-2017-1000364.  If we failed to
+    // map our codebuf, try again at an address one megabyte lower.
+    hint -= 1 * M;
+    codebuf = os::attempt_reserve_memory_at(page_size, hint);
+  }
+
   if ((codebuf == NULL) || (!os::commit_memory(codebuf, page_size, true))) {
     return; // No matter, we tried, best effort.
   }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/StackGap/T.java	Fri Feb 16 09:43:26 2018 +0000
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018, Red Hat, Inc. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class T {
+
+  public static void test(int n) {
+    if (n == 0) return;
+    System.out.println (n);
+    test (n - 1);
+
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/StackGap/exestack-gap.c	Fri Feb 16 09:43:26 2018 +0000
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018, Red Hat, Inc. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+JNIEnv* create_vm(JavaVM **jvm, char *extra_option)
+{
+    JNIEnv* env;
+    JavaVMInitArgs args;
+    JavaVMOption options[4];
+    args.version = JNI_VERSION_1_8;
+    args.nOptions = 3 + (extra_option != NULL);
+    options[0].optionString = "-Xss2048k";
+    char classpath[4096];
+    snprintf(classpath, sizeof classpath,
+             "-Djava.class.path=%s", getenv("CLASSPATH"));
+    options[1].optionString = classpath;
+    options[2].optionString = "-XX:+UnlockExperimentalVMOptions";
+    if (extra_option) {
+      options[3].optionString = extra_option;
+    }
+    args.options = &options[0];
+    args.ignoreUnrecognized = 0;
+    int rv;
+    rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
+    if (rv < 0) return NULL;
+    return env;
+}
+
+void run(char *extra_arg) {
+  JavaVM *jvm;
+  jclass T_class;
+  jmethodID test_method;
+  JNIEnv *env = create_vm(&jvm, extra_arg);
+  if (env == NULL)
+    exit(1);
+  T_class = (*env)->FindClass(env, "T");
+  test_method = (*env)->GetStaticMethodID(env, T_class, "test", "(I)V");
+  (*env)->CallStaticVoidMethod(env, T_class, test_method, 1000);
+}
+
+
+int main(int argc, char **argv)
+{
+  if (argc > 1) {
+    run(argv[1]);
+  } else {
+    run(NULL);
+  }
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/StackGap/testme.sh	Fri Feb 16 09:43:26 2018 +0000
@@ -0,0 +1,49 @@
+# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#!/bin/sh
+
+#
+# @test testme.sh
+# @summary Linux kernel stack guard should not cause segfaults on x86-32
+# @compile T.java
+# @run shell testme.sh
+#
+
+if [ "${TESTSRC}" = "" ]
+then
+  TESTSRC=${PWD}
+  echo "TESTSRC not set.  Using "${TESTSRC}" as default"
+fi
+echo "TESTSRC=${TESTSRC}"
+## Adding common setup Variables for running shell tests.
+. ${TESTSRC}/../../test_env.sh
+
+if [ "${VM_OS}" != "linux" ]
+then
+  echo "Test only valid for Linux"
+  exit 0
+fi
+
+LD_LIBRARY_PATH=.:${TESTJAVA}/lib/${VM_TYPE}:/usr/lib:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH
+
+${TESTNATIVEPATH}/stack-gap || exit $?
+${TESTNATIVEPATH}/stack-gap -XX:+DisablePrimordialThreadGuardPages || exit $?