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 detaching 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;
  54 static int _failures = 0;
  55 
  56 pid_t gettid() {
  57   return (pid_t) syscall(SYS_gettid);
  58 }
  59 
  60 static void handler(int sig, siginfo_t *si, void *unused) {
  61   _last_si_code = si->si_code;
  62   printf("Got SIGSEGV(%d) at address: 0x%lx\n",si->si_code, (long) si->si_addr);
  63   longjmp(context, 1);
  64 }
  65 
  66 void set_signal_handler() {
  67   static char altstack[SIGSTKSZ];
  68 
  69   stack_t ss = {
  70     .ss_size = SIGSTKSZ,
  71     .ss_flags = 0,
  72     .ss_sp = altstack
  73   };
  74 
  75   struct sigaction sa = {
  76     .sa_sigaction = handler,
  77     .sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESETHAND
  78   };
  79 
  80   sigaltstack(&ss, 0);
  81   sigemptyset(&sa.sa_mask);
  82   if (sigaction(SIGSEGV, &sa, NULL) == -1) {
  83     perror("sigaction");
  84   }
  85 }
  86 
  87 void *run_java_overflow (void *p) {
  88   JNIEnv *env;
  89   jclass class_id;
  90   jmethodID method_id;
  91 
  92   (*_jvm)->AttachCurrentThread(_jvm, (void**)&env, NULL);
  93 
  94   class_id = (*env)->FindClass (env, "DoOverflow");
  95   if (class_id == NULL) {
  96     printf("Test ERROR. Can't load class DoOverflow\n");
  97     exit(7);
  98   }
  99 
 100   method_id = (*env)->GetStaticMethodID(env, class_id, "printIt", "()V");
 101   if (method_id == NULL) {
 102     printf("Test ERROR. Can't find method DoOverflow.printIt\n");
 103     exit(7);
 104   }
 105 
 106   (*env)->CallStaticVoidMethod(env, class_id, method_id, NULL);
 107 
 108   (*_jvm)->DetachCurrentThread(_jvm);
 109 }
 110 
 111 void do_overflow(){
 112   int *p = alloca(sizeof(int));
 113   do_overflow();
 114 }
 115 
 116 void *run_native_overflow(void *p) {
 117   // Test that stack guard page is correctly set for initial and non initial thread
 118   // and correctly removed for the initial thread
 119   JNIEnv *env;
 120   jclass class_id;
 121   jmethodID method_id;
 122 
 123   printf("run_native_overflow %ld\n", (long) gettid());
 124 
 125   (*_jvm)->AttachCurrentThread(_jvm, (void **)&env, NULL);
 126 
 127   class_id = (*env)->FindClass (env, "DoOverflow");
 128   if (class_id == NULL) {
 129     printf("Test ERROR. Can't load class DoOverflow\n");
 130     exit(7);
 131   }
 132 
 133   method_id = (*env)->GetStaticMethodID (env, class_id, "printAlive", "()V");
 134   if (method_id == NULL) {
 135     printf("Test ERROR. Can't find method DoOverflow.printAlive\n");
 136     exit(7);
 137   }
 138 
 139   (*env)->CallStaticVoidMethod (env, class_id, method_id, NULL);
 140 
 141   set_signal_handler();
 142   if (! setjmp(context)) {
 143     do_overflow();
 144   }
 145 
 146   if (_last_si_code == SEGV_ACCERR) {
 147     printf("Test OK. Got access violation accessing guard page\n");
 148   }
 149 
 150   (*_jvm)->DetachCurrentThread(_jvm);
 151 
 152   if (getpid() != gettid()) {
 153     // For non-initial thread we doesn't unmap the region but call os::uncommit_memory and keep PROT_NONE
 154     // so if host has enough swap space we will get the same SEGV_ACCERR tring to access it
 155     // as if the guard page is present.
 156     // We have no ways to check this, so bail out, marking test as succeded
 157     printf("Test OK. Not initial thread\n");
 158     return NULL;
 159   }
 160 
 161   set_signal_handler();
 162   if (! setjmp(context)) {
 163     do_overflow();
 164   }
 165 
 166   if (_last_si_code == SEGV_ACCERR) {
 167       ++ _failures;
 168       printf("Test FAILED. Stack guard page is still there\n");
 169   }
 170   else {
 171     printf("Test OK. No stack guard page is present\n");
 172   }
 173 
 174   return NULL;
 175 }
 176 
 177 int main (int argc, const char** argv) {
 178   JavaVMInitArgs vm_args;
 179   JavaVMOption options[1];
 180   JNIEnv* env;
 181 
 182   printf("Test started with pid: %ld\n", (long) getpid());
 183 
 184   options[0].optionString = "-Xss320k";
 185 
 186   vm_args.version = JNI_VERSION_1_2;
 187   vm_args.ignoreUnrecognized = JNI_TRUE;
 188   vm_args.options = options;
 189   vm_args.nOptions = 1;
 190 
 191   if (JNI_CreateJavaVM (&_jvm, (void **)&env, &vm_args) < 0 ) {
 192     printf("Test ERROR. Can't create JavaVM\n");
 193     exit(7);
 194   }
 195 
 196   pthread_t thr;
 197 
 198   if (argc > 1 && strcmp(argv[1], "test_java_overflow") == 0) {
 199     printf("\nTesting JAVA_OVERFLOW\n");
 200 
 201     printf("Testing stack guard page behaviour for other thread\n");
 202     pthread_create (&thr, NULL, run_java_overflow, NULL);
 203     pthread_join (thr, NULL);
 204 
 205     printf("Testing stack guard page behaviour for initial thread\n");
 206     run_java_overflow(NULL);
 207     // This test crash on error
 208     return 0;
 209   }
 210 
 211   if (argc > 1 && strcmp(argv[1], "test_native_overflow") == 0) {
 212     printf("\nTesting NATIVE_OVERFLOW\n");
 213 
 214     printf("Testing stack guard page behaviour for other thread\n");
 215     pthread_create (&thr, NULL, run_native_overflow, NULL);
 216     pthread_join (thr, NULL);
 217 
 218     printf("Testing stack guard page behaviour for initial thread\n");
 219     run_native_overflow(NULL);
 220 
 221     return (_failures > 0) ? 1 : 0;
 222   }
 223 
 224   printf("Test ERROR. Unknown parameter should be test_java_overflow or test_native_overflow\n");
 225   exit(7);
 226 
 227 }