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