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