Coverage Report

Created: 2025-08-24 06:20

/src/tor/src/lib/net/alertsock.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2003-2004, Roger Dingledine
2
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3
 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4
/* See LICENSE for licensing information */
5
6
/**
7
 * \file alertsock.c
8
 *
9
 * \brief Use a socket to alert the main thread from a worker thread.
10
 *
11
 * Because our main loop spends all of its time in select, epoll, kqueue, or
12
 * etc, we need a way to wake up the main loop from another thread.  This code
13
 * tries to provide the fastest reasonable way to do that, depending on our
14
 * platform.
15
 **/
16
17
#include "orconfig.h"
18
#include "lib/net/alertsock.h"
19
#include "lib/net/socket.h"
20
#include "lib/log/util_bug.h"
21
22
#ifdef HAVE_SYS_EVENTFD_H
23
#include <sys/eventfd.h>
24
#endif
25
#ifdef HAVE_FCNTL_H
26
#include <fcntl.h>
27
#endif
28
#ifdef HAVE_UNISTD_H
29
#include <unistd.h>
30
#endif
31
#ifdef HAVE_SYS_SOCKET_H
32
#include <sys/socket.h>
33
#endif
34
#ifdef _WIN32
35
#include <winsock2.h>
36
#endif
37
38
#if defined(HAVE_EVENTFD) || defined(HAVE_PIPE)
39
/* As write(), but retry on EINTR, and return the negative error code on
40
 * error. */
41
static int
42
write_ni(int fd, const void *buf, size_t n)
43
0
{
44
0
  int r;
45
0
 again:
46
0
  r = (int) write(fd, buf, n);
47
0
  if (r < 0) {
48
0
    if (errno == EINTR)
49
0
      goto again;
50
0
    else
51
0
      return -errno;
52
0
  }
53
0
  return r;
54
0
}
55
/* As read(), but retry on EINTR, and return the negative error code on error.
56
 */
57
static int
58
read_ni(int fd, void *buf, size_t n)
59
0
{
60
0
  int r;
61
0
 again:
62
0
  r = (int) read(fd, buf, n);
63
0
  if (r < 0) {
64
0
    if (errno == EINTR)
65
0
      goto again;
66
0
    else
67
0
      return -errno;
68
0
  }
69
0
  return r;
70
0
}
71
#endif /* defined(HAVE_EVENTFD) || defined(HAVE_PIPE) */
72
73
/** As send(), but retry on EINTR, and return the negative error code on
74
 * error. */
75
static int
76
send_ni(int fd, const void *buf, size_t n, int flags)
77
0
{
78
0
  int r;
79
0
 again:
80
0
  r = (int) send(fd, buf, n, flags);
81
0
  if (r < 0) {
82
0
    int error = tor_socket_errno(fd);
83
0
    if (ERRNO_IS_EINTR(error))
84
0
      goto again;
85
0
    else
86
0
      return -error;
87
0
  }
88
0
  return r;
89
0
}
90
91
/** As recv(), but retry on EINTR, and return the negative error code on
92
 * error. */
93
static int
94
recv_ni(int fd, void *buf, size_t n, int flags)
95
0
{
96
0
  int r;
97
0
 again:
98
0
  r = (int) recv(fd, buf, n, flags);
99
0
  if (r < 0) {
100
0
    int error = tor_socket_errno(fd);
101
0
    if (ERRNO_IS_EINTR(error))
102
0
      goto again;
103
0
    else
104
0
      return -error;
105
0
  }
106
0
  return r;
107
0
}
108
109
#ifdef HAVE_EVENTFD
110
/* Increment the event count on an eventfd <b>fd</b> */
111
static int
112
eventfd_alert(int fd)
113
0
{
114
0
  uint64_t u = 1;
115
0
  int r = write_ni(fd, (void*)&u, sizeof(u));
116
0
  if (r < 0 && -r != EAGAIN)
117
0
    return -1;
118
0
  return 0;
119
0
}
120
121
/* Drain all events from an eventfd <b>fd</b>. */
122
static int
123
eventfd_drain(int fd)
124
0
{
125
0
  uint64_t u = 0;
126
0
  int r = read_ni(fd, (void*)&u, sizeof(u));
127
0
  if (r < 0 && -r != EAGAIN)
128
0
    return r;
129
0
  return 0;
130
0
}
131
#endif /* defined(HAVE_EVENTFD) */
132
133
#ifdef HAVE_PIPE
134
/** Send a byte over a pipe. Return 0 on success or EAGAIN; -1 on error */
135
static int
136
pipe_alert(int fd)
137
0
{
138
0
  ssize_t r = write_ni(fd, "x", 1);
139
0
  if (r < 0 && -r != EAGAIN)
140
0
    return (int)r;
141
0
  return 0;
142
0
}
143
144
/** Drain all input from a pipe <b>fd</b> and ignore it.  Return 0 on
145
 * success, -1 on error. */
146
static int
147
pipe_drain(int fd)
148
0
{
149
0
  char buf[32];
150
0
  ssize_t r;
151
0
  do {
152
0
    r = read_ni(fd, buf, sizeof(buf));
153
0
  } while (r > 0);
154
0
  if (r < 0 && errno != EAGAIN)
155
0
    return -errno;
156
  /* A value of r = 0 means EOF on the fd so successfully drained. */
157
0
  return 0;
158
0
}
159
#endif /* defined(HAVE_PIPE) */
160
161
/** Send a byte on socket <b>fd</b>t.  Return 0 on success or EAGAIN,
162
 * -1 on error. */
163
static int
164
sock_alert(tor_socket_t fd)
165
0
{
166
0
  ssize_t r = send_ni(fd, "x", 1, 0);
167
0
  if (r < 0 && !ERRNO_IS_EAGAIN(-r))
168
0
    return (int)r;
169
0
  return 0;
170
0
}
171
172
/** Drain all the input from a socket <b>fd</b>, and ignore it.  Return 0 on
173
 * success, -errno on error. */
174
static int
175
sock_drain(tor_socket_t fd)
176
0
{
177
0
  char buf[32];
178
0
  ssize_t r;
179
0
  do {
180
0
    r = recv_ni(fd, buf, sizeof(buf), 0);
181
0
  } while (r > 0);
182
0
  if (r < 0 && !ERRNO_IS_EAGAIN(-r))
183
0
    return (int)r;
184
  /* A value of r = 0 means EOF on the fd so successfully drained. */
185
0
  return 0;
186
0
}
187
188
/** Allocate a new set of alert sockets, and set the appropriate function
189
 * pointers, in <b>socks_out</b>. */
190
int
191
alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags)
192
0
{
193
0
  tor_socket_t socks[2] = { TOR_INVALID_SOCKET, TOR_INVALID_SOCKET };
194
195
0
#ifdef HAVE_EVENTFD
196
  /* First, we try the Linux eventfd() syscall.  This gives a 64-bit counter
197
   * associated with a single file descriptor. */
198
0
#if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
199
0
  if (!(flags & ASOCKS_NOEVENTFD2))
200
0
    socks[0] = eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK);
201
0
#endif
202
0
  if (socks[0] < 0 && !(flags & ASOCKS_NOEVENTFD)) {
203
0
    socks[0] = eventfd(0,0);
204
0
    if (socks[0] >= 0) {
205
0
      if (fcntl(socks[0], F_SETFD, FD_CLOEXEC) < 0 ||
206
0
          set_socket_nonblocking(socks[0]) < 0) {
207
        // LCOV_EXCL_START -- if eventfd succeeds, fcntl will.
208
0
        tor_assert_nonfatal_unreached();
209
0
        close(socks[0]);
210
0
        return -1;
211
        // LCOV_EXCL_STOP
212
0
      }
213
0
    }
214
0
  }
215
0
  if (socks[0] >= 0) {
216
0
    socks_out->read_fd = socks_out->write_fd = socks[0];
217
0
    socks_out->alert_fn = eventfd_alert;
218
0
    socks_out->drain_fn = eventfd_drain;
219
0
    return 0;
220
0
  }
221
0
#endif /* defined(HAVE_EVENTFD) */
222
223
0
#ifdef HAVE_PIPE2
224
  /* Now we're going to try pipes. First type the pipe2() syscall, if we
225
   * have it, so we can save some calls... */
226
0
  if (!(flags & ASOCKS_NOPIPE2) &&
227
0
      pipe2(socks, O_NONBLOCK|O_CLOEXEC) == 0) {
228
0
    socks_out->read_fd = socks[0];
229
0
    socks_out->write_fd = socks[1];
230
0
    socks_out->alert_fn = pipe_alert;
231
0
    socks_out->drain_fn = pipe_drain;
232
0
    return 0;
233
0
  }
234
0
#endif /* defined(HAVE_PIPE2) */
235
236
0
#ifdef HAVE_PIPE
237
  /* Now try the regular pipe() syscall.  Pipes have a bit lower overhead than
238
   * socketpairs, fwict. */
239
0
  if (!(flags & ASOCKS_NOPIPE) &&
240
0
      pipe(socks) == 0) {
241
0
    if (fcntl(socks[0], F_SETFD, FD_CLOEXEC) < 0 ||
242
0
        fcntl(socks[1], F_SETFD, FD_CLOEXEC) < 0 ||
243
0
        set_socket_nonblocking(socks[0]) < 0 ||
244
0
        set_socket_nonblocking(socks[1]) < 0) {
245
      // LCOV_EXCL_START -- if pipe succeeds, you can fcntl the output
246
0
      tor_assert_nonfatal_unreached();
247
0
      close(socks[0]);
248
0
      close(socks[1]);
249
0
      return -1;
250
      // LCOV_EXCL_STOP
251
0
    }
252
0
    socks_out->read_fd = socks[0];
253
0
    socks_out->write_fd = socks[1];
254
0
    socks_out->alert_fn = pipe_alert;
255
0
    socks_out->drain_fn = pipe_drain;
256
0
    return 0;
257
0
  }
258
0
#endif /* defined(HAVE_PIPE) */
259
260
  /* If nothing else worked, fall back on socketpair(). */
261
0
  if (!(flags & ASOCKS_NOSOCKETPAIR) &&
262
0
      tor_socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == 0) {
263
0
    if (set_socket_nonblocking(socks[0]) < 0 ||
264
0
        set_socket_nonblocking(socks[1])) {
265
      // LCOV_EXCL_START -- if socketpair worked, you can make it nonblocking.
266
0
      tor_assert_nonfatal_unreached();
267
0
      tor_close_socket(socks[0]);
268
0
      tor_close_socket(socks[1]);
269
0
      return -1;
270
      // LCOV_EXCL_STOP
271
0
    }
272
0
    socks_out->read_fd = socks[0];
273
0
    socks_out->write_fd = socks[1];
274
0
    socks_out->alert_fn = sock_alert;
275
0
    socks_out->drain_fn = sock_drain;
276
0
    return 0;
277
0
  }
278
0
  return -1;
279
0
}
280
281
/** Close the sockets in <b>socks</b>. */
282
void
283
alert_sockets_close(alert_sockets_t *socks)
284
0
{
285
0
  if (socks->alert_fn == sock_alert) {
286
    /* they are sockets. */
287
0
    tor_close_socket(socks->read_fd);
288
0
    tor_close_socket(socks->write_fd);
289
0
  } else {
290
0
    close(socks->read_fd);
291
0
    if (socks->write_fd != socks->read_fd)
292
0
      close(socks->write_fd);
293
0
  }
294
0
  socks->read_fd = socks->write_fd = -1;
295
0
}