From 10303a70b176746269bcd91d33607f7e16536d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Mon, 6 Jan 2025 11:28:47 +0100 Subject: [PATCH] [core] fix potential use-after-free after li_job_reset * need `job->ref->job = NULL;` when detaching ref from job * li_job_reset is just li_job_stop + reset of generation * move optimization of keeping jobref with refcount==1 to li_job_stop Change-Id: Ia2e4db186582ebfea92a64aa9aeaf9a67bb722bf --- include/lighttpd/jobqueue.h | 4 +++- src/common/jobqueue.c | 22 ++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/include/lighttpd/jobqueue.h b/include/lighttpd/jobqueue.h index bd6cbd2b..677a572c 100644 --- a/include/lighttpd/jobqueue.h +++ b/include/lighttpd/jobqueue.h @@ -14,6 +14,7 @@ typedef void (*liJobCB)(liJob *job); /* All data here is private; use the functions to interact with the job-queue */ struct liJob { + /* prevent running callback in a loop (delay if job generation == queue generation) */ guint generation; GList link; liJobCB callback; @@ -43,7 +44,8 @@ LI_API void li_job_queue_clear(liJobQueue *jq); /* runs until all jobs are done LI_API void li_job_init(liJob *job, liJobCB callback); LI_API void li_job_reset(liJob *job); -LI_API void li_job_stop(liJob *job); /* remove job from queue if active and detach references */ +/* remove job from queue if active and detach existing references, but doesn't reset loop detection */ +LI_API void li_job_stop(liJob *job); LI_API void li_job_clear(liJob *job); /* marks the job for later execution */ diff --git a/src/common/jobqueue.c b/src/common/jobqueue.c index bdd26644..b0a3c205 100644 --- a/src/common/jobqueue.c +++ b/src/common/jobqueue.c @@ -100,6 +100,11 @@ void li_job_init(liJob *job, liJobCB callback) { } void li_job_reset(liJob *job) { + li_job_stop(job); + job->generation = 0; +} + +void li_job_stop(liJob *job) { if (NULL != job->link.data) { liJobQueue *jq = job->link.data; @@ -107,31 +112,16 @@ void li_job_reset(liJob *job) { job->link.data = NULL; } - job->generation = 0; if (NULL != job->ref) { /* keep it if refcount == 1, as we are the only reference then */ if (1 < g_atomic_int_get(&job->ref->refcount)) { + job->ref->job = NULL; li_job_ref_release(job->ref); job->ref = NULL; } } } -void li_job_stop(liJob *job) { - if (NULL != job->link.data) { - liJobQueue *jq = job->link.data; - - g_queue_unlink(&jq->queue, &job->link); - job->link.data = NULL; - } - - if (NULL != job->ref) { - job->ref->job = NULL; - li_job_ref_release(job->ref); - job->ref = NULL; - } -} - void li_job_clear(liJob *job) { if (NULL != job->link.data) { liJobQueue *jq = job->link.data;