• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/debug/wasm/gdb-server/transport.h"
6 
7 #include <fcntl.h>
8 
9 #include "src/base/platform/wrappers.h"
10 
11 #ifndef SD_BOTH
12 #define SD_BOTH 2
13 #endif
14 
15 namespace v8 {
16 namespace internal {
17 namespace wasm {
18 namespace gdb_server {
19 
SocketBinding(SocketHandle socket_handle)20 SocketBinding::SocketBinding(SocketHandle socket_handle)
21     : socket_handle_(socket_handle) {}
22 
23 // static
Bind(uint16_t tcp_port)24 SocketBinding SocketBinding::Bind(uint16_t tcp_port) {
25   SocketHandle socket_handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
26   if (socket_handle == InvalidSocket) {
27     TRACE_GDB_REMOTE("Failed to create socket.\n");
28     return SocketBinding(InvalidSocket);
29   }
30   struct sockaddr_in sockaddr;
31   // Clearing sockaddr_in first appears to be necessary on Mac OS X.
32   memset(&sockaddr, 0, sizeof(sockaddr));
33   socklen_t addrlen = static_cast<socklen_t>(sizeof(sockaddr));
34   sockaddr.sin_family = AF_INET;
35   sockaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
36   sockaddr.sin_port = htons(tcp_port);
37 
38 #if _WIN32
39   // On Windows, SO_REUSEADDR has a different meaning than on POSIX systems.
40   // SO_REUSEADDR allows hijacking of an open socket by another process.
41   // The SO_EXCLUSIVEADDRUSE flag prevents this behavior.
42   // See:
43   // http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx
44   //
45   // Additionally, unlike POSIX, TCP server sockets can be bound to
46   // ports in the TIME_WAIT state, without setting SO_REUSEADDR.
47   int exclusive_address = 1;
48   if (setsockopt(socket_handle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
49                  reinterpret_cast<char*>(&exclusive_address),
50                  sizeof(exclusive_address))) {
51     TRACE_GDB_REMOTE("Failed to set SO_EXCLUSIVEADDRUSE option.\n");
52   }
53 #else
54   // On POSIX, this is necessary to ensure that the TCP port is released
55   // promptly when sel_ldr exits.  Without this, the TCP port might
56   // only be released after a timeout, and later processes can fail
57   // to bind it.
58   int reuse_address = 1;
59   if (setsockopt(socket_handle, SOL_SOCKET, SO_REUSEADDR,
60                  reinterpret_cast<char*>(&reuse_address),
61                  sizeof(reuse_address))) {
62     TRACE_GDB_REMOTE("Failed to set SO_REUSEADDR option.\n");
63   }
64 #endif
65 
66   if (bind(socket_handle, reinterpret_cast<struct sockaddr*>(&sockaddr),
67            addrlen)) {
68     TRACE_GDB_REMOTE("Failed to bind server.\n");
69     return SocketBinding(InvalidSocket);
70   }
71 
72   if (listen(socket_handle, 1)) {
73     TRACE_GDB_REMOTE("Failed to listen.\n");
74     return SocketBinding(InvalidSocket);
75   }
76   return SocketBinding(socket_handle);
77 }
78 
CreateTransport()79 std::unique_ptr<SocketTransport> SocketBinding::CreateTransport() {
80   return std::make_unique<SocketTransport>(socket_handle_);
81 }
82 
GetBoundPort()83 uint16_t SocketBinding::GetBoundPort() {
84   struct sockaddr_in saddr;
85   struct sockaddr* psaddr = reinterpret_cast<struct sockaddr*>(&saddr);
86   // Clearing sockaddr_in first appears to be necessary on Mac OS X.
87   memset(&saddr, 0, sizeof(saddr));
88   socklen_t addrlen = static_cast<socklen_t>(sizeof(saddr));
89   if (::getsockname(socket_handle_, psaddr, &addrlen)) {
90     TRACE_GDB_REMOTE("Failed to retrieve bound address.\n");
91     return 0;
92   }
93   return ntohs(saddr.sin_port);
94 }
95 
96 // Do not delay sending small packets.  This significantly speeds up
97 // remote debugging.  Debug stub uses buffering to send outgoing packets
98 // so they are not split into more TCP packets than necessary.
DisableNagleAlgorithm(SocketHandle socket)99 void DisableNagleAlgorithm(SocketHandle socket) {
100   int nodelay = 1;
101   if (::setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
102                    reinterpret_cast<char*>(&nodelay), sizeof(nodelay))) {
103     TRACE_GDB_REMOTE("Failed to set TCP_NODELAY option.\n");
104   }
105 }
106 
Transport(SocketHandle s)107 Transport::Transport(SocketHandle s)
108     : buf_(new char[kBufSize]),
109       pos_(0),
110       size_(0),
111       handle_bind_(s),
112       handle_accept_(InvalidSocket) {}
113 
~Transport()114 Transport::~Transport() {
115   if (handle_accept_ != InvalidSocket) {
116     CloseSocket(handle_accept_);
117   }
118 }
119 
CopyFromBuffer(char ** dst,int32_t * len)120 void Transport::CopyFromBuffer(char** dst, int32_t* len) {
121   int32_t copy_bytes = std::min(*len, size_ - pos_);
122   memcpy(*dst, buf_.get() + pos_, copy_bytes);
123   pos_ += copy_bytes;
124   *len -= copy_bytes;
125   *dst += copy_bytes;
126 }
127 
Read(char * dst,int32_t len)128 bool Transport::Read(char* dst, int32_t len) {
129   if (pos_ < size_) {
130     CopyFromBuffer(&dst, &len);
131   }
132   while (len > 0) {
133     pos_ = 0;
134     size_ = 0;
135     if (!ReadSomeData()) {
136       return false;
137     }
138     CopyFromBuffer(&dst, &len);
139   }
140   return true;
141 }
142 
Write(const char * src,int32_t len)143 bool Transport::Write(const char* src, int32_t len) {
144   while (len > 0) {
145     ssize_t result = ::send(handle_accept_, src, len, 0);
146     if (result > 0) {
147       src += result;
148       len -= result;
149       continue;
150     }
151     if (result == 0) {
152       return false;
153     }
154     if (SocketGetLastError() != kErrInterrupt) {
155       return false;
156     }
157   }
158   return true;
159 }
160 
161 // Return true if there is data to read.
IsDataAvailable() const162 bool Transport::IsDataAvailable() const {
163   if (pos_ < size_) {
164     return true;
165   }
166   fd_set fds;
167 
168   FD_ZERO(&fds);
169   FD_SET(handle_accept_, &fds);
170 
171   // We want a "non-blocking" check
172   struct timeval timeout;
173   timeout.tv_sec = 0;
174   timeout.tv_usec = 0;
175 
176   // Check if this file handle can select on read
177   int cnt = select(static_cast<int>(handle_accept_) + 1, &fds, 0, 0, &timeout);
178 
179   // If we are ready, or if there is an error.  We return true
180   // on error, to let the next IO request fail.
181   if (cnt != 0) return true;
182 
183   return false;
184 }
185 
Close()186 void Transport::Close() {
187   ::shutdown(handle_bind_, SD_BOTH);
188   CloseSocket(handle_bind_);
189   Disconnect();
190 }
191 
Disconnect()192 void Transport::Disconnect() {
193   if (handle_accept_ != InvalidSocket) {
194     // Shutdown the connection in both directions.  This should
195     // always succeed, and nothing we can do if this fails.
196     ::shutdown(handle_accept_, SD_BOTH);
197     CloseSocket(handle_accept_);
198     handle_accept_ = InvalidSocket;
199   }
200 }
201 
202 #if _WIN32
203 
SocketTransport(SocketHandle s)204 SocketTransport::SocketTransport(SocketHandle s) : Transport(s) {
205   socket_event_ = WSA_INVALID_EVENT;
206   faulted_thread_event_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
207   if (faulted_thread_event_ == NULL) {
208     TRACE_GDB_REMOTE(
209         "SocketTransport::SocketTransport: Failed to create event object for "
210         "faulted thread\n");
211   }
212 }
213 
~SocketTransport()214 SocketTransport::~SocketTransport() {
215   if (!CloseHandle(faulted_thread_event_)) {
216     TRACE_GDB_REMOTE(
217         "SocketTransport::~SocketTransport: Failed to close "
218         "event\n");
219   }
220 
221   if (socket_event_) {
222     if (!::WSACloseEvent(socket_event_)) {
223       TRACE_GDB_REMOTE(
224           "SocketTransport::~SocketTransport: Failed to close "
225           "socket event\n");
226     }
227   }
228 }
229 
AcceptConnection()230 bool SocketTransport::AcceptConnection() {
231   CHECK(handle_accept_ == InvalidSocket);
232   handle_accept_ = ::accept(handle_bind_, NULL, 0);
233   if (handle_accept_ != InvalidSocket) {
234     DisableNagleAlgorithm(handle_accept_);
235 
236     // Create socket event
237     socket_event_ = ::WSACreateEvent();
238     if (socket_event_ == WSA_INVALID_EVENT) {
239       TRACE_GDB_REMOTE(
240           "SocketTransport::AcceptConnection: Failed to create socket event\n");
241     }
242 
243     // Listen for close events in order to handle them correctly.
244     // Additionally listen for read readiness as WSAEventSelect sets the socket
245     // to non-blocking mode.
246     // http://msdn.microsoft.com/en-us/library/windows/desktop/ms738547(v=vs.85).aspx
247     if (::WSAEventSelect(handle_accept_, socket_event_, FD_CLOSE | FD_READ) ==
248         SOCKET_ERROR) {
249       TRACE_GDB_REMOTE(
250           "SocketTransport::AcceptConnection: Failed to bind event to "
251           "socket\n");
252     }
253     return true;
254   }
255   return false;
256 }
257 
ReadSomeData()258 bool SocketTransport::ReadSomeData() {
259   while (true) {
260     ssize_t result =
261         ::recv(handle_accept_, buf_.get() + size_, kBufSize - size_, 0);
262     if (result > 0) {
263       size_ += result;
264       return true;
265     }
266     if (result == 0) {
267       return false;  // The connection was gracefully closed.
268     }
269     // WSAEventSelect sets socket to non-blocking mode. This is essential
270     // for socket event notification to work, there is no workaround.
271     // See remarks section at the page
272     // http://msdn.microsoft.com/en-us/library/windows/desktop/ms741576(v=vs.85).aspx
273     if (SocketGetLastError() == WSAEWOULDBLOCK) {
274       if (::WaitForSingleObject(socket_event_, INFINITE) == WAIT_FAILED) {
275         TRACE_GDB_REMOTE(
276             "SocketTransport::ReadSomeData: Failed to wait on socket event\n");
277       }
278       if (!::ResetEvent(socket_event_)) {
279         TRACE_GDB_REMOTE(
280             "SocketTransport::ReadSomeData: Failed to reset socket event\n");
281       }
282       continue;
283     }
284 
285     if (SocketGetLastError() != kErrInterrupt) {
286       return false;
287     }
288   }
289 }
290 
WaitForDebugStubEvent()291 void SocketTransport::WaitForDebugStubEvent() {
292   // Don't wait if we already have data to read.
293   bool wait = !(pos_ < size_);
294 
295   HANDLE handles[2];
296   handles[0] = faulted_thread_event_;
297   handles[1] = socket_event_;
298   int count = size_ < kBufSize ? 2 : 1;
299   int result =
300       WaitForMultipleObjects(count, handles, FALSE, wait ? INFINITE : 0);
301   if (result == WAIT_OBJECT_0 + 1) {
302     if (!ResetEvent(socket_event_)) {
303       TRACE_GDB_REMOTE(
304           "SocketTransport::WaitForDebugStubEvent: Failed to reset socket "
305           "event\n");
306     }
307     return;
308   } else if (result == WAIT_OBJECT_0) {
309     if (!ResetEvent(faulted_thread_event_)) {
310       TRACE_GDB_REMOTE(
311           "SocketTransport::WaitForDebugStubEvent: Failed to reset event\n");
312     }
313     return;
314   } else if (result == WAIT_TIMEOUT) {
315     return;
316   }
317   TRACE_GDB_REMOTE(
318       "SocketTransport::WaitForDebugStubEvent: Wait for events failed\n");
319 }
320 
SignalThreadEvent()321 bool SocketTransport::SignalThreadEvent() {
322   if (!SetEvent(faulted_thread_event_)) {
323     return false;
324   }
325   return true;
326 }
327 
Disconnect()328 void SocketTransport::Disconnect() {
329   Transport::Disconnect();
330 
331   if (socket_event_ != WSA_INVALID_EVENT && !::WSACloseEvent(socket_event_)) {
332     TRACE_GDB_REMOTE(
333         "SocketTransport::~SocketTransport: Failed to close "
334         "socket event\n");
335   }
336   socket_event_ = WSA_INVALID_EVENT;
337   SignalThreadEvent();
338 }
339 
340 #else  // _WIN32
341 
SocketTransport(SocketHandle s)342 SocketTransport::SocketTransport(SocketHandle s) : Transport(s) {
343   int fds[2];
344 #if defined(__linux__)
345   int ret = pipe2(fds, O_CLOEXEC);
346 #else
347   int ret = pipe(fds);
348 #endif
349   if (ret < 0) {
350     TRACE_GDB_REMOTE(
351         "SocketTransport::SocketTransport: Failed to allocate pipe for faulted "
352         "thread\n");
353   }
354   faulted_thread_fd_read_ = fds[0];
355   faulted_thread_fd_write_ = fds[1];
356 }
357 
~SocketTransport()358 SocketTransport::~SocketTransport() {
359   if (close(faulted_thread_fd_read_) != 0) {
360     TRACE_GDB_REMOTE(
361         "SocketTransport::~SocketTransport: Failed to close "
362         "event\n");
363   }
364   if (close(faulted_thread_fd_write_) != 0) {
365     TRACE_GDB_REMOTE(
366         "SocketTransport::~SocketTransport: Failed to close "
367         "event\n");
368   }
369 }
370 
AcceptConnection()371 bool SocketTransport::AcceptConnection() {
372   CHECK(handle_accept_ == InvalidSocket);
373   handle_accept_ = ::accept(handle_bind_, NULL, 0);
374   if (handle_accept_ != InvalidSocket) {
375     DisableNagleAlgorithm(handle_accept_);
376     return true;
377   }
378   return false;
379 }
380 
ReadSomeData()381 bool SocketTransport::ReadSomeData() {
382   while (true) {
383     ssize_t result =
384         ::recv(handle_accept_, buf_.get() + size_, kBufSize - size_, 0);
385     if (result > 0) {
386       size_ += result;
387       return true;
388     }
389     if (result == 0) {
390       return false;  // The connection was gracefully closed.
391     }
392     if (SocketGetLastError() != kErrInterrupt) {
393       return false;
394     }
395   }
396 }
397 
WaitForDebugStubEvent()398 void SocketTransport::WaitForDebugStubEvent() {
399   // Don't wait if we already have data to read.
400   bool wait = !(pos_ < size_);
401 
402   fd_set fds;
403   FD_ZERO(&fds);
404   FD_SET(faulted_thread_fd_read_, &fds);
405   int max_fd = faulted_thread_fd_read_;
406   if (size_ < kBufSize) {
407     FD_SET(handle_accept_, &fds);
408     max_fd = std::max(max_fd, handle_accept_);
409   }
410 
411   int ret;
412   // We don't need sleep-polling on Linux now, so we set either zero or infinite
413   // timeout.
414   if (wait) {
415     ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
416   } else {
417     struct timeval timeout;
418     timeout.tv_sec = 0;
419     timeout.tv_usec = 0;
420     ret = select(max_fd + 1, &fds, NULL, NULL, &timeout);
421   }
422   if (ret < 0) {
423     TRACE_GDB_REMOTE(
424         "SocketTransport::WaitForDebugStubEvent: Failed to wait for "
425         "debug stub event\n");
426   }
427 
428   if (ret > 0) {
429     if (FD_ISSET(faulted_thread_fd_read_, &fds)) {
430       char buf[16];
431       if (read(faulted_thread_fd_read_, &buf, sizeof(buf)) < 0) {
432         TRACE_GDB_REMOTE(
433             "SocketTransport::WaitForDebugStubEvent: Failed to read from "
434             "debug stub event pipe fd\n");
435       }
436     }
437     if (FD_ISSET(handle_accept_, &fds)) ReadSomeData();
438   }
439 }
440 
SignalThreadEvent()441 bool SocketTransport::SignalThreadEvent() {
442   // Notify the debug stub by marking the thread as faulted.
443   char buf = 0;
444   if (write(faulted_thread_fd_write_, &buf, sizeof(buf)) != sizeof(buf)) {
445     TRACE_GDB_REMOTE(
446         "SocketTransport:SignalThreadEvent: Can't send debug stub "
447         "event\n");
448     return false;
449   }
450   return true;
451 }
452 
453 #endif  // _WIN32
454 
455 }  // namespace gdb_server
456 }  // namespace wasm
457 }  // namespace internal
458 }  // namespace v8
459 
460 #undef SD_BOTH
461