unix: replace uv__io_t callback pointer with enum

Rationale for changing it to an enum:

- frees up some bits that can be used for other things
- is potentially faster (direct vs. indirect call)
- is potentially more secure (makes UAF or overruns harder to exploit,
  no arbitrary function pointer to clobber)

Fixes: https://github.com/libuv/libuv/issues/4842
This commit is contained in:
Ben Noordhuis 2025-07-31 11:55:31 +02:00
parent b161b1f475
commit ed1ec562e3
16 changed files with 133 additions and 61 deletions

View File

@ -85,13 +85,10 @@
struct uv__io_s;
struct uv_loop_s;
typedef void (*uv__io_cb)(struct uv_loop_s* loop,
struct uv__io_s* w,
unsigned int events);
typedef struct uv__io_s uv__io_t;
struct uv__io_s {
uv__io_cb cb;
uintptr_t bits;
struct uv__queue pending_queue;
struct uv__queue watcher_queue;
unsigned int pevents; /* Pending event mask i.e. mask at next tick. */

View File

@ -324,7 +324,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
have_signals = 1;
} else {
uv__metrics_update_idle_time(loop);
w->cb(loop, w, pe->revents);
uv__io_cb(loop, w, pe->revents);
}
nevents++;
@ -339,7 +339,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
if (have_signals != 0) {
uv__metrics_update_idle_time(loop);
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
uv__signal_event(loop, &loop->signal_io_watcher, POLLIN);
}
loop->watchers[loop->nwatchers] = NULL;
@ -716,7 +716,7 @@ static int uv__parse_data(char *buf, int *events, uv_fs_event_t* handle) {
/* This is the internal callback */
static void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) {
void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) {
char result_data[RDWR_BUF_SIZE];
int bytes, rc = 0;
uv_fs_event_t* handle;
@ -828,7 +828,7 @@ int uv_fs_event_start(uv_fs_event_t* handle,
/* Setup/Initialize all the libuv routines */
uv__handle_start(handle);
uv__io_init(&handle->event_watcher, uv__ahafs_event, fd);
uv__io_init(&handle->event_watcher, UV__AHAFS_EVENT, fd);
handle->path = uv__strdup(filename);
handle->cb = cb;
handle->dir_filename = NULL;

View File

@ -157,7 +157,7 @@ void uv__async_close(uv_async_t* handle) {
}
static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
char buf[1024];
ssize_t r;
struct uv__queue queue;
@ -308,7 +308,7 @@ static int uv__async_start(uv_loop_t* loop) {
return err;
#endif
err = uv__io_init_start(loop, &loop->async_io_watcher, uv__async_io,
err = uv__io_init_start(loop, &loop->async_io_watcher, UV__ASYNC_IO,
pipefd[0], POLLIN);
if (err < 0) {
uv__close(pipefd[0]);

View File

@ -851,7 +851,7 @@ static void uv__run_pending(uv_loop_t* loop) {
uv__queue_remove(q);
uv__queue_init(q);
w = uv__queue_data(q, uv__io_t, pending_queue);
w->cb(loop, w, POLLOUT);
uv__io_cb(loop, w, POLLOUT);
}
}
@ -903,20 +903,61 @@ static int maybe_resize(uv_loop_t* loop, unsigned int len) {
}
void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) {
void uv__io_cb(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
switch (uv__io_cb_get(w)) {
case UV__AHAFS_EVENT:
uv__ahafs_event(loop, w, events);
break;
case UV__ASYNC_IO:
uv__async_io(loop, w, events);
break;
case UV__FS_EVENT:
uv__fs_event(loop, w, events);
break;
case UV__FS_EVENT_READ:
uv__fs_event_read(loop, w, events);
break;
case UV__INOTIFY_READ:
uv__inotify_read(loop, w, events);
break;
case UV__POLL_IO:
uv__poll_io(loop, w, events);
break;
case UV__SIGNAL_EVENT:
uv__signal_event(loop, w, events);
break;
case UV__SERVER_IO:
uv__server_io(loop, w, events);
break;
case UV__STREAM_IO:
uv__stream_io(loop, w, events);
break;
case UV__UDP_IO:
uv__udp_io(loop, w, events);
break;
default:
UNREACHABLE();
}
}
void uv__io_init(uv__io_t* w, uv__io_cb_t cb, int fd) {
assert(fd >= -1);
uv__queue_init(&w->pending_queue);
uv__queue_init(&w->watcher_queue);
w->cb = cb;
w->fd = fd;
w->bits = 0;
w->events = 0;
w->pevents = 0;
uv__io_cb_set(w, cb);
}
int uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
int err;
assert(uv__io_cb_get(w) >= UV__AHAFS_EVENT);
assert(uv__io_cb_get(w) <= UV__UDP_IO);
assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI)));
assert(0 != events);
assert(w->fd >= 0);
@ -950,17 +991,16 @@ int uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
int uv__io_init_start(uv_loop_t* loop,
uv__io_t* w,
uv__io_cb cb,
uv__io_cb_t cb,
int fd,
unsigned int events) {
int err;
assert(cb != NULL);
assert(fd > -1);
uv__io_init(w, cb, fd);
err = uv__io_start(loop, w, events);
if (err)
uv__io_init(w, NULL, -1);
uv__io_init(w, UV__NO_IO_CB, -1);
return err;
}

View File

@ -261,11 +261,60 @@ ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
void uv__make_close_pending(uv_handle_t* handle);
int uv__getiovmax(void);
void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd);
typedef enum {
UV__NO_IO_CB,
UV__AHAFS_EVENT,
UV__ASYNC_IO,
UV__FS_EVENT,
UV__FS_EVENT_READ,
UV__INOTIFY_READ,
UV__POLL_IO,
UV__SIGNAL_EVENT,
UV__SERVER_IO,
UV__STREAM_IO,
UV__UDP_IO,
} uv__io_cb_t;
#define uv__io_cb_get(w) ((uv__io_cb_t)((w)->bits & 15))
#define uv__io_cb_set(w, cb) \
do { \
(w)->bits -= uv__io_cb_get(w); \
(w)->bits |= (cb) & 15; \
} while (0)
void uv__ahafs_event(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__fs_event_read(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__inotify_read(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
#ifndef _AIX
#define uv__ahafs_event(loop, w, events) UNREACHABLE()
#endif
#ifndef __APPLE__
#define uv__fs_event(loop, w, events) UNREACHABLE()
#endif
#ifndef __linux__
#define uv__inotify_read(loop, w, events) UNREACHABLE()
#endif
#ifndef __sun__
#define uv__fs_event_read(loop, w, events) UNREACHABLE()
#endif
void uv__io_cb(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__io_init(uv__io_t* w, uv__io_cb_t cb, int fd);
int uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events);
int uv__io_init_start(uv_loop_t* loop,
uv__io_t* w,
uv__io_cb cb,
uv__io_cb_t cb,
int fd,
unsigned int events);
void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events);

View File

@ -48,8 +48,6 @@
#define EV_OOBAND EV_FLAG1
#endif
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags);
int uv__kqueue_init(uv_loop_t* loop) {
loop->backend_fd = kqueue();
@ -204,7 +202,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
fflags = 0;
op = EV_ADD;
if (w->cb == uv__fs_event) {
if (UV__FS_EVENT == uv__io_cb_get(w)) {
filter = EVFILT_VNODE;
fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME
| NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
@ -366,7 +364,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
w = &loop->async_io_watcher;
assert(fd == w->fd);
uv__metrics_update_idle_time(loop);
w->cb(loop, w, w->events);
uv__io_cb(loop, w, w->events);
nevents++;
continue;
}
@ -376,7 +374,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
assert(w->events == POLLIN);
assert(w->pevents == POLLIN);
uv__metrics_update_idle_time(loop);
w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */
uv__io_cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */
nevents++;
continue;
}
@ -420,7 +418,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
have_signals = 1;
} else {
uv__metrics_update_idle_time(loop);
w->cb(loop, w, revents);
uv__io_cb(loop, w, revents);
}
nevents++;
@ -440,7 +438,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
if (have_signals != 0) {
uv__metrics_update_idle_time(loop);
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
uv__signal_event(loop, &loop->signal_io_watcher, POLLIN);
}
loop->watchers[loop->nwatchers] = NULL;
@ -496,7 +494,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
}
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
uv_fs_event_t* handle;
struct kevent ev;
int events;
@ -622,7 +620,7 @@ fallback:
r = uv__io_init_start(handle->loop,
&handle->event_watcher,
uv__fs_event,
UV__FS_EVENT,
fd,
POLLIN);

View File

@ -267,9 +267,6 @@ struct watcher_root {
};
static int uv__inotify_fork(uv_loop_t* loop, struct watcher_list* root);
static void uv__inotify_read(uv_loop_t* loop,
uv__io_t* w,
unsigned int revents);
static int compare_watchers(const struct watcher_list* a,
const struct watcher_list* b);
static void maybe_free_watcher_list(struct watcher_list* w,
@ -1562,7 +1559,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
have_signals = 1;
} else {
uv__metrics_update_idle_time(loop);
w->cb(loop, w, pe->events);
uv__io_cb(loop, w, pe->events);
}
nevents++;
@ -1578,7 +1575,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
if (have_signals != 0) {
uv__metrics_update_idle_time(loop);
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
uv__signal_event(loop, &loop->signal_io_watcher, POLLIN);
}
lfields->inv = NULL;
@ -2494,7 +2491,7 @@ static int init_inotify(uv_loop_t* loop) {
if (fd < 0)
return UV__ERR(errno);
err = uv__io_init_start(loop, &loop->inotify_read_watcher, uv__inotify_read,
err = uv__io_init_start(loop, &loop->inotify_read_watcher, UV__INOTIFY_READ,
fd, POLLIN);
if (err) {
uv__close(fd);
@ -2590,9 +2587,7 @@ static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) {
}
static void uv__inotify_read(uv_loop_t* loop,
uv__io_t* dummy,
unsigned int events) {
void uv__inotify_read(uv_loop_t* loop, uv__io_t* dummy, unsigned int events) {
const struct inotify_event* e;
struct watcher_list* w;
uv_fs_event_t* h;

View File

@ -1008,7 +1008,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
have_signals = 1;
} else {
uv__metrics_update_idle_time(loop);
w->cb(loop, w, pe->events);
uv__io_cb(loop, w, pe->events);
}
nevents++;
}
@ -1023,7 +1023,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
if (have_signals != 0) {
uv__metrics_update_idle_time(loop);
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
uv__signal_event(loop, &loop->signal_io_watcher, POLLIN);
}
loop->watchers[loop->nwatchers] = NULL;

View File

@ -170,7 +170,7 @@ int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
return UV__ERR(errno);
handle->connection_cb = cb;
handle->io_watcher.cb = uv__server_io;
uv__io_cb_set(&handle->io_watcher, UV__SERVER_IO);
return uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
}

View File

@ -26,7 +26,7 @@
#include <assert.h>
static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
uv_poll_t* handle;
int pevents;
@ -87,7 +87,7 @@ int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
return err;
uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
uv__io_init(&handle->io_watcher, uv__poll_io, fd);
uv__io_init(&handle->io_watcher, UV__POLL_IO, fd);
handle->poll_cb = NULL;
return 0;
}

View File

@ -294,7 +294,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
have_signals = 1;
} else {
uv__metrics_update_idle_time(loop);
w->cb(loop, w, pe->revents);
uv__io_cb(loop, w, pe->revents);
}
nevents++;
@ -310,7 +310,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
if (have_signals != 0) {
uv__metrics_update_idle_time(loop);
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
uv__signal_event(loop, &loop->signal_io_watcher, POLLIN);
}
loop->poll_fds_iterating = 0;

View File

@ -45,7 +45,6 @@ static int uv__signal_start(uv_signal_t* handle,
uv_signal_cb signal_cb,
int signum,
int oneshot);
static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events);
static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2);
static void uv__signal_stop(uv_signal_t* handle);
static void uv__signal_unregister_handler(int signum);
@ -271,7 +270,7 @@ static int uv__signal_loop_once_init(uv_loop_t* loop) {
if (err)
return err;
err = uv__io_init_start(loop, &loop->signal_io_watcher, uv__signal_event,
err = uv__io_init_start(loop, &loop->signal_io_watcher, UV__SIGNAL_EVENT,
pipefd[0], POLLIN);
if (err) {
uv__close(pipefd[0]);
@ -436,9 +435,7 @@ static int uv__signal_start(uv_signal_t* handle,
}
static void uv__signal_event(uv_loop_t* loop,
uv__io_t* w,
unsigned int events) {
void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
uv__signal_msg_t* msg;
uv_signal_t* handle;
char buf[sizeof(uv__signal_msg_t) * 32];

View File

@ -73,7 +73,6 @@ STATIC_ASSERT(256 == sizeof(union uv__cmsg));
static void uv__stream_connect(uv_stream_t*);
static void uv__write(uv_stream_t* stream);
static void uv__read(uv_stream_t* stream);
static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
static void uv__write_callbacks(uv_stream_t* stream);
static size_t uv__write_req_size(uv_write_t* req);
static void uv__drain(uv_stream_t* stream);
@ -113,7 +112,7 @@ void uv__stream_init(uv_loop_t* loop,
stream->select = NULL;
#endif /* defined(__APPLE_) */
uv__io_init(&stream->io_watcher, uv__stream_io, -1);
uv__io_init(&stream->io_watcher, UV__STREAM_IO, -1);
}
@ -1186,7 +1185,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
}
static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
uv_stream_t* stream;
stream = container_of(w, uv_stream_t, io_watcher);

View File

@ -307,7 +307,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
have_signals = 1;
} else {
uv__metrics_update_idle_time(loop);
w->cb(loop, w, pe->portev_events);
uv__io_cb(loop, w, pe->portev_events);
}
nevents++;
@ -329,7 +329,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
if (have_signals != 0) {
uv__metrics_update_idle_time(loop);
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
uv__signal_event(loop, &loop->signal_io_watcher, POLLIN);
}
loop->watchers[loop->nwatchers] = NULL;
@ -446,9 +446,7 @@ static int uv__fs_event_rearm(uv_fs_event_t *handle) {
}
static void uv__fs_event_read(uv_loop_t* loop,
uv__io_t* w,
unsigned int revents) {
void uv__fs_event_read(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
uv_fs_event_t *handle = NULL;
timespec_t timeout;
port_event_t pe;
@ -548,7 +546,7 @@ int uv_fs_event_start(uv_fs_event_t* handle,
if (first_run) {
err = uv__io_init_start(handle->loop,
&handle->loop->fs_event_watcher,
uv__fs_event_read,
UV__FS_EVENT_READ,
portfd,
POLLIN);
if (err)

View File

@ -444,7 +444,7 @@ int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
tcp->flags |= UV_HANDLE_BOUND;
/* Start listening for connections. */
tcp->io_watcher.cb = uv__server_io;
uv__io_cb_set(&tcp->io_watcher, UV__SERVER_IO);
return uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);
}

View File

@ -41,7 +41,6 @@
#endif
static void uv__udp_run_completed(uv_udp_t* handle);
static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static void uv__udp_recvmsg(uv_udp_t* handle);
static void uv__udp_sendmsg(uv_udp_t* handle);
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
@ -136,7 +135,7 @@ static void uv__udp_run_completed(uv_udp_t* handle) {
}
static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
uv_udp_t* handle;
handle = container_of(w, uv_udp_t, io_watcher);
@ -881,7 +880,7 @@ int uv__udp_init_ex(uv_loop_t* loop,
handle->recv_cb = NULL;
handle->send_queue_size = 0;
handle->send_queue_count = 0;
uv__io_init(&handle->io_watcher, uv__udp_io, fd);
uv__io_init(&handle->io_watcher, UV__UDP_IO, fd);
uv__queue_init(&handle->write_queue);
uv__queue_init(&handle->write_completed_queue);