2014-05-22 20:22:09 +00:00
|
|
|
/*
|
2016-07-15 17:46:08 +00:00
|
|
|
* Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
|
2014-05-22 20:22:09 +00:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* This code tests the fact that we actually remove stack guard page when calling
|
|
|
|
* JavaThread::exit() i.e. when detaching from current thread.
|
|
|
|
* We overflow the stack and check that we get access error because of a guard page.
|
|
|
|
* Than we detach from vm thread and overflow stack once again. This time we shouldn't
|
|
|
|
* get access error because stack guard page is removed
|
|
|
|
*
|
|
|
|
* Notice: due a complicated interaction of signal handlers, the test may crash.
|
|
|
|
* It's OK - don't file a bug.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <jni.h>
|
|
|
|
#include <alloca.h>
|
|
|
|
#include <signal.h>
|
2016-07-15 17:46:08 +00:00
|
|
|
#include <string.h>
|
2014-05-22 20:22:09 +00:00
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/ucontext.h>
|
|
|
|
#include <setjmp.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
2016-07-15 17:46:08 +00:00
|
|
|
#define CLASS_PATH_OPT "-Djava.class.path="
|
|
|
|
|
2014-05-22 20:22:09 +00:00
|
|
|
JavaVM* _jvm;
|
|
|
|
|
|
|
|
static jmp_buf context;
|
|
|
|
|
|
|
|
static int _last_si_code = -1;
|
|
|
|
static int _failures = 0;
|
|
|
|
static int _rec_count = 0;
|
|
|
|
static int _kp_rec_count = 0;
|
|
|
|
|
|
|
|
pid_t gettid() {
|
|
|
|
return (pid_t) syscall(SYS_gettid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handler(int sig, siginfo_t *si, void *unused) {
|
|
|
|
_last_si_code = si->si_code;
|
|
|
|
printf("Got SIGSEGV(%d) at address: 0x%lx\n",si->si_code, (long) si->si_addr);
|
|
|
|
longjmp(context, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_signal_handler() {
|
|
|
|
static char altstack[SIGSTKSZ];
|
|
|
|
|
|
|
|
stack_t ss = {
|
|
|
|
.ss_size = SIGSTKSZ,
|
|
|
|
.ss_flags = 0,
|
|
|
|
.ss_sp = altstack
|
|
|
|
};
|
|
|
|
|
|
|
|
struct sigaction sa = {
|
|
|
|
.sa_sigaction = handler,
|
|
|
|
.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESETHAND
|
|
|
|
};
|
|
|
|
|
|
|
|
_last_si_code = -1;
|
|
|
|
|
|
|
|
sigaltstack(&ss, 0);
|
|
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
if (sigaction(SIGSEGV, &sa, NULL) == -1) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't set sigaction (%d)\n", errno);
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void *run_java_overflow (void *p) {
|
|
|
|
JNIEnv *env;
|
|
|
|
jclass class_id;
|
|
|
|
jmethodID method_id;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
res = (*_jvm)->AttachCurrentThread(_jvm, (void**)&env, NULL);
|
|
|
|
if (res != JNI_OK) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't attach to current thread\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
|
|
|
|
class_id = (*env)->FindClass (env, "DoOverflow");
|
|
|
|
if (class_id == NULL) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't load class DoOverflow\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
|
|
|
|
method_id = (*env)->GetStaticMethodID(env, class_id, "printIt", "()V");
|
|
|
|
if (method_id == NULL) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't find method DoOverflow.printIt\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
|
|
|
|
(*env)->CallStaticVoidMethod(env, class_id, method_id, NULL);
|
|
|
|
|
|
|
|
res = (*_jvm)->DetachCurrentThread(_jvm);
|
|
|
|
if (res != JNI_OK) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't call detach from current thread\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
2016-07-15 17:46:08 +00:00
|
|
|
return NULL;
|
2014-05-22 20:22:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void do_overflow(){
|
|
|
|
int *p = alloca(sizeof(int));
|
|
|
|
if (_kp_rec_count == 0 || _rec_count < _kp_rec_count) {
|
|
|
|
_rec_count ++;
|
|
|
|
do_overflow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void *run_native_overflow(void *p) {
|
|
|
|
// Test that stack guard page is correctly set for initial and non initial thread
|
|
|
|
// and correctly removed for the initial thread
|
|
|
|
JNIEnv *env;
|
|
|
|
jclass class_id;
|
|
|
|
jmethodID method_id;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
printf("run_native_overflow %ld\n", (long) gettid());
|
|
|
|
|
|
|
|
res = (*_jvm)->AttachCurrentThread(_jvm, (void **)&env, NULL);
|
|
|
|
if (res != JNI_OK) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't attach to current thread\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
|
|
|
|
class_id = (*env)->FindClass (env, "DoOverflow");
|
|
|
|
if (class_id == NULL) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't load class DoOverflow\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
|
|
|
|
method_id = (*env)->GetStaticMethodID (env, class_id, "printAlive", "()V");
|
|
|
|
if (method_id == NULL) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't find method DoOverflow.printAlive\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
|
|
|
|
(*env)->CallStaticVoidMethod (env, class_id, method_id, NULL);
|
|
|
|
|
|
|
|
set_signal_handler();
|
|
|
|
if (! setjmp(context)) {
|
|
|
|
do_overflow();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_last_si_code == SEGV_ACCERR) {
|
|
|
|
printf("Test PASSED. Got access violation accessing guard page at %d\n", _rec_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
res = (*_jvm)->DetachCurrentThread(_jvm);
|
|
|
|
if (res != JNI_OK) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't call detach from current thread\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getpid() != gettid()) {
|
|
|
|
// For non-initial thread we don't unmap the region but call os::uncommit_memory and keep PROT_NONE
|
|
|
|
// so if host has enough swap space we will get the same SEGV with code SEGV_ACCERR(2) trying
|
|
|
|
// to access it as if the guard page is present.
|
|
|
|
// We have no way to check this, so bail out, marking test as succeeded
|
|
|
|
printf("Test PASSED. Not initial thread\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Limit depth of recursion for second run. It can't exceed one for first run.
|
|
|
|
_kp_rec_count = _rec_count;
|
|
|
|
_rec_count = 0;
|
|
|
|
|
|
|
|
set_signal_handler();
|
|
|
|
if (! setjmp(context)) {
|
|
|
|
do_overflow();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_last_si_code == SEGV_ACCERR) {
|
|
|
|
++ _failures;
|
|
|
|
fprintf(stderr,"Test FAILED. Stack guard page is still there at %d\n", _rec_count);
|
|
|
|
} else if (_last_si_code == -1) {
|
|
|
|
printf("Test PASSED. No stack guard page is present. Maximum recursion level reached at %d\n", _rec_count);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
printf("Test PASSED. No stack guard page is present. SIGSEGV(%d) at %d\n", _last_si_code, _rec_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void usage() {
|
|
|
|
fprintf(stderr, "Usage: invoke test_java_overflow\n");
|
|
|
|
fprintf(stderr, " invoke test_native_overflow\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int main (int argc, const char** argv) {
|
|
|
|
JavaVMInitArgs vm_args;
|
2016-07-15 17:46:08 +00:00
|
|
|
JavaVMOption options[3];
|
2014-05-22 20:22:09 +00:00
|
|
|
JNIEnv* env;
|
2016-07-15 17:46:08 +00:00
|
|
|
int optlen;
|
|
|
|
char *javaclasspath = NULL;
|
|
|
|
char javaclasspathopt[4096];
|
2014-05-22 20:22:09 +00:00
|
|
|
|
|
|
|
printf("Test started with pid: %ld\n", (long) getpid());
|
|
|
|
|
2016-07-15 17:46:08 +00:00
|
|
|
/* set the java class path so the DoOverflow class can be found */
|
|
|
|
javaclasspath = getenv("CLASSPATH");
|
|
|
|
|
|
|
|
if (javaclasspath == NULL) {
|
|
|
|
fprintf(stderr, "Test ERROR. CLASSPATH is not set\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
optlen = strlen(CLASS_PATH_OPT) + strlen(javaclasspath) + 1;
|
|
|
|
if (optlen > 4096) {
|
|
|
|
fprintf(stderr, "Test ERROR. CLASSPATH is too long\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
snprintf(javaclasspathopt, sizeof(javaclasspathopt), "%s%s",
|
|
|
|
CLASS_PATH_OPT, javaclasspath);
|
|
|
|
|
2014-05-22 20:22:09 +00:00
|
|
|
options[0].optionString = "-Xint";
|
2016-08-02 20:48:07 +00:00
|
|
|
options[1].optionString = "-Xss1M";
|
2016-07-15 17:46:08 +00:00
|
|
|
options[2].optionString = javaclasspathopt;
|
2014-05-22 20:22:09 +00:00
|
|
|
|
|
|
|
vm_args.version = JNI_VERSION_1_2;
|
|
|
|
vm_args.ignoreUnrecognized = JNI_TRUE;
|
|
|
|
vm_args.options = options;
|
2016-07-15 17:46:08 +00:00
|
|
|
vm_args.nOptions = 3;
|
2014-05-22 20:22:09 +00:00
|
|
|
|
|
|
|
if (JNI_CreateJavaVM (&_jvm, (void **)&env, &vm_args) < 0 ) {
|
|
|
|
fprintf(stderr, "Test ERROR. Can't create JavaVM\n");
|
|
|
|
exit(7);
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_t thr;
|
|
|
|
|
|
|
|
if (argc > 1 && strcmp(argv[1], "test_java_overflow") == 0) {
|
|
|
|
printf("\nTesting JAVA_OVERFLOW\n");
|
|
|
|
|
|
|
|
printf("Testing stack guard page behaviour for other thread\n");
|
|
|
|
pthread_create (&thr, NULL, run_java_overflow, NULL);
|
|
|
|
pthread_join (thr, NULL);
|
|
|
|
|
|
|
|
printf("Testing stack guard page behaviour for initial thread\n");
|
|
|
|
run_java_overflow(NULL);
|
|
|
|
// This test crash on error
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc > 1 && strcmp(argv[1], "test_native_overflow") == 0) {
|
|
|
|
printf("\nTesting NATIVE_OVERFLOW\n");
|
|
|
|
|
|
|
|
printf("Testing stack guard page behaviour for other thread\n");
|
|
|
|
pthread_create (&thr, NULL, run_native_overflow, NULL);
|
|
|
|
pthread_join (thr, NULL);
|
|
|
|
|
|
|
|
printf("Testing stack guard page behaviour for initial thread\n");
|
|
|
|
run_native_overflow(NULL);
|
|
|
|
|
|
|
|
exit((_failures > 0) ? 1 : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Test ERROR. Unknown parameter %s\n", ((argc > 1) ? argv[1] : "none"));
|
|
|
|
usage();
|
2016-07-15 17:46:08 +00:00
|
|
|
exit(7);
|
2014-05-22 20:22:09 +00:00
|
|
|
}
|