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 
  51 JavaVM* _jvm;
  52 
  53 static jmp_buf  context;
  54 
  55 static int _last_si_code = -1;
  56 static int _failures = 0;
  57 static int _rec_count = 0;
  58 static int _kp_rec_count = 0;
  59 
  60 pid_t gettid() {
  61   return (pid_t) syscall(SYS_gettid);
  62 }
  63 
  64 static 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 
  70 void 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 
  94 void *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 
 128 void 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 
 136 void *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 
 212 void usage() {
 213   fprintf(stderr, "Usage: invoke test_java_overflow\n");
 214   fprintf(stderr, "       invoke test_native_overflow\n");
 215 }
 216 
 217 
 218 int 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 }