1 /*
   2  * Copyright (c) 2010, 2014, 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 
  25 /* This code tests the fact that we actually remove stack guard page when calling
  26  * JavaThread::exit() i.e. when detaching from current thread.
  27  * We overflow the stack and check that we get access error because of a guard page.
  28  * Than we detach from vm thread and overflow stack once again. This time we shouldn't
  29  * get access error because stack guard page is removed
  30  *
  31  * Notice: due a complicated interaction of signal handlers, the test may crash.
  32  * It's OK - don't file a bug.
  33  */
  34 
  35 #include <assert.h>
  36 #include <jni.h>
  37 #include <alloca.h>
  38 #include <signal.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 JavaVM* _jvm;
  50 
  51 static jmp_buf  context;
  52 
  53 static int _last_si_code = -1;
  54 static int _failures = 0;
  55 static int _rec_count = 0;
  56 static int _kp_rec_count = 0;
  57 
  58 pid_t gettid() {
  59   return (pid_t) syscall(SYS_gettid);
  60 }
  61 
  62 static void handler(int sig, siginfo_t *si, void *unused) {
  63   _last_si_code = si->si_code;
  64   printf("Got SIGSEGV(%d) at address: 0x%lx\n",si->si_code, (long) si->si_addr);
  65   longjmp(context, 1);
  66 }
  67 
  68 void set_signal_handler() {
  69   static char altstack[SIGSTKSZ];
  70 
  71   stack_t ss = {
  72     .ss_size = SIGSTKSZ,
  73     .ss_flags = 0,
  74     .ss_sp = altstack
  75   };
  76 
  77   struct sigaction sa = {
  78     .sa_sigaction = handler,
  79     .sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESETHAND
  80   };
  81 
  82   _last_si_code = -1;
  83 
  84   sigaltstack(&ss, 0);
  85   sigemptyset(&sa.sa_mask);
  86   if (sigaction(SIGSEGV, &sa, NULL) == -1) {
  87     fprintf(stderr, "Test ERROR. Can't set sigaction (%d)\n", errno);
  88     exit(7);
  89   }
  90 }
  91 
  92 void *run_java_overflow (void *p) {
  93   JNIEnv *env;
  94   jclass class_id;
  95   jmethodID method_id;
  96   int res;
  97 
  98   res = (*_jvm)->AttachCurrentThread(_jvm, (void**)&env, NULL);
  99   if (res != JNI_OK) {
 100     fprintf(stderr, "Test ERROR. Can't attach to current thread\n");
 101     exit(7);
 102   }
 103 
 104   class_id = (*env)->FindClass (env, "DoOverflow");
 105   if (class_id == NULL) {
 106     fprintf(stderr, "Test ERROR. Can't load class DoOverflow\n");
 107     exit(7);
 108   }
 109 
 110   method_id = (*env)->GetStaticMethodID(env, class_id, "printIt", "()V");
 111   if (method_id == NULL) {
 112     fprintf(stderr, "Test ERROR. Can't find method DoOverflow.printIt\n");
 113     exit(7);
 114   }
 115 
 116   (*env)->CallStaticVoidMethod(env, class_id, method_id, NULL);
 117 
 118   res = (*_jvm)->DetachCurrentThread(_jvm);
 119   if (res != JNI_OK) {
 120     fprintf(stderr, "Test ERROR. Can't call detach from current thread\n");
 121     exit(7);
 122   }
 123 }
 124 
 125 void do_overflow(){
 126   int *p = alloca(sizeof(int));
 127   if (_kp_rec_count == 0 || _rec_count < _kp_rec_count) {
 128       _rec_count ++;
 129       do_overflow();
 130   }
 131 }
 132 
 133 void *run_native_overflow(void *p) {
 134   // Test that stack guard page is correctly set for initial and non initial thread
 135   // and correctly removed for the initial thread
 136   JNIEnv *env;
 137   jclass class_id;
 138   jmethodID method_id;
 139   int res;
 140 
 141   printf("run_native_overflow %ld\n", (long) gettid());
 142 
 143   res = (*_jvm)->AttachCurrentThread(_jvm, (void **)&env, NULL);
 144   if (res != JNI_OK) {
 145     fprintf(stderr, "Test ERROR. Can't attach to current thread\n");
 146     exit(7);
 147   }
 148 
 149   class_id = (*env)->FindClass (env, "DoOverflow");
 150   if (class_id == NULL) {
 151     fprintf(stderr, "Test ERROR. Can't load class DoOverflow\n");
 152     exit(7);
 153   }
 154 
 155   method_id = (*env)->GetStaticMethodID (env, class_id, "printAlive", "()V");
 156   if (method_id == NULL) {
 157     fprintf(stderr, "Test ERROR. Can't find method DoOverflow.printAlive\n");
 158     exit(7);
 159   }
 160 
 161   (*env)->CallStaticVoidMethod (env, class_id, method_id, NULL);
 162 
 163   set_signal_handler();
 164   if (! setjmp(context)) {
 165     do_overflow();
 166   }
 167 
 168   if (_last_si_code == SEGV_ACCERR) {
 169     printf("Test PASSED. Got access violation accessing guard page at %d\n", _rec_count);
 170   }
 171 
 172   res = (*_jvm)->DetachCurrentThread(_jvm);
 173   if (res != JNI_OK) {
 174     fprintf(stderr, "Test ERROR. Can't call detach from current thread\n");
 175     exit(7);
 176   }
 177 
 178   if (getpid() != gettid()) {
 179     // For non-initial thread we don't unmap the region but call os::uncommit_memory and keep PROT_NONE
 180     // so if host has enough swap space we will get the same SEGV with code SEGV_ACCERR(2) trying
 181     // to access it as if the guard page is present.
 182     // We have no way to check this, so bail out, marking test as succeeded
 183     printf("Test PASSED. Not initial thread\n");
 184     return NULL;
 185   }
 186 
 187   // Limit depth of recursion for second run. It can't exceed one for first run.
 188   _kp_rec_count = _rec_count;
 189   _rec_count = 0;
 190 
 191   set_signal_handler();
 192   if (! setjmp(context)) {
 193     do_overflow();
 194   }
 195 
 196   if (_last_si_code == SEGV_ACCERR) {
 197       ++ _failures;
 198       fprintf(stderr,"Test FAILED. Stack guard page is still there at %d\n", _rec_count);
 199   } else if (_last_si_code == -1) {
 200       printf("Test PASSED. No stack guard page is present. Maximum recursion level reached at %d\n", _rec_count);
 201   }
 202   else{
 203       printf("Test PASSED. No stack guard page is present. SIGSEGV(%d) at %d\n", _last_si_code, _rec_count);
 204   }
 205 
 206   return NULL;
 207 }
 208 
 209 void usage() {
 210   fprintf(stderr, "Usage: invoke test_java_overflow\n");
 211   fprintf(stderr, "       invoke test_native_overflow\n");
 212   exit(7);
 213 }
 214 
 215 
 216 int main (int argc, const char** argv) {
 217   JavaVMInitArgs vm_args;
 218   JavaVMOption options[2];
 219   JNIEnv* env;
 220 
 221   printf("Test started with pid: %ld\n", (long) getpid());
 222 
 223   options[0].optionString = "-Xint";
 224   options[1].optionString = "-Xss328k";
 225 
 226   vm_args.version = JNI_VERSION_1_2;
 227   vm_args.ignoreUnrecognized = JNI_TRUE;
 228   vm_args.options = options;
 229   vm_args.nOptions = 2;
 230 
 231   if (JNI_CreateJavaVM (&_jvm, (void **)&env, &vm_args) < 0 ) {
 232     fprintf(stderr, "Test ERROR. Can't create JavaVM\n");
 233     exit(7);
 234   }
 235 
 236   pthread_t thr;
 237 
 238   if (argc > 1 && strcmp(argv[1], "test_java_overflow") == 0) {
 239     printf("\nTesting JAVA_OVERFLOW\n");
 240 
 241     printf("Testing stack guard page behaviour for other thread\n");
 242     pthread_create (&thr, NULL, run_java_overflow, NULL);
 243     pthread_join (thr, NULL);
 244 
 245     printf("Testing stack guard page behaviour for initial thread\n");
 246     run_java_overflow(NULL);
 247     // This test crash on error
 248     exit(0);
 249   }
 250 
 251   if (argc > 1 && strcmp(argv[1], "test_native_overflow") == 0) {
 252     printf("\nTesting NATIVE_OVERFLOW\n");
 253 
 254     printf("Testing stack guard page behaviour for other thread\n");
 255     pthread_create (&thr, NULL, run_native_overflow, NULL);
 256     pthread_join (thr, NULL);
 257 
 258     printf("Testing stack guard page behaviour for initial thread\n");
 259     run_native_overflow(NULL);
 260 
 261     exit((_failures > 0) ? 1 : 0);
 262   }
 263 
 264   fprintf(stderr, "Test ERROR. Unknown parameter %s\n", ((argc > 1) ? argv[1] : "none"));
 265   usage();
 266 }