Skip to content

Commit 1d79d15

Browse files
author
Herbert Xu
committed
padata: Fix pd UAF once and for all
JIRA: https://issues.redhat.com/browse/RHEL-39495 Upstream Status: v6.17 commit 71203f6 Author: Herbert Xu <herbert@gondor.apana.org.au> Date: Sat May 24 20:32:20 2025 +0800 padata: Fix pd UAF once and for all There is a race condition/UAF in padata_reorder that goes back to the initial commit. A reference count is taken at the start of the process in padata_do_parallel, and released at the end in padata_serial_worker. This reference count is (and only is) required for padata_replace to function correctly. If padata_replace is never called then there is no issue. In the function padata_reorder which serves as the core of padata, as soon as padata is added to queue->serial.list, and the associated spin lock released, that padata may be processed and the reference count on pd would go away. Fix this by getting the next padata before the squeue->serial lock is released. In order to make this possible, simplify padata_reorder by only calling it once the next padata arrives. Fixes: 16295be ("padata: Generic parallelization/serialization interface") Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Herbert Xu <herbert.xu@redhat.com>
1 parent db9ccfa commit 1d79d15

File tree

2 files changed

+37
-98
lines changed

2 files changed

+37
-98
lines changed

include/linux/padata.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ struct padata_cpumask {
9191
* @cpu: Next CPU to be processed.
9292
* @cpumask: The cpumasks in use for parallel and serial workers.
9393
* @reorder_work: work struct for reordering.
94-
* @lock: Reorder lock.
9594
*/
9695
struct parallel_data {
9796
struct padata_shell *ps;
@@ -102,8 +101,6 @@ struct parallel_data {
102101
unsigned int processed;
103102
int cpu;
104103
struct padata_cpumask cpumask;
105-
struct work_struct reorder_work;
106-
spinlock_t ____cacheline_aligned lock;
107104
};
108105

109106
/**

kernel/padata.c

Lines changed: 37 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -261,118 +261,70 @@ EXPORT_SYMBOL(padata_do_parallel);
261261
* be parallel processed by another cpu and is not yet present in
262262
* the cpu's reorder queue.
263263
*/
264-
static struct padata_priv *padata_find_next(struct parallel_data *pd,
265-
bool remove_object)
264+
static struct padata_priv *padata_find_next(struct parallel_data *pd, int cpu,
265+
unsigned int processed)
266266
{
267267
struct padata_priv *padata;
268268
struct padata_list *reorder;
269-
int cpu = pd->cpu;
270269

271270
reorder = per_cpu_ptr(pd->reorder_list, cpu);
272271

273272
spin_lock(&reorder->lock);
274-
if (list_empty(&reorder->list)) {
275-
spin_unlock(&reorder->lock);
276-
return NULL;
277-
}
273+
if (list_empty(&reorder->list))
274+
goto notfound;
278275

279276
padata = list_entry(reorder->list.next, struct padata_priv, list);
280277

281278
/*
282279
* Checks the rare case where two or more parallel jobs have hashed to
283280
* the same CPU and one of the later ones finishes first.
284281
*/
285-
if (padata->seq_nr != pd->processed) {
286-
spin_unlock(&reorder->lock);
287-
return NULL;
288-
}
289-
290-
if (remove_object) {
291-
list_del_init(&padata->list);
292-
++pd->processed;
293-
pd->cpu = cpumask_next_wrap(cpu, pd->cpumask.pcpu, -1, false);
294-
}
282+
if (padata->seq_nr != processed)
283+
goto notfound;
295284

285+
list_del_init(&padata->list);
296286
spin_unlock(&reorder->lock);
297287
return padata;
288+
289+
notfound:
290+
pd->processed = processed;
291+
pd->cpu = cpu;
292+
spin_unlock(&reorder->lock);
293+
return NULL;
298294
}
299295

300-
static void padata_reorder(struct parallel_data *pd)
296+
static void padata_reorder(struct padata_priv *padata)
301297
{
298+
struct parallel_data *pd = padata->pd;
302299
struct padata_instance *pinst = pd->ps->pinst;
303-
int cb_cpu;
304-
struct padata_priv *padata;
305-
struct padata_serial_queue *squeue;
306-
struct padata_list *reorder;
300+
unsigned int processed;
301+
int cpu;
307302

308-
/*
309-
* We need to ensure that only one cpu can work on dequeueing of
310-
* the reorder queue the time. Calculating in which percpu reorder
311-
* queue the next object will arrive takes some time. A spinlock
312-
* would be highly contended. Also it is not clear in which order
313-
* the objects arrive to the reorder queues. So a cpu could wait to
314-
* get the lock just to notice that there is nothing to do at the
315-
* moment. Therefore we use a trylock and let the holder of the lock
316-
* care for all the objects enqueued during the holdtime of the lock.
317-
*/
318-
if (!spin_trylock_bh(&pd->lock))
319-
return;
303+
processed = pd->processed;
304+
cpu = pd->cpu;
320305

321-
while (1) {
322-
padata = padata_find_next(pd, true);
306+
do {
307+
struct padata_serial_queue *squeue;
308+
int cb_cpu;
323309

324-
/*
325-
* If the next object that needs serialization is parallel
326-
* processed by another cpu and is still on it's way to the
327-
* cpu's reorder queue, nothing to do for now.
328-
*/
329-
if (!padata)
330-
break;
310+
cpu = cpumask_next_wrap(cpu, pd->cpumask.pcpu, -1, false);
311+
processed++;
331312

332313
cb_cpu = padata->cb_cpu;
333314
squeue = per_cpu_ptr(pd->squeue, cb_cpu);
334315

335316
spin_lock(&squeue->serial.lock);
336317
list_add_tail(&padata->list, &squeue->serial.list);
337-
spin_unlock(&squeue->serial.lock);
338-
339318
queue_work_on(cb_cpu, pinst->serial_wq, &squeue->work);
340-
}
341319

342-
spin_unlock_bh(&pd->lock);
343-
344-
/*
345-
* The next object that needs serialization might have arrived to
346-
* the reorder queues in the meantime.
347-
*
348-
* Ensure reorder queue is read after pd->lock is dropped so we see
349-
* new objects from another task in padata_do_serial. Pairs with
350-
* smp_mb in padata_do_serial.
351-
*/
352-
smp_mb();
353-
354-
reorder = per_cpu_ptr(pd->reorder_list, pd->cpu);
355-
if (!list_empty(&reorder->list) && padata_find_next(pd, false)) {
356320
/*
357-
* Other context(eg. the padata_serial_worker) can finish the request.
358-
* To avoid UAF issue, add pd ref here, and put pd ref after reorder_work finish.
321+
* If the next object that needs serialization is parallel
322+
* processed by another cpu and is still on it's way to the
323+
* cpu's reorder queue, end the loop.
359324
*/
360-
padata_get_pd(pd);
361-
if (!queue_work(pinst->serial_wq, &pd->reorder_work))
362-
padata_put_pd(pd);
363-
}
364-
}
365-
366-
static void invoke_padata_reorder(struct work_struct *work)
367-
{
368-
struct parallel_data *pd;
369-
370-
local_bh_disable();
371-
pd = container_of(work, struct parallel_data, reorder_work);
372-
padata_reorder(pd);
373-
local_bh_enable();
374-
/* Pairs with putting the reorder_work in the serial_wq */
375-
padata_put_pd(pd);
325+
padata = padata_find_next(pd, cpu, processed);
326+
spin_unlock(&squeue->serial.lock);
327+
} while (padata);
376328
}
377329

378330
static void padata_serial_worker(struct work_struct *serial_work)
@@ -423,6 +375,7 @@ void padata_do_serial(struct padata_priv *padata)
423375
struct padata_list *reorder = per_cpu_ptr(pd->reorder_list, hashed_cpu);
424376
struct padata_priv *cur;
425377
struct list_head *pos;
378+
bool gotit = true;
426379

427380
spin_lock(&reorder->lock);
428381
/* Sort in ascending order of sequence number. */
@@ -432,17 +385,14 @@ void padata_do_serial(struct padata_priv *padata)
432385
if ((signed int)(cur->seq_nr - padata->seq_nr) < 0)
433386
break;
434387
}
435-
list_add(&padata->list, pos);
388+
if (padata->seq_nr != pd->processed) {
389+
gotit = false;
390+
list_add(&padata->list, pos);
391+
}
436392
spin_unlock(&reorder->lock);
437393

438-
/*
439-
* Ensure the addition to the reorder list is ordered correctly
440-
* with the trylock of pd->lock in padata_reorder. Pairs with smp_mb
441-
* in padata_reorder.
442-
*/
443-
smp_mb();
444-
445-
padata_reorder(pd);
394+
if (gotit)
395+
padata_reorder(padata);
446396
}
447397
EXPORT_SYMBOL(padata_do_serial);
448398

@@ -632,9 +582,7 @@ static struct parallel_data *padata_alloc_pd(struct padata_shell *ps)
632582
padata_init_squeues(pd);
633583
pd->seq_nr = -1;
634584
refcount_set(&pd->refcnt, 1);
635-
spin_lock_init(&pd->lock);
636585
pd->cpu = cpumask_first(pd->cpumask.pcpu);
637-
INIT_WORK(&pd->reorder_work, invoke_padata_reorder);
638586

639587
return pd;
640588

@@ -1144,12 +1092,6 @@ void padata_free_shell(struct padata_shell *ps)
11441092
if (!ps)
11451093
return;
11461094

1147-
/*
1148-
* Wait for all _do_serial calls to finish to avoid touching
1149-
* freed pd's and ps's.
1150-
*/
1151-
synchronize_rcu();
1152-
11531095
mutex_lock(&ps->pinst->lock);
11541096
list_del(&ps->list);
11551097
pd = rcu_dereference_protected(ps->pd, 1);

0 commit comments

Comments
 (0)