# HG changeset patch # User aph # Date 1530894306 -3600 # Fri Jul 06 17:25:06 2018 +0100 # Node ID 3b9a54b7f745b6e73c83920c88b6b51bac3ce722 # Parent ad057f2e3211cd18bc56550d8a2c400d92ec35b1 8197429: Increased stack guard causes segfaults on x86-32 Reviewed-by: dholmes diff --git a/src/os/linux/vm/os_linux.cpp b/src/os/linux/vm/os_linux.cpp --- a/src/os/linux/vm/os_linux.cpp +++ b/src/os/linux/vm/os_linux.cpp @@ -674,6 +674,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"); diff --git a/src/os/linux/vm/os_linux.hpp b/src/os/linux/vm/os_linux.hpp --- a/src/os/linux/vm/os_linux.hpp +++ b/src/os/linux/vm/os_linux.hpp @@ -244,6 +244,8 @@ static int safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime); 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); diff --git a/src/os_cpu/linux_x86/vm/os_linux_x86.cpp b/src/os_cpu/linux_x86/vm/os_linux_x86.cpp --- a/src/os_cpu/linux_x86/vm/os_linux_x86.cpp +++ b/src/os_cpu/linux_x86/vm/os_linux_x86.cpp @@ -892,6 +892,27 @@ 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 += (StackYellowPages + StackRedPages) * page_size; + } + os::Linux::expand_stack_to(limit); + } + /* * Take the highest VA the OS will give us and exec * @@ -910,6 +931,16 @@ char* hint = (char*) (Linux::initial_thread_stack_bottom() - ((StackYellowPages + StackRedPages + 1) * 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. } diff --git a/test/runtime/StackGap/T.java b/test/runtime/StackGap/T.java new file mode 100644 --- /dev/null +++ b/test/runtime/StackGap/T.java @@ -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); + + } + +} diff --git a/test/runtime/StackGap/exestack-gap.c b/test/runtime/StackGap/exestack-gap.c new file mode 100644 --- /dev/null +++ b/test/runtime/StackGap/exestack-gap.c @@ -0,0 +1,82 @@ +/* + * 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 +#include +#include + +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"); + if ((*env)->ExceptionCheck(env) == JNI_TRUE) { + (*env)->ExceptionDescribe(env); + exit(1); + } + test_method = (*env)->GetStaticMethodID(env, T_class, "test", "(I)V"); + if ((*env)->ExceptionCheck(env) == JNI_TRUE) { + (*env)->ExceptionDescribe(env); + exit(1); + } + (*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; +} diff --git a/test/runtime/StackGap/testme.sh b/test/runtime/StackGap/testme.sh new file mode 100644 --- /dev/null +++ b/test/runtime/StackGap/testme.sh @@ -0,0 +1,73 @@ +# 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 +# @bug 8197429 +# @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 + +gcc_cmd=`which gcc` +if [ "x$gcc_cmd" = "x" ]; then + echo "WARNING: gcc not found. Cannot execute test." 2>&1 + exit 0; +fi + +CFLAGS="-m${VM_BITS}" + +LD_LIBRARY_PATH=.:${COMPILEJAVA}/jre/lib/${VM_CPU}/${VM_TYPE}:/usr/lib:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH + +cp ${TESTSRC}${FS}exestack-gap.c . + +# Copy the result of our @compile action: +cp ${TESTCLASSES}${FS}T.class . + +echo "Compilation flag: ${COMP_FLAG}" +# Note pthread may not be found thus invoke creation will fail to be created. +# Check to ensure you have a /usr/lib/libpthread.so if you don't please look +# for /usr/lib/`uname -m`-linux-gnu version ensure to add that path to below compilation. + +$gcc_cmd -DLINUX ${CFLAGS} -o stack-gap \ + -I${COMPILEJAVA}/include -I${COMPILEJAVA}/include/linux \ + -L${COMPILEJAVA}/jre/lib/${VM_CPU}/${VM_TYPE} \ + -ljvm -lpthread exestack-gap.c + +./stack-gap || exit $? +./stack-gap -XX:+DisablePrimordialThreadGuardPages || exit $?