1 /*
  2  * Copyright (c) 1998, 2017, 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 
 26 #include "util.h"
 27 #include "utf_util.h"
 28 #include "stream.h"
 29 #include "inStream.h"
 30 #include "transport.h"
 31 #include "bag.h"
 32 #include "commonRef.h"
 33 #include "FrameID.h"
 34 
 35 #define INITIAL_REF_ALLOC 50
 36 #define SMALLEST(a, b) ((a) < (b)) ? (a) : (b)
 37 
 38 /*
 39  * TO DO: Support processing of replies through command input streams.
 40  */
 41 void
 42 inStream_init(PacketInputStream *stream, jdwpPacket packet)
 43 {
 44     stream->packet = packet;
 45     stream->error = JDWP_ERROR(NONE);
 46     stream->left = packet.type.cmd.len - JDWP_HEADER_SIZE;
 47     stream->current = packet.type.cmd.data;
 48     stream->refs = bagCreateBag(sizeof(jobject), INITIAL_REF_ALLOC);
 49     if (stream->refs == NULL) {
 50         stream->error = JDWP_ERROR(OUT_OF_MEMORY);
 51     }
 52 }
 53 
 54 jint
 55 inStream_id(PacketInputStream *stream)
 56 {
 57     return stream->packet.type.cmd.id;
 58 }
 59 
 60 jbyte
 61 inStream_command(PacketInputStream *stream)
 62 {
 63     return stream->packet.type.cmd.cmd;
 64 }
 65 
 66 static jdwpError
 67 readBytes(PacketInputStream *stream, void *dest, int size)
 68 {
 69     if (stream->error) {
 70         return stream->error;
 71     }
 72 
 73     if (size > stream->left) {
 74         stream->error = JDWP_ERROR(INTERNAL);
 75         return stream->error;
 76     }
 77 
 78     if (dest) {
 79         (void)memcpy(dest, stream->current, size);
 80     }
 81     stream->current += size;
 82     stream->left -= size;
 83 
 84     return stream->error;
 85 }
 86 
 87 jdwpError
 88 inStream_skipBytes(PacketInputStream *stream, jint size) {
 89     return readBytes(stream, NULL, size);
 90 }
 91 
 92 jboolean
 93 inStream_readBoolean(PacketInputStream *stream)
 94 {
 95     jbyte flag = 0;
 96     (void)readBytes(stream, &flag, sizeof(flag));
 97     if (stream->error) {
 98         return 0;
 99     } else {
100         return flag ? JNI_TRUE : JNI_FALSE;
101     }
102 }
103 
104 jbyte
105 inStream_readByte(PacketInputStream *stream)
106 {
107     jbyte val = 0;
108     (void)readBytes(stream, &val, sizeof(val));
109     return val;
110 }
111 
112 jbyte *
113 inStream_readBytes(PacketInputStream *stream, int length, jbyte *buf)
114 {
115     (void)readBytes(stream, buf, length);
116     return buf;
117 }
118 
119 jchar
120 inStream_readChar(PacketInputStream *stream)
121 {
122     jchar val = 0;
123     (void)readBytes(stream, &val, sizeof(val));
124     return JAVA_TO_HOST_CHAR(val);
125 }
126 
127 jshort
128 inStream_readShort(PacketInputStream *stream)
129 {
130     jshort val = 0;
131     (void)readBytes(stream, &val, sizeof(val));
132     return JAVA_TO_HOST_SHORT(val);
133 }
134 
135 jint
136 inStream_readInt(PacketInputStream *stream)
137 {
138     jint val = 0;
139     (void)readBytes(stream, &val, sizeof(val));
140     return JAVA_TO_HOST_INT(val);
141 }
142 
143 jlong
144 inStream_readLong(PacketInputStream *stream)
145 {
146     jlong val = 0;
147     (void)readBytes(stream, &val, sizeof(val));
148     return JAVA_TO_HOST_LONG(val);
149 }
150 
151 jfloat
152 inStream_readFloat(PacketInputStream *stream)
153 {
154     jfloat val = 0;
155     (void)readBytes(stream, &val, sizeof(val));
156     return JAVA_TO_HOST_FLOAT(val);
157 }
158 
159 jdouble
160 inStream_readDouble(PacketInputStream *stream)
161 {
162     jdouble val = 0;
163     (void)readBytes(stream, &val, sizeof(val));
164     return JAVA_TO_HOST_DOUBLE(val);
165 }
166 
167 /*
168  * Read a module from the stream. The ID used in the wire protocol
169  * is converted to a reference which is returned. The reference is
170  * global and strong, but it should *not* be deleted by the caller
171  * since it is freed when this stream is destroyed.
172  */
173 jobject
174 inStream_readModuleRef(JNIEnv *env, PacketInputStream *stream)
175 {
176     jobject ref = inStream_readObjectRef(env, stream);
177     if (ref == NULL && stream->error == JDWP_ERROR(INVALID_OBJECT)) {
178         stream->error = JDWP_ERROR(INVALID_MODULE);
179         return NULL;
180     }
181 
182     return ref;
183 }
184 
185 /*
186  * Read an object from the stream. The ID used in the wire protocol
187  * is converted to a reference which is returned. The reference is
188  * global and strong, but it should *not* be deleted by the caller
189  * since it is freed when this stream is destroyed.
190  */
191 jobject
192 inStream_readObjectRef(JNIEnv *env, PacketInputStream *stream)
193 {
194     jobject ref;
195     jobject *refPtr;
196     jlong id = inStream_readLong(stream);
197     if (stream->error) {
198         return NULL;
199     }
200     if (id == NULL_OBJECT_ID) {
201         return NULL;
202     }
203 
204     ref = commonRef_idToRef(env, id);
205     if (ref == NULL) {
206         stream->error = JDWP_ERROR(INVALID_OBJECT);
207         return NULL;
208     }
209 
210     refPtr = bagAdd(stream->refs);
211     if (refPtr == NULL) {
212         commonRef_idToRef_delete(env, ref);
213         return NULL;
214     }
215 
216     *refPtr = ref;
217     return ref;
218 }
219 
220 /*
221  * Read a raw object id from the stream. This should be used rarely.
222  * Normally, inStream_readObjectRef is preferred since it takes care
223  * of reference conversion and tracking. Only code that needs to
224  * perform maintence of the commonRef hash table uses this function.
225  */
226 jlong
227 inStream_readObjectID(PacketInputStream *stream)
228 {
229     return inStream_readLong(stream);
230 }
231 
232 jclass
233 inStream_readClassRef(JNIEnv *env, PacketInputStream *stream)
234 {
235     jobject object = inStream_readObjectRef(env, stream);
236     if (object == NULL) {
237         /*
238          * Could be error or just the null reference. In either case,
239          * stop now.
240          */
241         return NULL;
242     }
243     if (!isClass(object)) {
244         stream->error = JDWP_ERROR(INVALID_CLASS);
245         return NULL;
246     }
247     return object;
248 }
249 
250 jthread
251 inStream_readThreadRef(JNIEnv *env, PacketInputStream *stream)
252 {
253     jobject object = inStream_readObjectRef(env, stream);
254     if (object == NULL) {
255         /*
256          * Could be error or just the null reference. In either case,
257          * stop now.
258          */
259         return NULL;
260     }
261     if (!isThread(object)) {
262         stream->error = JDWP_ERROR(INVALID_THREAD);
263         return NULL;
264     }
265     return object;
266 }
267 
268 jthreadGroup
269 inStream_readThreadGroupRef(JNIEnv *env, PacketInputStream *stream)
270 {
271     jobject object = inStream_readObjectRef(env, stream);
272     if (object == NULL) {
273         /*
274          * Could be error or just the null reference. In either case,
275          * stop now.
276          */
277         return NULL;
278     }
279     if (!isThreadGroup(object)) {
280         stream->error = JDWP_ERROR(INVALID_THREAD_GROUP);
281         return NULL;
282     }
283     return object;
284 }
285 
286 jstring
287 inStream_readStringRef(JNIEnv *env, PacketInputStream *stream)
288 {
289     jobject object = inStream_readObjectRef(env, stream);
290     if (object == NULL) {
291         /*
292          * Could be error or just the null reference. In either case,
293          * stop now.
294          */
295         return NULL;
296     }
297     if (!isString(object)) {
298         stream->error = JDWP_ERROR(INVALID_STRING);
299         return NULL;
300     }
301     return object;
302 }
303 
304 jclass
305 inStream_readClassLoaderRef(JNIEnv *env, PacketInputStream *stream)
306 {
307     jobject object = inStream_readObjectRef(env, stream);
308     if (object == NULL) {
309         /*
310          * Could be error or just the null reference. In either case,
311          * stop now.
312          */
313         return NULL;
314     }
315     if (!isClassLoader(object)) {
316         stream->error = JDWP_ERROR(INVALID_CLASS_LOADER);
317         return NULL;
318     }
319     return object;
320 }
321 
322 jarray
323 inStream_readArrayRef(JNIEnv *env, PacketInputStream *stream)
324 {
325     jobject object = inStream_readObjectRef(env, stream);
326     if (object == NULL) {
327         /*
328          * Could be error or just the null reference. In either case,
329          * stop now.
330          */
331         return NULL;
332     }
333     if (!isArray(object)) {
334         stream->error = JDWP_ERROR(INVALID_ARRAY);
335         return NULL;
336     }
337     return object;
338 }
339 
340 /*
341  * Next 3 functions read an Int and convert to a Pointer!?
342  * If sizeof(jxxxID) == 8 we must read these values as Longs.
343  */
344 FrameID
345 inStream_readFrameID(PacketInputStream *stream)
346 {
347     if (sizeof(FrameID) == 8) {
348         /*LINTED*/
349         return (FrameID)inStream_readLong(stream);
350     } else {
351         /*LINTED*/
352         return (FrameID)inStream_readInt(stream);
353     }
354 }
355 
356 jmethodID
357 inStream_readMethodID(PacketInputStream *stream)
358 {
359     if (sizeof(jmethodID) == 8) {
360         /*LINTED*/
361         return (jmethodID)(intptr_t)inStream_readLong(stream);
362     } else {
363         /*LINTED*/
364         return (jmethodID)(intptr_t)inStream_readInt(stream);
365     }
366 }
367 
368 jfieldID
369 inStream_readFieldID(PacketInputStream *stream)
370 {
371     if (sizeof(jfieldID) == 8) {
372         /*LINTED*/
373         return (jfieldID)(intptr_t)inStream_readLong(stream);
374     } else {
375         /*LINTED*/
376         return (jfieldID)(intptr_t)inStream_readInt(stream);
377     }
378 }
379 
380 jlocation
381 inStream_readLocation(PacketInputStream *stream)
382 {
383     return (jlocation)inStream_readLong(stream);
384 }
385 
386 char *
387 inStream_readString(PacketInputStream *stream)
388 {
389     int length;
390     char *string;
391 
392     length = inStream_readInt(stream);
393     string = jvmtiAllocate(length + 1);
394     if (string != NULL) {
395         int new_length;
396 
397         (void)readBytes(stream, string, length);
398         string[length] = '\0';
399 
400         /* This is Standard UTF-8, convert to Modified UTF-8 if necessary */
401         new_length = utf8sToUtf8mLength((jbyte*)string, length);
402         if ( new_length != length ) {
403             char *new_string;
404 
405             new_string = jvmtiAllocate(new_length+1);
406             utf8sToUtf8m((jbyte*)string, length, (jbyte*)new_string, new_length);
407             jvmtiDeallocate(string);
408             return new_string;
409         }
410     }
411     return string;
412 }
413 
414 jdwpError
415 inStream_error(PacketInputStream *stream)
416 {
417     return stream->error;
418 }
419 
420 void
421 inStream_clearError(PacketInputStream *stream)
422 {
423     stream->error = JDWP_ERROR(NONE);
424 }
425 
426 jvalue
427 inStream_readValue(PacketInputStream *stream)
428 {
429     jvalue value;
430     jbyte typeKey = inStream_readByte(stream);
431     if (stream->error) {
432         value.j = 0L;
433         return value;
434     }
435 
436     if (isObjectTag(typeKey)) {
437         value.l = inStream_readObjectRef(getEnv(), stream);
438     } else {
439         switch (typeKey) {
440             case JDWP_TAG(BYTE):
441                 value.b = inStream_readByte(stream);
442                 break;
443 
444             case JDWP_TAG(CHAR):
445                 value.c = inStream_readChar(stream);
446                 break;
447 
448             case JDWP_TAG(FLOAT):
449                 value.f = inStream_readFloat(stream);
450                 break;
451 
452             case JDWP_TAG(DOUBLE):
453                 value.d = inStream_readDouble(stream);
454                 break;
455 
456             case JDWP_TAG(INT):
457                 value.i = inStream_readInt(stream);
458                 break;
459 
460             case JDWP_TAG(LONG):
461                 value.j = inStream_readLong(stream);
462                 break;
463 
464             case JDWP_TAG(SHORT):
465                 value.s = inStream_readShort(stream);
466                 break;
467 
468             case JDWP_TAG(BOOLEAN):
469                 value.z = inStream_readBoolean(stream);
470                 break;
471             default:
472                 stream->error = JDWP_ERROR(INVALID_TAG);
473                 break;
474         }
475     }
476     return value;
477 }
478 
479 static jboolean
480 deleteRef(void *elementPtr, void *arg)
481 {
482     JNIEnv *env = arg;
483     jobject *refPtr = elementPtr;
484     commonRef_idToRef_delete(env, *refPtr);
485     return JNI_TRUE;
486 }
487 
488 void
489 inStream_destroy(PacketInputStream *stream)
490 {
491     if (stream->packet.type.cmd.data != NULL) {
492     jvmtiDeallocate(stream->packet.type.cmd.data);
493     }
494 
495     (void)bagEnumerateOver(stream->refs, deleteRef, (void *)getEnv());
496     bagDestroyBag(stream->refs);
497 }