1 /* 2 * Copyright (c) 2005, 2015, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 #include <sys/types.h> 26 #include <sys/stat.h> 27 #include <door.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <signal.h> 31 #include <string.h> 32 #include <fcntl.h> 33 #include <errno.h> 34 #include <limits.h> 35 36 #include "jni.h" 37 #include "jni_util.h" 38 #include "jvm.h" 39 40 #include "sun_tools_attach_VirtualMachineImpl.h" 41 42 #define RESTARTABLE(_cmd, _result) do { \ 43 do { \ 44 _result = _cmd; \ 45 } while((_result == -1) && (errno == EINTR)); \ 46 } while(0) 47 48 /* 49 * Declare library specific JNI_Onload entry if static build 50 */ 51 DEF_STATIC_JNI_OnLoad 52 53 /* 54 * Class: sun_tools_attach_VirtualMachineImpl 55 * Method: open 56 * Signature: (Ljava/lang/String;)I 57 */ 58 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_open 59 (JNIEnv *env, jclass cls, jstring path) 60 { 61 jboolean isCopy; 62 const char* p = GetStringPlatformChars(env, path, &isCopy); 63 if (p == NULL) { 64 return 0; 65 } else { 66 int fd; 67 int err = 0; 68 69 fd = open(p, O_RDWR); 70 if (fd == -1) { 71 err = errno; 72 } 73 74 if (isCopy) { 75 JNU_ReleaseStringPlatformChars(env, path, p); 76 } 77 78 if (fd == -1) { 79 if (err == ENOENT) { 80 JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL); 81 } else { 82 char* msg = strdup(strerror(err)); 83 JNU_ThrowIOException(env, msg); 84 if (msg != NULL) { 85 free(msg); 86 } 87 } 88 } 89 return fd; 90 } 91 } 92 93 /* 94 * Class: sun_tools_attach_VirtualMachineImpl 95 * Method: checkPermissions 96 * Signature: (Ljava/lang/String;)V 97 */ 98 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions 99 (JNIEnv *env, jclass cls, jstring path) 100 { 101 jboolean isCopy; 102 const char* p = GetStringPlatformChars(env, path, &isCopy); 103 if (p != NULL) { 104 struct stat64 sb; 105 uid_t uid, gid; 106 int res; 107 108 /* 109 * Check that the path is owned by the effective uid/gid of this 110 * process. Also check that group/other access is not allowed. 111 */ 112 uid = geteuid(); 113 gid = getegid(); 114 115 res = stat64(p, &sb); 116 if (res != 0) { 117 /* save errno */ 118 res = errno; 119 } 120 121 if (res == 0) { 122 char msg[100]; 123 jboolean isError = JNI_FALSE; 124 if (sb.st_uid != uid) { 125 jio_snprintf(msg, sizeof(msg)-1, 126 "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid); 127 isError = JNI_TRUE; 128 } else if (sb.st_gid != gid) { 129 jio_snprintf(msg, sizeof(msg)-1, 130 "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid); 131 isError = JNI_TRUE; 132 } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) { 133 jio_snprintf(msg, sizeof(msg)-1, 134 "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777); 135 isError = JNI_TRUE; 136 } 137 if (isError) { 138 char buf[256]; 139 jio_snprintf(buf, sizeof(buf)-1, "well-known file %s is not secure: %s", p, msg); 140 JNU_ThrowIOException(env, buf); 141 } 142 } else { 143 char* msg = strdup(strerror(res)); 144 JNU_ThrowIOException(env, msg); 145 if (msg != NULL) { 146 free(msg); 147 } 148 } 149 150 if (isCopy) { 151 JNU_ReleaseStringPlatformChars(env, path, p); 152 } 153 } 154 } 155 156 /* 157 * Class: sun_tools_attach_VirtualMachineImpl 158 * Method: close 159 * Signature: (I)V 160 */ 161 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close 162 (JNIEnv *env, jclass cls, jint fd) 163 { 164 int ret; 165 RESTARTABLE(close(fd), ret); 166 } 167 168 /* 169 * Class: sun_tools_attach_VirtualMachineImpl 170 * Method: read 171 * Signature: (I[BI)I 172 */ 173 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read 174 (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen) 175 { 176 unsigned char buf[128]; 177 size_t len = sizeof(buf); 178 ssize_t n; 179 180 size_t remaining = (size_t)(baLen - off); 181 if (len > remaining) { 182 len = remaining; 183 } 184 185 RESTARTABLE(read(fd, buf, len), n); 186 if (n == -1) { 187 JNU_ThrowIOExceptionWithLastError(env, "read"); 188 } else { 189 if (n == 0) { 190 n = -1; // EOF 191 } else { 192 (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf)); 193 } 194 } 195 return n; 196 } 197 198 /* 199 * Class: sun_tools_attach_VirtualMachineImpl 200 * Method: sigquit 201 * Signature: (I)V 202 */ 203 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sigquit 204 (JNIEnv *env, jclass cls, jint pid) 205 { 206 if (kill((pid_t)pid, SIGQUIT) == -1) { 207 JNU_ThrowIOExceptionWithLastError(env, "kill"); 208 } 209 } 210 211 /* 212 * A simple table to translate some known errors into reasonable 213 * error messages 214 */ 215 static struct { 216 jint err; 217 const char* msg; 218 } const error_messages[] = { 219 { 100, "Bad request" }, 220 { 101, "Protocol mismatch" }, 221 { 102, "Resource failure" }, 222 { 103, "Internal error" }, 223 { 104, "Permission denied" }, 224 }; 225 226 /* 227 * Lookup the given error code and return the appropriate 228 * message. If not found return NULL. 229 */ 230 static const char* translate_error(jint err) { 231 int table_size = sizeof(error_messages) / sizeof(error_messages[0]); 232 int i; 233 234 for (i=0; i<table_size; i++) { 235 if (err == error_messages[i].err) { 236 return error_messages[i].msg; 237 } 238 } 239 return NULL; 240 } 241 242 /* 243 * Current protocol version 244 */ 245 static const char* PROTOCOL_VERSION = "1"; 246 247 /* 248 * Class: sun_tools_attach_VirtualMachineImpl 249 * Method: enqueue 250 * Signature: (JILjava/lang/String;[Ljava/lang/Object;)V 251 */ 252 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue 253 (JNIEnv *env, jclass cls, jint fd, jstring cmd, jobjectArray args) 254 { 255 jint arg_count, i; 256 size_t size; 257 jboolean isCopy; 258 door_arg_t door_args; 259 char res_buffer[128]; 260 jint result = -1; 261 int rc; 262 const char* cstr; 263 char* buf; 264 265 /* 266 * First we get the command string and create the start of the 267 * argument string to send to the target VM: 268 * <ver>\0<cmd>\0 269 */ 270 cstr = JNU_GetStringPlatformChars(env, cmd, &isCopy); 271 if (cstr == NULL) { 272 return -1; /* pending exception */ 273 } 274 size = strlen(PROTOCOL_VERSION) + strlen(cstr) + 2; 275 buf = (char*)malloc(size); 276 if (buf != NULL) { 277 char* pos = buf; 278 strcpy(buf, PROTOCOL_VERSION); 279 pos += strlen(PROTOCOL_VERSION)+1; 280 strcpy(pos, cstr); 281 } 282 if (isCopy) { 283 JNU_ReleaseStringPlatformChars(env, cmd, cstr); 284 } 285 if (buf == NULL) { 286 JNU_ThrowOutOfMemoryError(env, "malloc failed"); 287 return -1; 288 } 289 290 /* 291 * Next we iterate over the arguments and extend the buffer 292 * to include them. 293 */ 294 arg_count = (*env)->GetArrayLength(env, args); 295 296 for (i=0; i<arg_count; i++) { 297 jobject obj = (*env)->GetObjectArrayElement(env, args, i); 298 if (obj != NULL) { 299 cstr = JNU_GetStringPlatformChars(env, obj, &isCopy); 300 if (cstr != NULL) { 301 size_t len = strlen(cstr); 302 char* newbuf = (char*)realloc(buf, size+len+1); 303 if (newbuf != NULL) { 304 buf = newbuf; 305 strcpy(buf+size, cstr); 306 size += len+1; 307 } 308 if (isCopy) { 309 JNU_ReleaseStringPlatformChars(env, obj, cstr); 310 } 311 if (newbuf == NULL) { 312 free(buf); 313 JNU_ThrowOutOfMemoryError(env, "realloc failed"); 314 return -1; 315 } 316 } 317 } 318 if ((*env)->ExceptionOccurred(env)) { 319 free(buf); 320 return -1; 321 } 322 } 323 324 /* 325 * The arguments to the door function are in 'buf' so we now 326 * do the door call 327 */ 328 door_args.data_ptr = buf; 329 door_args.data_size = size; 330 door_args.desc_ptr = NULL; 331 door_args.desc_num = 0; 332 door_args.rbuf = (char*)&res_buffer; 333 door_args.rsize = sizeof(res_buffer); 334 335 RESTARTABLE(door_call(fd, &door_args), rc); 336 337 /* 338 * door_call failed 339 */ 340 if (rc == -1) { 341 JNU_ThrowIOExceptionWithLastError(env, "door_call"); 342 } else { 343 /* 344 * door_call succeeded but the call didn't return the expected jint. 345 */ 346 if (door_args.data_size < sizeof(jint)) { 347 JNU_ThrowIOException(env, "Enqueue error - reason unknown as result is truncated!"); 348 } else { 349 jint* res = (jint*)(door_args.data_ptr); 350 if (*res != JNI_OK) { 351 const char* msg = translate_error(*res); 352 char buf[255]; 353 if (msg == NULL) { 354 sprintf(buf, "Unable to enqueue command to target VM: %d", *res); 355 } else { 356 sprintf(buf, "Unable to enqueue command to target VM: %s", msg); 357 } 358 JNU_ThrowIOException(env, buf); 359 } else { 360 /* 361 * The door call should return a file descriptor to one end of 362 * a socket pair 363 */ 364 if ((door_args.desc_ptr != NULL) && 365 (door_args.desc_num == 1) && 366 (door_args.desc_ptr->d_attributes & DOOR_DESCRIPTOR)) { 367 result = door_args.desc_ptr->d_data.d_desc.d_descriptor; 368 } else { 369 JNU_ThrowIOException(env, "Reply from enqueue missing descriptor!"); 370 } 371 } 372 } 373 } 374 375 free(buf); 376 return result; 377 }