Browse Source

fix stale pointer after realloc in uasync socket/event handlers

congestion
Evgeny 5 months ago
parent
commit
1a4b0a9de6
  1. 484
      lib/u_async.c

484
lib/u_async.c

@ -661,28 +661,25 @@ void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, s
if (read_cbk) ev.events |= EPOLLIN; if (read_cbk) ev.events |= EPOLLIN;
if (write_cbk) ev.events |= EPOLLOUT; if (write_cbk) ev.events |= EPOLLOUT;
// Use level-triggered mode (default) for compatibility with UDP sockets // Use level-triggered mode (default) for compatibility with UDP sockets
ev.data.ptr = &ua->sockets->sockets[index]; ev.data.fd = fd;
if (epoll_ctl(ua->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { if (epoll_ctl(ua->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
// Failed to add to epoll - remove from socket array and return error socket_array_remove(ua->sockets, fd);
socket_array_remove(ua->sockets, fd); ua->socket_alloc_count--;
ua->socket_alloc_count--; return NULL;
return NULL; }
} }
} #endif
#endif
return (void*)(intptr_t)fd;
// Return pointer to the socket node as ID
return &ua->sockets->sockets[index];
} }
err_t uasync_remove_socket(struct UASYNC* ua, void* s_id) { err_t uasync_remove_socket(struct UASYNC* ua, void* s_id) {
if (!ua || !s_id) return ERR_FAIL; if (!ua || !s_id) return ERR_FAIL;
struct socket_node* node = (struct socket_node*)s_id; int fd = (int)(intptr_t)s_id;
if (!node->active || node->fd < 0) return ERR_FAIL; struct socket_node* node = socket_array_get(ua->sockets, fd);
if (!node || !node->active) return ERR_FAIL;
int fd = node->fd;
#if HAS_EPOLL #if HAS_EPOLL
// Remove from epoll if using epoll // Remove from epoll if using epoll
@ -709,32 +706,31 @@ void* uasync_add_socket_t(struct UASYNC* ua, socket_t sock, socket_t_callback_t
(socket_callback_t)except_cbk, user_data); (socket_callback_t)except_cbk, user_data);
if (index < 0) return NULL; if (index < 0) return NULL;
ua->socket_alloc_count++; ua->socket_alloc_count++;
ua->poll_fds_dirty = 1; ua->poll_fds_dirty = 1;
#if HAS_EPOLL #ifdef _WIN32
if (ua->use_epoll && ua->epoll_fd >= 0) { int fd = (int)(intptr_t)sock;
struct epoll_event ev; #else
ev.events = 0; int fd = sock;
if (read_cbk) ev.events |= EPOLLIN; #endif
if (write_cbk) ev.events |= EPOLLOUT;
ev.data.ptr = &ua->sockets->sockets[index]; #if HAS_EPOLL
if (ua->use_epoll && ua->epoll_fd >= 0) {
// On Windows, need to cast socket_t to int for epoll_ctl struct epoll_event ev;
#ifdef _WIN32 ev.events = 0;
int fd = (int)(intptr_t)sock; if (read_cbk) ev.events |= EPOLLIN;
#else if (write_cbk) ev.events |= EPOLLOUT;
int fd = sock; ev.data.fd = fd;
#endif if (epoll_ctl(ua->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
if (epoll_ctl(ua->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { socket_array_remove(ua->sockets, fd);
socket_array_remove(ua->sockets, fd); ua->socket_alloc_count--;
ua->socket_alloc_count--; return NULL;
return NULL; }
} }
} #endif
#endif
return (void*)(intptr_t)fd;
return &ua->sockets->sockets[index];
} }
// Remove socket by socket_t // Remove socket by socket_t
@ -836,56 +832,73 @@ static void rebuild_poll_fds(struct UASYNC* ua) {
ua->poll_fds_dirty = 0; ua->poll_fds_dirty = 0;
} }
// Process events from epoll (Linux only) // Process events from epoll (Linux only). Each callback group re-looks up node
#if HAS_EPOLL // via socket_array_get to survive realloc triggered by other callbacks.
static void process_epoll_events(struct UASYNC* ua, struct epoll_event* events, int n_events) { #if HAS_EPOLL
for (int i = 0; i < n_events; i++) { static void process_epoll_events(struct UASYNC* ua, struct epoll_event* events, int n_events) {
// Check if this is the wakeup fd (data.ptr is NULL) for (int i = 0; i < n_events; i++) {
if (events[i].data.ptr == NULL) { if (events[i].data.fd == -1) {
if (events[i].events & EPOLLIN) { if (events[i].events & EPOLLIN) {
drain_wakeup_pipe(ua); drain_wakeup_pipe(ua);
} }
continue; continue;
} }
// Socket event int fd = events[i].data.fd;
struct socket_node* node = (struct socket_node*)events[i].data.ptr;
if (!node || !node->active) continue; if (events[i].events & (EPOLLERR | EPOLLHUP)) {
struct socket_node* node = socket_array_get(ua->sockets, fd);
/* Check for error conditions first */ if (node && node->active && node->except_cbk) {
if (events[i].events & (EPOLLERR | EPOLLHUP)) { socket_callback_t cb = node->except_cbk;
if (node->except_cbk) { int node_fd = node->fd;
node->except_cbk(node->fd, node->user_data); void* ud = node->user_data;
} cb(node_fd, ud);
} }
}
/* Read readiness - use appropriate callback based on socket type */
if (events[i].events & EPOLLIN) { if (events[i].events & EPOLLIN) {
if (node->type == SOCKET_NODE_TYPE_SOCK) { struct socket_node* node = socket_array_get(ua->sockets, fd);
if (node->read_cbk_sock) { if (node && node->active) {
node->read_cbk_sock(node->sock, node->user_data); if (node->type == SOCKET_NODE_TYPE_SOCK) {
} if (node->read_cbk_sock) {
} else { socket_t_callback_t cb = node->read_cbk_sock;
if (node->read_cbk) { socket_t sock = node->sock;
node->read_cbk(node->fd, node->user_data); void* ud = node->user_data;
} cb(sock, ud);
} }
} } else {
if (node->read_cbk) {
/* Write readiness - use appropriate callback based on socket type */ socket_callback_t cb = node->read_cbk;
if (events[i].events & EPOLLOUT) { int node_fd = node->fd;
if (node->type == SOCKET_NODE_TYPE_SOCK) { void* ud = node->user_data;
if (node->write_cbk_sock) { cb(node_fd, ud);
node->write_cbk_sock(node->sock, node->user_data); }
} }
} else { }
if (node->write_cbk) { }
node->write_cbk(node->fd, node->user_data);
} if (events[i].events & EPOLLOUT) {
} struct socket_node* node = socket_array_get(ua->sockets, fd);
} if (node && node->active) {
} if (node->type == SOCKET_NODE_TYPE_SOCK) {
} if (node->write_cbk_sock) {
socket_t_callback_t cb = node->write_cbk_sock;
socket_t sock = node->sock;
void* ud = node->user_data;
cb(sock, ud);
}
} else {
if (node->write_cbk) {
socket_callback_t cb = node->write_cbk;
int node_fd = node->fd;
void* ud = node->user_data;
cb(node_fd, ud);
}
}
}
}
}
}
#endif #endif
// Instance version // Instance version
@ -1039,55 +1052,87 @@ void uasync_poll(struct UASYNC* ua, int timeout_tb) {
return; return;
} }
if (ret > 0) { if (ret > 0) {
for (int i = 0; i < ua->sockets->count; i++) { for (int i = 0; i < ua->sockets->count; i++) {
int idx = ua->sockets->active_indices[i]; int idx = ua->sockets->active_indices[i];
struct socket_node* node = &ua->sockets->sockets[idx]; struct socket_node* node = &ua->sockets->sockets[idx];
if (!node->active) continue; if (!node->active) continue;
SOCKET s; SOCKET s;
if (node->type == SOCKET_NODE_TYPE_SOCK) { int fd;
s = node->sock; int type = node->type;
} else { if (type == SOCKET_NODE_TYPE_SOCK) {
s = (SOCKET)node->fd; s = node->sock;
} fd = (int)(intptr_t)s;
} else {
int has_read = FD_ISSET(s, &read_fds); s = (SOCKET)node->fd;
int has_write = FD_ISSET(s, &write_fds); fd = node->fd;
int has_except = FD_ISSET(s, &except_fds); }
if (!has_read && !has_write && !has_except) continue; int has_read = FD_ISSET(s, &read_fds);
int has_write = FD_ISSET(s, &write_fds);
if (has_except) { int has_except = FD_ISSET(s, &except_fds);
if (node->except_cbk) {
node->except_cbk(node->fd, node->user_data); if (!has_read && !has_write && !has_except) continue;
}
} if (has_except) {
struct socket_node* n = (type == SOCKET_NODE_TYPE_SOCK) ?
if (has_read) { socket_array_get_by_sock(ua->sockets, s) :
if (node->type == SOCKET_NODE_TYPE_SOCK) { socket_array_get(ua->sockets, fd);
if (node->read_cbk_sock) { if (n && n->active && n->except_cbk) {
node->read_cbk_sock(node->sock, node->user_data); socket_callback_t cb = n->except_cbk;
} int n_fd = n->fd;
} else { void* ud = n->user_data;
if (node->read_cbk) { cb(n_fd, ud);
node->read_cbk(node->fd, node->user_data); }
} }
}
} if (has_read) {
struct socket_node* n = (type == SOCKET_NODE_TYPE_SOCK) ?
if (has_write) { socket_array_get_by_sock(ua->sockets, s) :
if (node->type == SOCKET_NODE_TYPE_SOCK) { socket_array_get(ua->sockets, fd);
if (node->write_cbk_sock) { if (n && n->active) {
node->write_cbk_sock(node->sock, node->user_data); if (type == SOCKET_NODE_TYPE_SOCK) {
} if (n->read_cbk_sock) {
} else { socket_t_callback_t cb = n->read_cbk_sock;
if (node->write_cbk) { socket_t sock = n->sock;
node->write_cbk(node->fd, node->user_data); void* ud = n->user_data;
} cb(sock, ud);
} }
} } else {
} if (n->read_cbk) {
socket_callback_t cb = n->read_cbk;
int n_fd = n->fd;
void* ud = n->user_data;
cb(n_fd, ud);
}
}
}
}
if (has_write) {
struct socket_node* n = (type == SOCKET_NODE_TYPE_SOCK) ?
socket_array_get_by_sock(ua->sockets, s) :
socket_array_get(ua->sockets, fd);
if (n && n->active) {
if (type == SOCKET_NODE_TYPE_SOCK) {
if (n->write_cbk_sock) {
socket_t_callback_t cb = n->write_cbk_sock;
socket_t sock = n->sock;
void* ud = n->user_data;
cb(sock, ud);
}
} else {
if (n->write_cbk) {
socket_callback_t cb = n->write_cbk;
int n_fd = n->fd;
void* ud = n->user_data;
cb(n_fd, ud);
}
}
}
}
}
} }
#else #else
// On non-Windows, use poll() // On non-Windows, use poll()
@ -1111,68 +1156,91 @@ void uasync_poll(struct UASYNC* ua, int timeout_tb) {
return; return;
} }
/* Process socket events first to give sockets higher priority */ /* Process socket events first to give sockets higher priority */
if (ret > 0) { if (ret > 0) {
for (int i = 0; i < ua->poll_fds_count; i++) { for (int i = 0; i < ua->poll_fds_count; i++) {
if (ua->poll_fds[i].revents == 0) continue; if (ua->poll_fds[i].revents == 0) continue;
/* Handle wakeup fd separately */ /* Handle wakeup fd separately */
if (wakeup_fd_present && i == 0) { if (wakeup_fd_present && i == 0) {
if (ua->poll_fds[i].revents & POLLIN) { if (ua->poll_fds[i].revents & POLLIN) {
drain_wakeup_pipe(ua); drain_wakeup_pipe(ua);
} }
continue; continue;
} }
/* Socket event - lookup by fd */ int fd = ua->poll_fds[i].fd;
struct socket_node* node = socket_array_get(ua->sockets, ua->poll_fds[i].fd);
if (!node) { // Try by socket_t (in case this is a socket) /* Check for error conditions first — fresh lookup per group */
socket_t lookup_sock = ua->poll_fds[i].fd; if (ua->poll_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
node = socket_array_get_by_sock(ua->sockets, lookup_sock); struct socket_node* node = socket_array_get(ua->sockets, fd);
} if (!node) node = socket_array_get_by_sock(ua->sockets, (socket_t)fd);
if (!node) continue; // Socket may have been removed if (node && node->active && node->except_cbk) {
socket_callback_t cb = node->except_cbk;
/* Check for error conditions first */ int n_fd = node->fd;
if (ua->poll_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { void* ud = node->user_data;
/* Treat as exceptional condition */ cb(n_fd, ud);
if (node->except_cbk) { }
node->except_cbk(node->fd, node->user_data); }
}
} /* Exceptional data (out-of-band) */
if (ua->poll_fds[i].revents & POLLPRI) {
/* Exceptional data (out-of-band) */ struct socket_node* node = socket_array_get(ua->sockets, fd);
if (ua->poll_fds[i].revents & POLLPRI) { if (!node) node = socket_array_get_by_sock(ua->sockets, (socket_t)fd);
if (node->except_cbk) { if (node && node->active && node->except_cbk) {
node->except_cbk(node->fd, node->user_data); socket_callback_t cb = node->except_cbk;
} int n_fd = node->fd;
} void* ud = node->user_data;
cb(n_fd, ud);
/* Read readiness - use appropriate callback based on socket type */ }
if (ua->poll_fds[i].revents & POLLIN) { }
if (node->type == SOCKET_NODE_TYPE_SOCK) {
if (node->read_cbk_sock) { /* Read readiness — fresh lookup, copy cb+args before call */
node->read_cbk_sock(node->sock, node->user_data); if (ua->poll_fds[i].revents & POLLIN) {
} struct socket_node* node = socket_array_get(ua->sockets, fd);
} else { if (!node) node = socket_array_get_by_sock(ua->sockets, (socket_t)fd);
if (node->read_cbk) { if (node && node->active) {
node->read_cbk(node->fd, node->user_data); if (node->type == SOCKET_NODE_TYPE_SOCK) {
} if (node->read_cbk_sock) {
} socket_t_callback_t cb = node->read_cbk_sock;
} socket_t sock = node->sock;
void* ud = node->user_data;
/* Write readiness - use appropriate callback based on socket type */ cb(sock, ud);
if (ua->poll_fds[i].revents & POLLOUT) { }
if (node->type == SOCKET_NODE_TYPE_SOCK) { } else {
if (node->write_cbk_sock) { if (node->read_cbk) {
node->write_cbk_sock(node->sock, node->user_data); socket_callback_t cb = node->read_cbk;
} int n_fd = node->fd;
} else { void* ud = node->user_data;
if (node->write_cbk) { cb(n_fd, ud);
node->write_cbk(node->fd, node->user_data); }
} }
} }
} }
}
/* Write readiness — fresh lookup, copy cb+args before call */
if (ua->poll_fds[i].revents & POLLOUT) {
struct socket_node* node = socket_array_get(ua->sockets, fd);
if (!node) node = socket_array_get_by_sock(ua->sockets, (socket_t)fd);
if (node && node->active) {
if (node->type == SOCKET_NODE_TYPE_SOCK) {
if (node->write_cbk_sock) {
socket_t_callback_t cb = node->write_cbk_sock;
socket_t sock = node->sock;
void* ud = node->user_data;
cb(sock, ud);
}
} else {
if (node->write_cbk) {
socket_callback_t cb = node->write_cbk;
int n_fd = node->fd;
void* ud = node->user_data;
cb(n_fd, ud);
}
}
}
}
}
} }
#endif #endif
@ -1294,7 +1362,7 @@ struct UASYNC* uasync_create(void) {
if (ua->wakeup_initialized) { if (ua->wakeup_initialized) {
struct epoll_event ev; struct epoll_event ev;
ev.events = EPOLLIN; ev.events = EPOLLIN;
ev.data.ptr = NULL; // NULL ptr indicates wakeup fd ev.data.fd = -1;
if (epoll_ctl(ua->epoll_fd, EPOLL_CTL_ADD, ua->wakeup_pipe[0], &ev) < 0) { if (epoll_ctl(ua->epoll_fd, EPOLL_CTL_ADD, ua->wakeup_pipe[0], &ev) < 0) {
DEBUG_WARN(DEBUG_CATEGORY_UASYNC, "Failed to add wakeup pipe to epoll: %s", strerror(errno)); DEBUG_WARN(DEBUG_CATEGORY_UASYNC, "Failed to add wakeup pipe to epoll: %s", strerror(errno));
} }

Loading…
Cancel
Save