From 3813460d57ae33f93548c7b382ba657b8b123486 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 23 Aug 2025 20:41:01 +0200 Subject: [PATCH] unix: replace uv__io_t callback pointer with enum (#4854) 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 --- include/uv/unix.h | 5 +--- src/unix/aix.c | 8 +++---- src/unix/async.c | 4 ++-- src/unix/core.c | 52 +++++++++++++++++++++++++++++++++++++----- src/unix/internal.h | 53 +++++++++++++++++++++++++++++++++++++++++-- src/unix/kqueue.c | 16 ++++++------- src/unix/linux.c | 13 ++++------- src/unix/os390.c | 4 ++-- src/unix/pipe.c | 2 +- src/unix/poll.c | 4 ++-- src/unix/posix-poll.c | 4 ++-- src/unix/signal.c | 7 ++---- src/unix/stream.c | 5 ++-- src/unix/sunos.c | 10 ++++---- src/unix/tcp.c | 2 +- src/unix/udp.c | 5 ++-- 16 files changed, 133 insertions(+), 61 deletions(-) diff --git a/include/uv/unix.h b/include/uv/unix.h index 7c972026f..c6ba419da 100644 --- a/include/uv/unix.h +++ b/include/uv/unix.h @@ -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. */ diff --git a/src/unix/aix.c b/src/unix/aix.c index 8343bc5c6..6666564c5 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -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; diff --git a/src/unix/async.c b/src/unix/async.c index 9e4e113a7..64cf2d639 100644 --- a/src/unix/async.c +++ b/src/unix/async.c @@ -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]); diff --git a/src/unix/core.c b/src/unix/core.c index 6e7416288..97db50345 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -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; } diff --git a/src/unix/internal.h b/src/unix/internal.h index d8c792f62..ac37fbdfe 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -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); diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index 39b72012c..9c5f19c00 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -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); diff --git a/src/unix/linux.c b/src/unix/linux.c index 4bfb2d2e7..435f6a1de 100644 --- a/src/unix/linux.c +++ b/src/unix/linux.c @@ -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; diff --git a/src/unix/os390.c b/src/unix/os390.c index 1b277292a..029add414 100644 --- a/src/unix/os390.c +++ b/src/unix/os390.c @@ -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; diff --git a/src/unix/pipe.c b/src/unix/pipe.c index f086a22f4..eb03a47cc 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -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); } diff --git a/src/unix/poll.c b/src/unix/poll.c index 535ac6baa..60c3c2009 100644 --- a/src/unix/poll.c +++ b/src/unix/poll.c @@ -26,7 +26,7 @@ #include -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; } diff --git a/src/unix/posix-poll.c b/src/unix/posix-poll.c index 2e016c2fb..2efb843fb 100644 --- a/src/unix/posix-poll.c +++ b/src/unix/posix-poll.c @@ -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; diff --git a/src/unix/signal.c b/src/unix/signal.c index ccaa72db4..91fb14946 100644 --- a/src/unix/signal.c +++ b/src/unix/signal.c @@ -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]; diff --git a/src/unix/stream.c b/src/unix/stream.c index 204f5a25d..725558662 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -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); diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 62584d72e..220ff74bd 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -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) diff --git a/src/unix/tcp.c b/src/unix/tcp.c index 1f59083e5..aa81c16d6 100644 --- a/src/unix/tcp.c +++ b/src/unix/tcp.c @@ -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); } diff --git a/src/unix/udp.c b/src/unix/udp.c index 75cac4cdf..5f8d86f57 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -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);