Forwarded: [PATCH] BUG: sleeping function called from invalid context in null_insert_page

From: syzbot

Date: Mon Sep 14 2026 - 06:27:21 EST


For archival purposes, forwarding an incoming command email to
linux-kernel@xxxxxxxxxxxxxxx.

***

Subject: [PATCH] BUG: sleeping function called from invalid context in null_insert_page
Author: jchuang26@xxxxxxxxxxxxxx

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

Reported-by: syzbot+95fdab36405e5ffdb680@xxxxxxxxxxxxxxxxxxxxxxxxx

diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index 6beb1f5b7..7f3fbac0f 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -70,6 +70,7 @@ static DEFINE_MUTEX(lock);
static int null_major;
static DEFINE_IDA(nullb_indexes);
static struct blk_mq_tag_set tag_set;
+static struct blk_mq_tag_set blocking_tag_set;

enum {
NULL_IRQ_NONE = 0,
@@ -1847,34 +1848,44 @@ static int null_init_tag_set(struct blk_mq_tag_set *set, int poll_queues)
return blk_mq_alloc_tag_set(set);
}

-static int null_init_global_tag_set(void)
+static int null_init_global_tag_set(struct blk_mq_tag_set *set, bool blocking)
{
int error;

- if (tag_set.ops)
+ if (set->ops)
return 0;

- tag_set.nr_hw_queues = g_submit_queues;
- tag_set.queue_depth = g_hw_queue_depth;
- tag_set.numa_node = g_home_node;
+ set->nr_hw_queues = g_submit_queues;
+ set->queue_depth = g_hw_queue_depth;
+ set->numa_node = g_home_node;
if (g_no_sched)
- tag_set.flags |= BLK_MQ_F_NO_SCHED_BY_DEFAULT;
+ set->flags |= BLK_MQ_F_NO_SCHED_BY_DEFAULT;
if (g_shared_tag_bitmap)
- tag_set.flags |= BLK_MQ_F_TAG_HCTX_SHARED;
- if (g_blocking)
- tag_set.flags |= BLK_MQ_F_BLOCKING;
+ set->flags |= BLK_MQ_F_TAG_HCTX_SHARED;
+ if (blocking)
+ set->flags |= BLK_MQ_F_BLOCKING;

- error = null_init_tag_set(&tag_set, g_poll_queues);
+ error = null_init_tag_set(set, g_poll_queues);
if (error)
- tag_set.ops = NULL;
+ set->ops = NULL;
return error;
}

static int null_setup_tagset(struct nullb *nullb)
{
if (nullb->dev->shared_tags) {
- nullb->tag_set = &tag_set;
- return null_init_global_tag_set();
+ bool blocking = g_blocking || nullb->dev->blocking;
+
+ /*
+ * Some memory-backed devices can sleep in their request
+ * submission path, which is invalid if the tag set is
+ * non-blocking since the block layer then calls the driver
+ * under rcu_read_lock(). The blocking flag is fixed when a tag
+ * set is allocated, so use a dedicated blocking global tag set
+ * for such devices.
+ */
+ nullb->tag_set = blocking ? &blocking_tag_set : &tag_set;
+ return null_init_global_tag_set(nullb->tag_set, blocking);
}

nullb->tag_set = &nullb->__tag_set;
@@ -2195,6 +2206,8 @@ static int __init null_init(void)
unregister_blkdev(null_major, "nullb");
if (tag_set.ops)
blk_mq_free_tag_set(&tag_set);
+ if (blocking_tag_set.ops)
+ blk_mq_free_tag_set(&blocking_tag_set);
return ret;
}

@@ -2215,6 +2228,8 @@ static void __exit null_exit(void)

if (tag_set.ops)
blk_mq_free_tag_set(&tag_set);
+ if (blocking_tag_set.ops)
+ blk_mq_free_tag_set(&blocking_tag_set);
}

module_init(null_init);