From b7bef6cc3c871a506bdecee91e8c4161a6817a8f Mon Sep 17 00:00:00 2001 From: Ashwin Naik Date: Thu, 27 Aug 2026 12:13:31 +0000 Subject: [PATCH] Fix Heap Use-After-Free in cupsdContinueJob via Concurrent Filter Deletion A heap use-after-free vulnerability exists in the CUPS scheduler where `cupsdContinueJob` drops `MimeDatabase->lock` while retaining references to borrowed `mime_filter_t` pointers in a local `filters` array. While the lock is temporarily dropped, a concurrent background thread (e.g., `create_local_bg_thread` processing an unauthenticated `CUPS-Create-Local-Printer` request) can trigger `cupsdSetPrinterAttrs` which calls `delete_printer_filters`. This unconditionally frees the `mime_filter_t` objects. When the main thread resumes and dereferences these pointers later in the job processing loop, it results in a UAF. This patch resolves the issue by creating a deep copy of the borrowed `mime_filter_t` pointers into a privately owned array before dropping the lock. This ensures the main thread iterates over a safe snapshot of the filter data, preventing the UAF if the original filters are freed in the background. --- scheduler/job.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/scheduler/job.c b/scheduler/job.c index 85c4d0462..5a6aedc33 100644 --- a/scheduler/job.c +++ b/scheduler/job.c @@ -700,6 +700,28 @@ cupsdContinueJob(cupsd_job_t *job) /* I - Job */ cupsRWUnlock(&MimeLock); } + /* + * Deep-copy the borrowed mime_filter_t pointers before dropping the + * lock: create_local_bg_thread() may concurrently call + * cupsdSetPrinterAttrs() -> delete_printer_filters() -> + * mimeDeleteFilter(), which free()s these objects while we are still + * holding raw pointers in 'filters'. + */ + if (filters) + { + cups_array_t *owned = cupsArrayNew3(NULL, NULL, NULL, 0, NULL, + (cups_afree_func_t)free); + for (filter = (mime_filter_t *)cupsArrayFirst(filters); + filter; + filter = (mime_filter_t *)cupsArrayNext(filters)) + { + mime_filter_t *copy = calloc(1, sizeof(mime_filter_t)); + if (copy) { *copy = *filter; cupsArrayAdd(owned, copy); } + } + cupsArrayDelete(filters); + filters = owned; + } + /* * Set a minimum cost of 100 for all jobs so that FilterLimit * works with raw queues and other low-cost paths.