exeinvoke.c revision 12158:0fe2815ffa74
1/* 2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* This code tests the fact that we actually remove stack guard page when calling 25 * JavaThread::exit() i.e. when detaching from current thread. 26 * We overflow the stack and check that we get access error because of a guard page. 27 * Than we detach from vm thread and overflow stack once again. This time we shouldn't 28 * get access error because stack guard page is removed 29 * 30 * Notice: due a complicated interaction of signal handlers, the test may crash. 31 * It's OK - don't file a bug. 32 */ 33 34#include <assert.h> 35#include <jni.h> 36#include <alloca.h> 37#include <signal.h> 38#include <string.h> 39#include <sys/mman.h> 40#include <stdlib.h> 41#include <sys/ucontext.h> 42#include <setjmp.h> 43#include <unistd.h> 44#include <sys/syscall.h> 45#include <errno.h> 46 47#include <pthread.h> 48 49#define CLASS_PATH_OPT "-Djava.class.path=" 50 51JavaVM* _jvm; 52 53static jmp_buf context; 54 55static int _last_si_code = -1; 56static int _failures = 0; 57static int _rec_count = 0; 58static int _kp_rec_count = 0; 59 60pid_t gettid() { 61 return (pid_t) syscall(SYS_gettid); 62} 63 64static void handler(int sig, siginfo_t *si, void *unused) { 65 _last_si_code = si->si_code; 66 printf("Got SIGSEGV(%d) at address: 0x%lx\n",si->si_code, (long) si->si_addr); 67 longjmp(context, 1); 68} 69 70void set_signal_handler() { 71 static char altstack[SIGSTKSZ]; 72 73 stack_t ss = { 74 .ss_size = SIGSTKSZ, 75 .ss_flags = 0, 76 .ss_sp = altstack 77 }; 78 79 struct sigaction sa = { 80 .sa_sigaction = handler, 81 .sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESETHAND 82 }; 83 84 _last_si_code = -1; 85 86 sigaltstack(&ss, 0); 87 sigemptyset(&sa.sa_mask); 88 if (sigaction(SIGSEGV, &sa, NULL) == -1) { 89 fprintf(stderr, "Test ERROR. Can't set sigaction (%d)\n", errno); 90 exit(7); 91 } 92} 93 94void *run_java_overflow (void *p) { 95 JNIEnv *env; 96 jclass class_id; 97 jmethodID method_id; 98 int res; 99 100 res = (*_jvm)->AttachCurrentThread(_jvm, (void**)&env, NULL); 101 if (res != JNI_OK) { 102 fprintf(stderr, "Test ERROR. Can't attach to current thread\n"); 103 exit(7); 104 } 105 106 class_id = (*env)->FindClass (env, "DoOverflow"); 107 if (class_id == NULL) { 108 fprintf(stderr, "Test ERROR. Can't load class DoOverflow\n"); 109 exit(7); 110 } 111 112 method_id = (*env)->GetStaticMethodID(env, class_id, "printIt", "()V"); 113 if (method_id == NULL) { 114 fprintf(stderr, "Test ERROR. Can't find method DoOverflow.printIt\n"); 115 exit(7); 116 } 117 118 (*env)->CallStaticVoidMethod(env, class_id, method_id, NULL); 119 120 res = (*_jvm)->DetachCurrentThread(_jvm); 121 if (res != JNI_OK) { 122 fprintf(stderr, "Test ERROR. Can't call detach from current thread\n"); 123 exit(7); 124 } 125 return NULL; 126} 127 128void do_overflow(){ 129 int *p = alloca(sizeof(int)); 130 if (_kp_rec_count == 0 || _rec_count < _kp_rec_count) { 131 _rec_count ++; 132 do_overflow(); 133 } 134} 135 136void *run_native_overflow(void *p) { 137 // Test that stack guard page is correctly set for initial and non initial thread 138 // and correctly removed for the initial thread 139 JNIEnv *env; 140 jclass class_id; 141 jmethodID method_id; 142 int res; 143 144 printf("run_native_overflow %ld\n", (long) gettid()); 145 146 res = (*_jvm)->AttachCurrentThread(_jvm, (void **)&env, NULL); 147 if (res != JNI_OK) { 148 fprintf(stderr, "Test ERROR. Can't attach to current thread\n"); 149 exit(7); 150 } 151 152 class_id = (*env)->FindClass (env, "DoOverflow"); 153 if (class_id == NULL) { 154 fprintf(stderr, "Test ERROR. Can't load class DoOverflow\n"); 155 exit(7); 156 } 157 158 method_id = (*env)->GetStaticMethodID (env, class_id, "printAlive", "()V"); 159 if (method_id == NULL) { 160 fprintf(stderr, "Test ERROR. Can't find method DoOverflow.printAlive\n"); 161 exit(7); 162 } 163 164 (*env)->CallStaticVoidMethod (env, class_id, method_id, NULL); 165 166 set_signal_handler(); 167 if (! setjmp(context)) { 168 do_overflow(); 169 } 170 171 if (_last_si_code == SEGV_ACCERR) { 172 printf("Test PASSED. Got access violation accessing guard page at %d\n", _rec_count); 173 } 174 175 res = (*_jvm)->DetachCurrentThread(_jvm); 176 if (res != JNI_OK) { 177 fprintf(stderr, "Test ERROR. Can't call detach from current thread\n"); 178 exit(7); 179 } 180 181 if (getpid() != gettid()) { 182 // For non-initial thread we don't unmap the region but call os::uncommit_memory and keep PROT_NONE 183 // so if host has enough swap space we will get the same SEGV with code SEGV_ACCERR(2) trying 184 // to access it as if the guard page is present. 185 // We have no way to check this, so bail out, marking test as succeeded 186 printf("Test PASSED. Not initial thread\n"); 187 return NULL; 188 } 189 190 // Limit depth of recursion for second run. It can't exceed one for first run. 191 _kp_rec_count = _rec_count; 192 _rec_count = 0; 193 194 set_signal_handler(); 195 if (! setjmp(context)) { 196 do_overflow(); 197 } 198 199 if (_last_si_code == SEGV_ACCERR) { 200 ++ _failures; 201 fprintf(stderr,"Test FAILED. Stack guard page is still there at %d\n", _rec_count); 202 } else if (_last_si_code == -1) { 203 printf("Test PASSED. No stack guard page is present. Maximum recursion level reached at %d\n", _rec_count); 204 } 205 else{ 206 printf("Test PASSED. No stack guard page is present. SIGSEGV(%d) at %d\n", _last_si_code, _rec_count); 207 } 208 209 return NULL; 210} 211 212void usage() { 213 fprintf(stderr, "Usage: invoke test_java_overflow\n"); 214 fprintf(stderr, " invoke test_native_overflow\n"); 215} 216 217 218int main (int argc, const char** argv) { 219 JavaVMInitArgs vm_args; 220 JavaVMOption options[3]; 221 JNIEnv* env; 222 int optlen; 223 char *javaclasspath = NULL; 224 char javaclasspathopt[4096]; 225 226 printf("Test started with pid: %ld\n", (long) getpid()); 227 228 /* set the java class path so the DoOverflow class can be found */ 229 javaclasspath = getenv("CLASSPATH"); 230 231 if (javaclasspath == NULL) { 232 fprintf(stderr, "Test ERROR. CLASSPATH is not set\n"); 233 exit(7); 234 } 235 optlen = strlen(CLASS_PATH_OPT) + strlen(javaclasspath) + 1; 236 if (optlen > 4096) { 237 fprintf(stderr, "Test ERROR. CLASSPATH is too long\n"); 238 exit(7); 239 } 240 snprintf(javaclasspathopt, sizeof(javaclasspathopt), "%s%s", 241 CLASS_PATH_OPT, javaclasspath); 242 243 options[0].optionString = "-Xint"; 244 options[1].optionString = "-Xss1M"; 245 options[2].optionString = javaclasspathopt; 246 247 vm_args.version = JNI_VERSION_1_2; 248 vm_args.ignoreUnrecognized = JNI_TRUE; 249 vm_args.options = options; 250 vm_args.nOptions = 3; 251 252 if (JNI_CreateJavaVM (&_jvm, (void **)&env, &vm_args) < 0 ) { 253 fprintf(stderr, "Test ERROR. Can't create JavaVM\n"); 254 exit(7); 255 } 256 257 pthread_t thr; 258 259 if (argc > 1 && strcmp(argv[1], "test_java_overflow") == 0) { 260 printf("\nTesting JAVA_OVERFLOW\n"); 261 262 printf("Testing stack guard page behaviour for other thread\n"); 263 pthread_create (&thr, NULL, run_java_overflow, NULL); 264 pthread_join (thr, NULL); 265 266 printf("Testing stack guard page behaviour for initial thread\n"); 267 run_java_overflow(NULL); 268 // This test crash on error 269 exit(0); 270 } 271 272 if (argc > 1 && strcmp(argv[1], "test_native_overflow") == 0) { 273 printf("\nTesting NATIVE_OVERFLOW\n"); 274 275 printf("Testing stack guard page behaviour for other thread\n"); 276 pthread_create (&thr, NULL, run_native_overflow, NULL); 277 pthread_join (thr, NULL); 278 279 printf("Testing stack guard page behaviour for initial thread\n"); 280 run_native_overflow(NULL); 281 282 exit((_failures > 0) ? 1 : 0); 283 } 284 285 fprintf(stderr, "Test ERROR. Unknown parameter %s\n", ((argc > 1) ? argv[1] : "none")); 286 usage(); 287 exit(7); 288} 289