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 "transport.h"
28 #include "debugLoop.h"
29 #include "debugDispatch.h"
30 #include "standardHandlers.h"
31 #include "inStream.h"
32 #include "outStream.h"
33 #include "threadControl.h"
34 
35 
36 static void JNICALL reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg);
37 static void enqueue(jdwpPacket *p);
38 static jboolean dequeue(jdwpPacket *p);
39 static void notifyTransportError(void);
40 
41 struct PacketList {
42     jdwpPacket packet;
43     struct PacketList *next;
44 };
45 
46 static volatile struct PacketList *cmdQueue;
47 static jrawMonitorID cmdQueueLock;
48 static jrawMonitorID vmDeathLock;
49 static jboolean transportError;
50 
51 static jboolean
lastCommand(jdwpCmdPacket * cmd)52 lastCommand(jdwpCmdPacket *cmd)
53 {
54     if ((cmd->cmdSet == JDWP_COMMAND_SET(VirtualMachine)) &&
55         ((cmd->cmd == JDWP_COMMAND(VirtualMachine, Dispose)) ||
56          (cmd->cmd == JDWP_COMMAND(VirtualMachine, Exit)))) {
57         return JNI_TRUE;
58     } else {
59         return JNI_FALSE;
60     }
61 }
62 
63 void
debugLoop_initialize(void)64 debugLoop_initialize(void)
65 {
66     vmDeathLock = debugMonitorCreate("JDWP VM_DEATH Lock");
67 }
68 
69 void
debugLoop_sync(void)70 debugLoop_sync(void)
71 {
72     debugMonitorEnter(vmDeathLock);
73     debugMonitorExit(vmDeathLock);
74 }
75 
76 /*
77  * This is where all the work gets done.
78  */
79 
80 void
debugLoop_run(void)81 debugLoop_run(void)
82 {
83     jboolean shouldListen;
84     jdwpPacket p;
85     jvmtiStartFunction func;
86 
87     /* Initialize all statics */
88     /* We may be starting a new connection after an error */
89     cmdQueue = NULL;
90     cmdQueueLock = debugMonitorCreate("JDWP Command Queue Lock");
91     transportError = JNI_FALSE;
92 
93     shouldListen = JNI_TRUE;
94 
95     func = &reader;
96     (void)spawnNewThread(func, NULL, "JDWP Command Reader");
97 
98     standardHandlers_onConnect();
99     threadControl_onConnect();
100 
101     /* Okay, start reading cmds! */
102     while (shouldListen) {
103         if (!dequeue(&p)) {
104             break;
105         }
106 
107         if (p.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
108             /*
109              * Its a reply packet.
110              */
111            continue;
112         } else {
113             /*
114              * Its a cmd packet.
115              */
116             jdwpCmdPacket *cmd = &p.type.cmd;
117             PacketInputStream in;
118             PacketOutputStream out;
119             CommandHandler func;
120 
121             /* Should reply be sent to sender.
122              * For error handling, assume yes, since
123              * only VM/exit does not reply
124              */
125             jboolean replyToSender = JNI_TRUE;
126 
127             /*
128              * For all commands we hold the vmDeathLock
129              * while executing and replying to the command. This ensures
130              * that a command after VM_DEATH will be allowed to complete
131              * before the thread posting the VM_DEATH continues VM
132              * termination.
133              */
134             debugMonitorEnter(vmDeathLock);
135 
136             /* Initialize the input and output streams */
137             inStream_init(&in, p);
138             outStream_initReply(&out, inStream_id(&in));
139 
140             LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd));
141 
142             func = debugDispatch_getHandler(cmd->cmdSet,cmd->cmd);
143             if (func == NULL) {
144                 /* we've never heard of this, so I guess we
145                  * haven't implemented it.
146                  * Handle gracefully for future expansion
147                  * and platform / vendor expansion.
148                  */
149                 outStream_setError(&out, JDWP_ERROR(NOT_IMPLEMENTED));
150             } else if (gdata->vmDead &&
151              ((cmd->cmdSet) != JDWP_COMMAND_SET(VirtualMachine))) {
152                 /* Protect the VM from calls while dead.
153                  * VirtualMachine cmdSet quietly ignores some cmds
154                  * after VM death, so, it sends it's own errors.
155                  */
156                 outStream_setError(&out, JDWP_ERROR(VM_DEAD));
157             } else {
158                 /* Call the command handler */
159                 replyToSender = func(&in, &out);
160             }
161 
162             /* Reply to the sender */
163             if (replyToSender) {
164                 if (inStream_error(&in)) {
165                     outStream_setError(&out, inStream_error(&in));
166                 }
167                 outStream_sendReply(&out);
168             }
169 
170             /*
171              * Release the vmDeathLock as the reply has been posted.
172              */
173             debugMonitorExit(vmDeathLock);
174 
175             inStream_destroy(&in);
176             outStream_destroy(&out);
177 
178             shouldListen = !lastCommand(cmd);
179         }
180     }
181     threadControl_onDisconnect();
182     standardHandlers_onDisconnect();
183 
184     /*
185      * Cut off the transport immediately. This has the effect of
186      * cutting off any events that the eventHelper thread might
187      * be trying to send.
188      */
189     transport_close();
190     debugMonitorDestroy(cmdQueueLock);
191 
192     /* Reset for a new connection to this VM if it's still alive */
193     if ( ! gdata->vmDead ) {
194         debugInit_reset(getEnv());
195     }
196 }
197 
198 /* Command reader */
199 static void JNICALL
reader(jvmtiEnv * jvmti_env,JNIEnv * jni_env,void * arg)200 reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
201 {
202     jdwpPacket packet;
203     jdwpCmdPacket *cmd;
204     jboolean shouldListen = JNI_TRUE;
205 
206     LOG_MISC(("Begin reader thread"));
207 
208     while (shouldListen) {
209         jint rc;
210 
211         rc = transport_receivePacket(&packet);
212 
213         /* I/O error or EOF */
214         if (rc != 0 || (rc == 0 && packet.type.cmd.len == 0)) {
215             shouldListen = JNI_FALSE;
216             notifyTransportError();
217         } else if (packet.type.cmd.flags != JDWPTRANSPORT_FLAGS_NONE) {
218             /*
219              * Close the connection when we get a jdwpCmdPacket with an
220              * invalid flags field value. This is a protocol violation
221              * so we drop the connection. Also this could be a web
222              * browser generating an HTTP request that passes the JDWP
223              * handshake. HTTP requests requires that everything be in
224              * the ASCII printable range so a flags value of
225              * JDWPTRANSPORT_FLAGS_NONE(0) cannot be generated via HTTP.
226              */
227             ERROR_MESSAGE(("Received jdwpPacket with flags != 0x%d (actual=0x%x) when a jdwpCmdPacket was expected.",
228                            JDWPTRANSPORT_FLAGS_NONE, packet.type.cmd.flags));
229             shouldListen = JNI_FALSE;
230             notifyTransportError();
231         } else {
232             cmd = &packet.type.cmd;
233 
234             LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd));
235 
236             /*
237              * FIXME! We need to deal with high priority
238              * packets and queue flushes!
239              */
240             enqueue(&packet);
241 
242             shouldListen = !lastCommand(cmd);
243         }
244     }
245     LOG_MISC(("End reader thread"));
246 }
247 
248 /*
249  * The current system for queueing packets is highly
250  * inefficient, and should be rewritten! It'd be nice
251  * to avoid any additional memory allocations.
252  */
253 
254 static void
enqueue(jdwpPacket * packet)255 enqueue(jdwpPacket *packet)
256 {
257     struct PacketList *pL;
258     struct PacketList *walker;
259 
260     pL = jvmtiAllocate((jint)sizeof(struct PacketList));
261     if (pL == NULL) {
262         EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"packet list");
263     }
264 
265     pL->packet = *packet;
266     pL->next = NULL;
267 
268     debugMonitorEnter(cmdQueueLock);
269 
270     if (cmdQueue == NULL) {
271         cmdQueue = pL;
272         debugMonitorNotify(cmdQueueLock);
273     } else {
274         walker = (struct PacketList *)cmdQueue;
275         while (walker->next != NULL)
276             walker = walker->next;
277 
278         walker->next = pL;
279     }
280 
281     debugMonitorExit(cmdQueueLock);
282 }
283 
284 static jboolean
dequeue(jdwpPacket * packet)285 dequeue(jdwpPacket *packet) {
286     struct PacketList *node = NULL;
287 
288     debugMonitorEnter(cmdQueueLock);
289 
290     while (!transportError && (cmdQueue == NULL)) {
291         debugMonitorWait(cmdQueueLock);
292     }
293 
294     if (cmdQueue != NULL) {
295         node = (struct PacketList *)cmdQueue;
296         cmdQueue = node->next;
297     }
298     debugMonitorExit(cmdQueueLock);
299 
300     if (node != NULL) {
301         *packet = node->packet;
302         jvmtiDeallocate(node);
303     }
304     return (node != NULL);
305 }
306 
307 static void
notifyTransportError(void)308 notifyTransportError(void) {
309     debugMonitorEnter(cmdQueueLock);
310     transportError = JNI_TRUE;
311     debugMonitorNotify(cmdQueueLock);
312     debugMonitorExit(cmdQueueLock);
313 }
314