1 /*
2  * Copyright (c) 1998, 2013, 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 "stream.h"
28 #include "outStream.h"
29 #include "inStream.h"
30 #include "transport.h"
31 #include "commonRef.h"
32 #include "bag.h"
33 #include "FrameID.h"
34 
35 #define INITIAL_ID_ALLOC  50
36 #define SMALLEST(a, b) ((a) < (b)) ? (a) : (b)
37 
38 static void
commonInit(PacketOutputStream * stream)39 commonInit(PacketOutputStream *stream)
40 {
41     stream->current = &stream->initialSegment[0];
42     stream->left = sizeof(stream->initialSegment);
43     stream->segment = &stream->firstSegment;
44     stream->segment->length = 0;
45     stream->segment->data = &stream->initialSegment[0];
46     stream->segment->next = NULL;
47     stream->error = JDWP_ERROR(NONE);
48     stream->sent = JNI_FALSE;
49     stream->ids = bagCreateBag(sizeof(jlong), INITIAL_ID_ALLOC);
50     if (stream->ids == NULL) {
51         stream->error = JDWP_ERROR(OUT_OF_MEMORY);
52     }
53 }
54 
55 void
outStream_initCommand(PacketOutputStream * stream,jint id,jbyte flags,jbyte commandSet,jbyte command)56 outStream_initCommand(PacketOutputStream *stream, jint id,
57                       jbyte flags, jbyte commandSet, jbyte command)
58 {
59     commonInit(stream);
60 
61     /*
62      * Command-specific initialization
63      */
64     stream->packet.type.cmd.id = id;
65     stream->packet.type.cmd.cmdSet = commandSet;
66     stream->packet.type.cmd.cmd = command;
67 
68     stream->packet.type.cmd.flags = flags;
69 }
70 
71 void
outStream_initReply(PacketOutputStream * stream,jint id)72 outStream_initReply(PacketOutputStream *stream, jint id)
73 {
74     commonInit(stream);
75 
76     /*
77      * Reply-specific initialization
78      */
79     stream->packet.type.reply.id = id;
80     stream->packet.type.reply.errorCode = 0x0;
81     stream->packet.type.cmd.flags = (jbyte)JDWPTRANSPORT_FLAGS_REPLY;
82 }
83 
84 jint
outStream_id(PacketOutputStream * stream)85 outStream_id(PacketOutputStream *stream)
86 {
87     return stream->packet.type.cmd.id;
88 }
89 
90 jbyte
outStream_command(PacketOutputStream * stream)91 outStream_command(PacketOutputStream *stream)
92 {
93     /* Only makes sense for commands */
94     JDI_ASSERT(!(stream->packet.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY));
95     return stream->packet.type.cmd.cmd;
96 }
97 
98 static jdwpError
writeBytes(PacketOutputStream * stream,void * source,int size)99 writeBytes(PacketOutputStream *stream, void *source, int size)
100 {
101     jbyte *bytes = (jbyte *)source;
102 
103     if (stream->error) {
104         return stream->error;
105     }
106     while (size > 0) {
107         jint count;
108         if (stream->left == 0) {
109             jint segSize = SMALLEST(2 * stream->segment->length, MAX_SEGMENT_SIZE);
110             jbyte *newSeg = jvmtiAllocate(segSize);
111             struct PacketData *newHeader = jvmtiAllocate(sizeof(*newHeader));
112             if ((newSeg == NULL) || (newHeader == NULL)) {
113                 jvmtiDeallocate(newSeg);
114                 jvmtiDeallocate(newHeader);
115                 stream->error = JDWP_ERROR(OUT_OF_MEMORY);
116                 return stream->error;
117             }
118             newHeader->length = 0;
119             newHeader->data = newSeg;
120             newHeader->next = NULL;
121             stream->segment->next = newHeader;
122             stream->segment = newHeader;
123             stream->current = newHeader->data;
124             stream->left = segSize;
125         }
126         count = SMALLEST(size, stream->left);
127         (void)memcpy(stream->current, bytes, count);
128         stream->current += count;
129         stream->left -= count;
130         stream->segment->length += count;
131         size -= count;
132         bytes += count;
133     }
134     return JDWP_ERROR(NONE);
135 }
136 
137 jdwpError
outStream_writeBoolean(PacketOutputStream * stream,jboolean val)138 outStream_writeBoolean(PacketOutputStream *stream, jboolean val)
139 {
140     jbyte byte = (val != 0) ? 1 : 0;
141     return writeBytes(stream, &byte, sizeof(byte));
142 }
143 
144 jdwpError
outStream_writeByte(PacketOutputStream * stream,jbyte val)145 outStream_writeByte(PacketOutputStream *stream, jbyte val)
146 {
147     return writeBytes(stream, &val, sizeof(val));
148 }
149 
150 jdwpError
outStream_writeChar(PacketOutputStream * stream,jchar val)151 outStream_writeChar(PacketOutputStream *stream, jchar val)
152 {
153     val = HOST_TO_JAVA_CHAR(val);
154     return writeBytes(stream, &val, sizeof(val));
155 }
156 
157 jdwpError
outStream_writeShort(PacketOutputStream * stream,jshort val)158 outStream_writeShort(PacketOutputStream *stream, jshort val)
159 {
160     val = HOST_TO_JAVA_SHORT(val);
161     return writeBytes(stream, &val, sizeof(val));
162 }
163 
164 jdwpError
outStream_writeInt(PacketOutputStream * stream,jint val)165 outStream_writeInt(PacketOutputStream *stream, jint val)
166 {
167     val = HOST_TO_JAVA_INT(val);
168     return writeBytes(stream, &val, sizeof(val));
169 }
170 
171 jdwpError
outStream_writeLong(PacketOutputStream * stream,jlong val)172 outStream_writeLong(PacketOutputStream *stream, jlong val)
173 {
174     val = HOST_TO_JAVA_LONG(val);
175     return writeBytes(stream, &val, sizeof(val));
176 }
177 
178 jdwpError
outStream_writeFloat(PacketOutputStream * stream,jfloat val)179 outStream_writeFloat(PacketOutputStream *stream, jfloat val)
180 {
181     val = HOST_TO_JAVA_FLOAT(val);
182     return writeBytes(stream, &val, sizeof(val));
183 }
184 
185 jdwpError
outStream_writeDouble(PacketOutputStream * stream,jdouble val)186 outStream_writeDouble(PacketOutputStream *stream, jdouble val)
187 {
188     val = HOST_TO_JAVA_DOUBLE(val);
189     return writeBytes(stream, &val, sizeof(val));
190 }
191 
192 jdwpError
outStream_writeObjectTag(JNIEnv * env,PacketOutputStream * stream,jobject val)193 outStream_writeObjectTag(JNIEnv *env, PacketOutputStream *stream, jobject val)
194 {
195     return outStream_writeByte(stream, specificTypeKey(env, val));
196 }
197 
198 jdwpError
outStream_writeObjectRef(JNIEnv * env,PacketOutputStream * stream,jobject val)199 outStream_writeObjectRef(JNIEnv *env, PacketOutputStream *stream, jobject val)
200 {
201     jlong id;
202     jlong *idPtr;
203 
204     if (stream->error) {
205         return stream->error;
206     }
207 
208     if (val == NULL) {
209         id = NULL_OBJECT_ID;
210     } else {
211         /* Convert the object to an object id */
212         id = commonRef_refToID(env, val);
213         if (id == NULL_OBJECT_ID) {
214             stream->error = JDWP_ERROR(OUT_OF_MEMORY);
215             return stream->error;
216         }
217 
218         /* Track the common ref in case we need to release it on a future error */
219         idPtr = bagAdd(stream->ids);
220         if (idPtr == NULL) {
221             commonRef_release(env, id);
222             stream->error = JDWP_ERROR(OUT_OF_MEMORY);
223             return stream->error;
224         } else {
225             *idPtr = id;
226         }
227 
228         /* Add the encoded object id to the stream */
229         id = HOST_TO_JAVA_LONG(id);
230     }
231 
232     return writeBytes(stream, &id, sizeof(id));
233 }
234 
235 jdwpError
outStream_writeFrameID(PacketOutputStream * stream,FrameID val)236 outStream_writeFrameID(PacketOutputStream *stream, FrameID val)
237 {
238     /*
239      * Not good - we're writing a pointer as a jint.  Need
240      * to write as a jlong if sizeof(FrameID) == 8.
241      */
242     if (sizeof(FrameID) == 8) {
243         /*LINTED*/
244         return outStream_writeLong(stream, (jlong)val);
245     } else {
246         /*LINTED*/
247         return outStream_writeInt(stream, (jint)val);
248     }
249 }
250 
251 jdwpError
outStream_writeMethodID(PacketOutputStream * stream,jmethodID val)252 outStream_writeMethodID(PacketOutputStream *stream, jmethodID val)
253 {
254     /*
255      * Not good - we're writing a pointer as a jint.  Need
256      * to write as a jlong if sizeof(jmethodID) == 8.
257      */
258     if (sizeof(jmethodID) == 8) {
259         /*LINTED*/
260         return outStream_writeLong(stream, (jlong)(intptr_t)val);
261     } else {
262         /*LINTED*/
263         return outStream_writeInt(stream, (jint)(intptr_t)val);
264     }
265 }
266 
267 jdwpError
outStream_writeFieldID(PacketOutputStream * stream,jfieldID val)268 outStream_writeFieldID(PacketOutputStream *stream, jfieldID val)
269 {
270     /*
271      * Not good - we're writing a pointer as a jint.  Need
272      * to write as a jlong if sizeof(jfieldID) == 8.
273      */
274     if (sizeof(jfieldID) == 8) {
275         /*LINTED*/
276         return outStream_writeLong(stream, (jlong)(intptr_t)val);
277     } else {
278         /*LINTED*/
279         return outStream_writeInt(stream, (jint)(intptr_t)val);
280     }
281 }
282 
283 jdwpError
outStream_writeLocation(PacketOutputStream * stream,jlocation val)284 outStream_writeLocation(PacketOutputStream *stream, jlocation val)
285 {
286     return outStream_writeLong(stream, (jlong)val);
287 }
288 
289 jdwpError
outStream_writeByteArray(PacketOutputStream * stream,jint length,jbyte * bytes)290 outStream_writeByteArray(PacketOutputStream*stream, jint length,
291                          jbyte *bytes)
292 {
293     (void)outStream_writeInt(stream, length);
294     return writeBytes(stream, bytes, length);
295 }
296 
297 jdwpError
outStream_writeString(PacketOutputStream * stream,char * string)298 outStream_writeString(PacketOutputStream *stream, char *string)
299 {
300     jdwpError error;
301     jint      length = string != NULL ? (int)strlen(string) : 0;
302 
303     /* Options utf8=y/n controls if we want Standard UTF-8 or Modified */
304     if ( gdata->modifiedUtf8 ) {
305         (void)outStream_writeInt(stream, length);
306         error = writeBytes(stream, (jbyte *)string, length);
307     } else {
308         jint      new_length;
309 
310         new_length = (gdata->npt->utf8mToUtf8sLength)
311                             (gdata->npt->utf, (jbyte*)string, length);
312         if ( new_length == length ) {
313             (void)outStream_writeInt(stream, length);
314             error = writeBytes(stream, (jbyte *)string, length);
315         } else {
316             char *new_string;
317 
318             new_string = jvmtiAllocate(new_length+1);
319             (gdata->npt->utf8mToUtf8s)
320                             (gdata->npt->utf, (jbyte*)string, length,
321                              (jbyte*)new_string, new_length);
322             (void)outStream_writeInt(stream, new_length);
323             error = writeBytes(stream, (jbyte *)new_string, new_length);
324             jvmtiDeallocate(new_string);
325         }
326     }
327     return error;
328 }
329 
330 jdwpError
outStream_writeValue(JNIEnv * env,PacketOutputStream * out,jbyte typeKey,jvalue value)331 outStream_writeValue(JNIEnv *env, PacketOutputStream *out,
332                      jbyte typeKey, jvalue value)
333 {
334     if (typeKey == JDWP_TAG(OBJECT)) {
335         (void)outStream_writeByte(out, specificTypeKey(env, value.l));
336     } else {
337         (void)outStream_writeByte(out, typeKey);
338     }
339     if (isObjectTag(typeKey)) {
340         (void)outStream_writeObjectRef(env, out, value.l);
341     } else {
342         switch (typeKey) {
343             case JDWP_TAG(BYTE):
344                 return outStream_writeByte(out, value.b);
345 
346             case JDWP_TAG(CHAR):
347                 return outStream_writeChar(out, value.c);
348 
349             case JDWP_TAG(FLOAT):
350                 return outStream_writeFloat(out, value.f);
351 
352             case JDWP_TAG(DOUBLE):
353                 return outStream_writeDouble(out, value.d);
354 
355             case JDWP_TAG(INT):
356                 return outStream_writeInt(out, value.i);
357 
358             case JDWP_TAG(LONG):
359                 return outStream_writeLong(out, value.j);
360 
361             case JDWP_TAG(SHORT):
362                 return outStream_writeShort(out, value.s);
363 
364             case JDWP_TAG(BOOLEAN):
365                 return outStream_writeBoolean(out, value.z);
366 
367             case JDWP_TAG(VOID):  /* happens with function return values */
368                 /* write nothing */
369                 return JDWP_ERROR(NONE);
370 
371             default:
372                 EXIT_ERROR(AGENT_ERROR_INVALID_OBJECT,"Invalid type key");
373                 break;
374         }
375     }
376     return JDWP_ERROR(NONE);
377 }
378 
379 jdwpError
outStream_skipBytes(PacketOutputStream * stream,jint count)380 outStream_skipBytes(PacketOutputStream *stream, jint count)
381 {
382     int i;
383     for (i = 0; i < count; i++) {
384         (void)outStream_writeByte(stream, 0);
385     }
386     return stream->error;
387 }
388 
389 jdwpError
outStream_error(PacketOutputStream * stream)390 outStream_error(PacketOutputStream *stream)
391 {
392     return stream->error;
393 }
394 
395 void
outStream_setError(PacketOutputStream * stream,jdwpError error)396 outStream_setError(PacketOutputStream *stream, jdwpError error)
397 {
398     if (stream->error == JDWP_ERROR(NONE)) {
399         stream->error = error;
400         LOG_MISC(("outStream_setError error=%s(%d)", jdwpErrorText(error), error));
401     }
402 }
403 
404 static jint
outStream_send(PacketOutputStream * stream)405 outStream_send(PacketOutputStream *stream) {
406 
407     jint rc;
408     jint len = 0;
409     PacketData *segment;
410     jbyte *data, *posP;
411 
412     /*
413      * If there's only 1 segment then we just send the
414      * packet.
415      */
416     if (stream->firstSegment.next == NULL) {
417         stream->packet.type.cmd.len = 11 + stream->firstSegment.length;
418         stream->packet.type.cmd.data = stream->firstSegment.data;
419         rc = transport_sendPacket(&stream->packet);
420         return rc;
421     }
422 
423     /*
424      * Multiple segments
425      */
426     len = 0;
427     segment = (PacketData *)&(stream->firstSegment);
428     do {
429         len += segment->length;
430         segment = segment->next;
431     } while (segment != NULL);
432 
433     data = jvmtiAllocate(len);
434     if (data == NULL) {
435         return JDWP_ERROR(OUT_OF_MEMORY);
436     }
437 
438     posP = data;
439     segment = (PacketData *)&(stream->firstSegment);
440     while (segment != NULL) {
441         (void)memcpy(posP, segment->data, segment->length);
442         posP += segment->length;
443         segment = segment->next;
444     }
445 
446     stream->packet.type.cmd.len = 11 + len;
447     stream->packet.type.cmd.data = data;
448     rc = transport_sendPacket(&stream->packet);
449     stream->packet.type.cmd.data = NULL;
450     jvmtiDeallocate(data);
451 
452     return rc;
453 }
454 
455 void
outStream_sendReply(PacketOutputStream * stream)456 outStream_sendReply(PacketOutputStream *stream)
457 {
458     jint rc;
459     if (stream->error) {
460         /*
461          * Don't send any collected stream data on an error reply
462          */
463         stream->packet.type.reply.len = 0;
464         stream->packet.type.reply.errorCode = (jshort)stream->error;
465     }
466     rc = outStream_send(stream);
467     if (rc == 0) {
468         stream->sent = JNI_TRUE;
469     }
470 }
471 
472 void
outStream_sendCommand(PacketOutputStream * stream)473 outStream_sendCommand(PacketOutputStream *stream)
474 {
475     jint rc;
476     if (!stream->error) {
477         rc = outStream_send(stream);
478         if (rc == 0) {
479             stream->sent = JNI_TRUE;
480         }
481     }
482 }
483 
484 
485 static jboolean
releaseID(void * elementPtr,void * arg)486 releaseID(void *elementPtr, void *arg)
487 {
488     jlong *idPtr = elementPtr;
489     commonRef_release(getEnv(), *idPtr);
490     return JNI_TRUE;
491 }
492 
493 void
outStream_destroy(PacketOutputStream * stream)494 outStream_destroy(PacketOutputStream *stream)
495 {
496     struct PacketData *next;
497 
498     if (stream->error || !stream->sent) {
499         (void)bagEnumerateOver(stream->ids, releaseID, NULL);
500     }
501 
502     next = stream->firstSegment.next;
503     while (next != NULL) {
504         struct PacketData *p = next;
505         next = p->next;
506         jvmtiDeallocate(p->data);
507         jvmtiDeallocate(p);
508     }
509     bagDestroyBag(stream->ids);
510 }
511