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