Re: [PATCH] loop: Fix NULL pointer dereference by synchronizing lo_release and loop_queue_rq

From: Tetsuo Handa

Date: Tue May 12 2026 - 07:48:13 EST


Commit 99ebc509eef5 ("mm: memcontrol: fix rcu unbalance in get_non_dying_memcg_end()") fixed
RCU imbalance bug, and it seems that so far only trees without that commit is reproducing RCU
imbalance bug.

But since this NULL pointer dereference bug was reproduced in linux-next-20260508 which already
includes that commit, I suspect that some change in the block layer broke protection by RCU
synchronization or flushing of lo->workqueue.

Can you check that there was no commit that might break protection by RCU synchronization or
flushing of lo->workqueue?

On 2026/05/12 2:43, Tetsuo Handa wrote:
> Thank you for responding.
>
> Given it is protected by RCU, this might be yet another manifestation of
> "is trying to release lock (rcu_read_lock) at:" + "but there are no more locks to release!"-type of
> hidden RCU imbalance bugs recently introduced? Andrew, was any progress made for RCU imbalance bugs?
>
> On 2026/04/21 20:05, Tetsuo Handa wrote:
>> Since this problem started after the merge window opened, the culprit commit
>> might be in between commit a028739a4330 ("Merge tag 'block-7.0-20260305' of
>> git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux") and
>> commit 7fe6ac157b7e ("Merge tag 'for-7.1/block-20260411' of
>> git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux").
>> Also, syzbot was not testing changes in linux-next since next-20260403,
>> and found this problem in next-20260413.
>
> On 2026/05/12 0:58, Bart Van Assche wrote:
>> Why SRCU instead of RCU? The loop driver doesn't set BLK_MQ_F_BLOCKING
>> and hence must not sleep inside loop_queue_rq(). Additionally, the block
>> layer already holds an RCU lock around all loop_queue_rq() calls. From
>> block/blk-mq.h:
>>
>> /* run the code block in @dispatch_ops with rcu/srcu read lock held */
>> #define __blk_mq_run_dispatch_ops(q, check_sleep, dispatch_ops)    \
>> do {                                \
>>     if ((q)->tag_set->flags & BLK_MQ_F_BLOCKING) {        \
>>         struct blk_mq_tag_set *__tag_set = (q)->tag_set; \
>>         int srcu_idx;                    \
>>                                 \
>>         might_sleep_if(check_sleep);            \
>>         srcu_idx = srcu_read_lock(__tag_set->srcu);    \
>>         (dispatch_ops);                    \
>>         srcu_read_unlock(__tag_set->srcu, srcu_idx);    \
>>     } else {                        \
>>         rcu_read_lock();                \
>>         (dispatch_ops);                    \
>>         rcu_read_unlock();                \
>>     }                            \
>> } while (0)