@@ -47,6 +47,22 @@ struct padata_mt_job_state {
4747static void padata_free_pd (struct parallel_data * pd );
4848static void __init padata_mt_helper (struct work_struct * work );
4949
50+ static inline void padata_get_pd (struct parallel_data * pd )
51+ {
52+ refcount_inc (& pd -> refcnt );
53+ }
54+
55+ static inline void padata_put_pd_cnt (struct parallel_data * pd , int cnt )
56+ {
57+ if (refcount_sub_and_test (cnt , & pd -> refcnt ))
58+ padata_free_pd (pd );
59+ }
60+
61+ static inline void padata_put_pd (struct parallel_data * pd )
62+ {
63+ padata_put_pd_cnt (pd , 1 );
64+ }
65+
5066static int padata_index_to_cpu (struct parallel_data * pd , int cpu_index )
5167{
5268 int cpu , target_cpu ;
@@ -206,7 +222,7 @@ int padata_do_parallel(struct padata_shell *ps,
206222 if ((pinst -> flags & PADATA_RESET ))
207223 goto out ;
208224
209- refcount_inc ( & pd -> refcnt );
225+ padata_get_pd ( pd );
210226 padata -> pd = pd ;
211227 padata -> cb_cpu = * cb_cpu ;
212228
@@ -336,8 +352,14 @@ static void padata_reorder(struct parallel_data *pd)
336352 smp_mb ();
337353
338354 reorder = per_cpu_ptr (pd -> reorder_list , pd -> cpu );
339- if (!list_empty (& reorder -> list ) && padata_find_next (pd , false))
355+ if (!list_empty (& reorder -> list ) && padata_find_next (pd , false)) {
356+ /*
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.
359+ */
360+ padata_get_pd (pd );
340361 queue_work (pinst -> serial_wq , & pd -> reorder_work );
362+ }
341363}
342364
343365static void invoke_padata_reorder (struct work_struct * work )
@@ -348,6 +370,8 @@ static void invoke_padata_reorder(struct work_struct *work)
348370 pd = container_of (work , struct parallel_data , reorder_work );
349371 padata_reorder (pd );
350372 local_bh_enable ();
373+ /* Pairs with putting the reorder_work in the serial_wq */
374+ padata_put_pd (pd );
351375}
352376
353377static void padata_serial_worker (struct work_struct * serial_work )
@@ -380,8 +404,7 @@ static void padata_serial_worker(struct work_struct *serial_work)
380404 }
381405 local_bh_enable ();
382406
383- if (refcount_sub_and_test (cnt , & pd -> refcnt ))
384- padata_free_pd (pd );
407+ padata_put_pd_cnt (pd , cnt );
385408}
386409
387410/**
@@ -521,13 +544,6 @@ void __init padata_do_multithreaded(struct padata_mt_job *job)
521544 ps .chunk_size = max (ps .chunk_size , 1ul );
522545 ps .chunk_size = roundup (ps .chunk_size , job -> align );
523546
524- /*
525- * chunk_size can be 0 if the caller sets min_chunk to 0. So force it
526- * to at least 1 to prevent divide-by-0 panic in padata_mt_helper().`
527- */
528- if (!ps .chunk_size )
529- ps .chunk_size = 1U ;
530-
531547 list_for_each_entry (pw , & works , pw_list )
532548 if (job -> numa_aware ) {
533549 int old_node = atomic_read (& last_used_nid );
@@ -688,8 +704,7 @@ static int padata_replace(struct padata_instance *pinst)
688704 synchronize_rcu ();
689705
690706 list_for_each_entry_continue_reverse (ps , & pinst -> pslist , list )
691- if (refcount_dec_and_test (& ps -> opd -> refcnt ))
692- padata_free_pd (ps -> opd );
707+ padata_put_pd (ps -> opd );
693708
694709 pinst -> flags &= ~PADATA_RESET ;
695710
@@ -977,7 +992,7 @@ static ssize_t padata_sysfs_store(struct kobject *kobj, struct attribute *attr,
977992
978993 pinst = kobj2pinst (kobj );
979994 pentry = attr2pentry (attr );
980- if (pentry -> show )
995+ if (pentry -> store )
981996 ret = pentry -> store (pinst , attr , buf , count );
982997
983998 return ret ;
@@ -1128,11 +1143,16 @@ void padata_free_shell(struct padata_shell *ps)
11281143 if (!ps )
11291144 return ;
11301145
1146+ /*
1147+ * Wait for all _do_serial calls to finish to avoid touching
1148+ * freed pd's and ps's.
1149+ */
1150+ synchronize_rcu ();
1151+
11311152 mutex_lock (& ps -> pinst -> lock );
11321153 list_del (& ps -> list );
11331154 pd = rcu_dereference_protected (ps -> pd , 1 );
1134- if (refcount_dec_and_test (& pd -> refcnt ))
1135- padata_free_pd (pd );
1155+ padata_put_pd (pd );
11361156 mutex_unlock (& ps -> pinst -> lock );
11371157
11381158 kfree (ps );
0 commit comments