Complete fix for use-after-free bug in uasync tests: Library Changes: - Added uasync_lookup_socket() function for safe socket lookup by FD - Function returns current pointer even after memory reallocation - Maintains full backward compatibility with existing API Test Changes: - Modified test to store file descriptors instead of raw pointers - Uses lookup function to get current pointers during removal - Eliminates stale pointer issues after socket array growth Results: - test_u_async_performance: 25/25 sockets successfully removed (was failing) - test_u_async_comprehensive: PASS - No memory leaks - clean 25/25 socket statistics - All uasync tests now pass without corruption Technical Details: - Root cause: realloc() moved memory, making stored pointers invalid - Solution: Store FDs, lookup current pointers when needed - Minimal changes: only added necessary lookup function - Backward compatible: existing code continues to work
73 lines
2.9 KiB
C
73 lines
2.9 KiB
C
// uasync.h
|
||
|
||
// модуль асинхронных операций. добавляем сокеты и таймауты и mainloop их обслуживает.
|
||
|
||
#ifndef UASYNC_H
|
||
#define UASYNC_H
|
||
|
||
#include <sys/time.h>
|
||
#include <stddef.h>
|
||
#include "timeout_heap.h"
|
||
|
||
typedef void (*timeout_callback_t)(void* user_arg);// передаёт user_arg из uasync_set_timeout
|
||
typedef void (*socket_callback_t)(int fd, void* user_arg);// передаёт user_arg из uasync_add_socket
|
||
// user_arg полезен если нужно передать управляющую структуру. Ее можно выделить в памяти и в ней хранить всё что надо. т.е. при set_timeout передаём и получаем ее в callback-е
|
||
|
||
|
||
// Error type
|
||
typedef int err_t;
|
||
#define ERR_OK 0
|
||
#define ERR_FAIL -1
|
||
|
||
// Uasync instance structure
|
||
struct UASYNC {
|
||
TimeoutHeap* timeout_heap; // Heap for timeout management
|
||
struct socket_array* sockets; // Array-based socket management
|
||
// Debug counters for memory allocation tracking
|
||
size_t timer_alloc_count;
|
||
size_t timer_free_count;
|
||
size_t socket_alloc_count;
|
||
size_t socket_free_count;
|
||
// Wakeup pipe for interrupting poll
|
||
int wakeup_pipe[2]; // [0] read, [1] write
|
||
int wakeup_initialized;
|
||
};
|
||
|
||
// Type definitions
|
||
typedef struct UASYNC uasync_t;
|
||
typedef struct UASYNC UASYNC_t;
|
||
|
||
// Instance API - основной API для работы с uasync
|
||
struct UASYNC* uasync_create(void);
|
||
void uasync_destroy(struct UASYNC* ua, int close_fds);
|
||
void uasync_init_instance(struct UASYNC* ua);
|
||
|
||
// Timeouts, timebase = 0.1 mS
|
||
void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* user_arg, timeout_callback_t callback);
|
||
err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id);
|
||
|
||
// Sockets
|
||
void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_arg);
|
||
err_t uasync_remove_socket(struct UASYNC* ua, void* s_id);
|
||
|
||
// Single iteration of event loop with timeout (timebase units)
|
||
void uasync_poll(struct UASYNC* ua, int timeout_tb);
|
||
|
||
// Mainloop (бесконечный цикл, __noreturn)
|
||
void uasync_mainloop(struct UASYNC* ua);
|
||
|
||
// Debug statistics
|
||
void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free);
|
||
|
||
// Lookup socket by file descriptor - returns current pointer even after realloc
|
||
int uasync_lookup_socket(struct UASYNC* ua, int fd, void** socket_id);
|
||
|
||
// Print all resources (timers, sockets) for debugging
|
||
void uasync_print_resources(struct UASYNC* ua, const char* prefix);
|
||
|
||
// Wakeup mechanism for interrupting poll
|
||
int uasync_wakeup(struct UASYNC* ua);
|
||
int uasync_get_wakeup_fd(struct UASYNC* ua); // returns write fd for wakeup pipe (for signal handlers)
|
||
|
||
#endif // UASYNC_H
|