ll_queue: queue_waiter_cancel отменяет отложенный call_soon при internal==NULL (фикс UAF waiter_defer)

This commit is contained in:
evgeny committed 2026-09-26 18:58:27 +03:00
1 parent f6e6e2c73f
commit 44d4265084
2 files changed
+34 -2

No files matched your search

+27
View File
@@ -281,6 +281,32 @@ static void test_waiter_cancel(void) {
PASS();
}
static void test_waiter_cancel_deferred(void) {
TEST("waiter cancel deferred call_soon (internal==NULL)");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0, 0, 0, "wcd");
queue_set_threshold(q, 0, 0);
queue_set_waiter_defer(q, 1);
int called = 0;
struct queue_waiter_handle h = {0};
int ret = queue_waiter_wait(q, &h, waiter_cb, &called);
ASSERT(ret == 1, "immediate branch (empty queue)");
ASSERT(h.internal == NULL, "no waiter registered (deferred path)");
ASSERT(h.call_soon_id != NULL, "deferred call scheduled");
/* До фикса: queue_waiter_cancel выходил по !internal, call_soon_id оставался
* висеть и deferred-колбэк срабатывал позже (UAF после teardown). */
queue_waiter_cancel(q, &h);
ASSERT(h.call_soon_id == NULL, "call_soon_id cleared by cancel");
for (int i = 0; i < 10; i++) uasync_poll(ua, 1);
ASSERT_EQ(called, 0, "deferred callback cancelled (did not fire)");
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_waiter_queue_free(void) {
TEST("queue_free clears waiters");
struct UASYNC *ua = uasync_create();
@@ -454,6 +480,7 @@ int main(void) {
test_waiter();
test_waiter_multiple();
test_waiter_cancel();
test_waiter_cancel_deferred();
test_waiter_queue_free();
test_waiter_threshold();
test_waiter_reuse();