* RFC PATCH] null_blk: fix UBSAN shift-out-of-bounds when zone_size is 0 or overflows
@ 2026-08-08 15:42 Rik van Riel
2026-08-08 15:56 ` Damien Le Moal
0 siblings, 1 reply; 2+ messages in thread
From: Rik van Riel @ 2026-08-08 15:42 UTC (permalink / raw)
To: Jens Axboe; +Cc: Keith Busch, Damien Le Moal, linux-block, linux-kernel
null_zone_no() does sect >> ilog2(dev->zone_size_sects). When
zone_size_sects is 0, ilog2(0) returns -1, producing shift exponent -1
which UBSAN reports as shift-out-of-bounds.
UBSAN: shift-out-of-bounds in drivers/block/null_blk/zoned.c:21:14
shift exponent -1 is negative
Call Trace:
null_zone_no drivers/block/null_blk/zoned.c:21 [inline]
null_process_zoned_cmd+0xf76/0xf80 drivers/block/null_blk/zoned.c:728
null_handle_cmd drivers/block/null_blk/main.c:1455 [inline]
null_queue_rq+0x8bc/0xe70 drivers/block/null_blk/main.c:1703
__blk_mq_issue_directly block/blk-mq.c:2694 [inline]
blk_mq_try_issue_directly+0x3f4/0x880 block/blk-mq.c:2754
blk_mq_submit_bio+0x20c0/0x2a40 block/blk-mq.c:3208
submit_bio_noacct_nocheck+0x2f4/0xa40 block/blk-core.c:790
block_read_full_folio+0x7a6/0x810 fs/buffer.c:2463
filemap_read_folio+0x12c/0x3a0 mm/filemap.c:2510
read_part_sector+0xb6/0x2b0 block/partitions/core.c:724
adfspart_check_ICS+0xb1/0x960 block/partitions/acorn.c:357
check_partition block/partitions/core.c:143 [inline]
blk_add_partitions block/partitions/core.c:591 [inline]
bdev_disk_changed+0x851/0x17a0 block/partitions/core.c:695
blkdev_get_whole+0x372/0x510 block/bdev.c:751
add_disk_final block/genhd.c:412 [inline]
add_disk_fwnode+0x24b/0x3a0 block/genhd.c:606
null_add_dev+0x130b/0x1d70 drivers/block/null_blk/main.c:2052
nullb_device_power_store+0x240/0x380 drivers/block/null_blk/main.c:501
configfs_write_iter+0x337/0x430 fs/configfs/file.c:229
Syzkaller triggers this by creating a zoned null_blk device via
configfs. The Call Trace shows configfs_write_iter in configfs/file.c
handling a write to power file, which calls nullb_device_power_store in
main.c, which calls null_add_dev in main.c, which calls add_disk in
genhd.c, which triggers partition scan via bdev_disk_changed in
partitions/core.c.
A zoned null_blk device with zone_size 0 should not be legal. Existing
code tries to reject it via is_power_of_2() check in zoned.c and
!zone_size check in main.c, but syzkaller can still reach
null_zone_no() with zone_size_sects 0 via two paths:
1. Direct 0 via configfs: zone_size attribute store in main.c has
NULLB_DEVICE_ATTR(zone_size, ulong, NULL) with no validation callback,
so echo 0 > zone_size succeeds before power store. If zoned is false
at power store time, the !zone_size check in main.c is skipped, and
later zoned set true leaves zone_size 0.
2. Large value overflow: mb_to_sects() in zoned.c does
(sector_t)mb * SZ_1M >> SECTOR_SHIFT which is mb * 2048. If mb is
1UL << 53 (9PB), mb * 2048 overflows 64-bit to 0. The value is
power-of-two so is_power_of_2() passes, but mb_to_sects() returns 0.
Check for zero zone_size explicitly in null_init_zoned_dev() in
zoned.c, returning -EINVAL with "must be non-zero power-of-two".
Check for zero zone_size_sects after mb_to_sects() conversion,
returning -EINVAL for overflow case. Keep defensive check in
null_zone_no() returning 0 for zero sectors to avoid shift out-of-bounds
even if zero slips through.
This change should be safe because zone_size is set once in
null_init_zoned_dev() under device lock and never changes after, and 0
is never valid for a zoned device. Returning -EINVAL at init time fails
device creation early with clear error, while defensive return 0 in
null_zone_no() makes zoned command fail via offline zone check.
No new locking is introduced.
Reported-by: syzbot+abd6a8dca0f2b7726060@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=abd6a8dca0f2b7726060
Link: https://lore.kernel.org/all/6a75205c.01d0871a.3a0d52.0033.GAE@google.com/
Fixes: 8a3cf049af68 ("null_blk: add zoned block device emulation")
Cc: stable@vger.kernel.org
Assisted-by: Hermes:muse-spark-1.2 syzkaller
Signed-off-by: Rik van Riel <riel@surriel.com>
---
drivers/block/null_blk/zoned.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/block/null_blk/zoned.c b/drivers/block/null_blk/zoned.c
index 384bdce6a9b7..7e9b5ffb9e84 100644
--- a/drivers/block/null_blk/zoned.c
+++ b/drivers/block/null_blk/zoned.c
@@ -18,6 +18,8 @@ static inline sector_t mb_to_sects(unsigned long mb)
static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect)
{
+ if (WARN_ON_ONCE(!dev->zone_size_sects))
+ return 0;
return sect >> ilog2(dev->zone_size_sects);
}
@@ -56,8 +58,8 @@ int null_init_zoned_dev(struct nullb_device *dev,
sector_t sector = 0;
unsigned int i;
- if (!is_power_of_2(dev->zone_size)) {
- pr_err("zone_size must be power-of-two\n");
+ if (!dev->zone_size || !is_power_of_2(dev->zone_size)) {
+ pr_err("zone_size must be non-zero power-of-two\n");
return -EINVAL;
}
if (dev->zone_size > dev->size) {
@@ -88,6 +90,10 @@ int null_init_zoned_dev(struct nullb_device *dev,
zone_capacity_sects = mb_to_sects(dev->zone_capacity);
dev_capacity_sects = mb_to_sects(dev->size);
dev->zone_size_sects = mb_to_sects(dev->zone_size);
+ if (!dev->zone_size_sects) {
+ pr_err("zone_size too large or too small, leads to zero sectors\n");
+ return -EINVAL;
+ }
dev->nr_zones = round_up(dev_capacity_sects, dev->zone_size_sects)
>> ilog2(dev->zone_size_sects);
--
2.55.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: RFC PATCH] null_blk: fix UBSAN shift-out-of-bounds when zone_size is 0 or overflows
2026-08-08 15:42 RFC PATCH] null_blk: fix UBSAN shift-out-of-bounds when zone_size is 0 or overflows Rik van Riel
@ 2026-08-08 15:56 ` Damien Le Moal
0 siblings, 0 replies; 2+ messages in thread
From: Damien Le Moal @ 2026-08-08 15:56 UTC (permalink / raw)
To: Rik van Riel, Jens Axboe; +Cc: Keith Busch, linux-block, linux-kernel
On 2026/08/08 8:42, Rik van Riel wrote:
> null_zone_no() does sect >> ilog2(dev->zone_size_sects). When
> zone_size_sects is 0, ilog2(0) returns -1, producing shift exponent -1
> which UBSAN reports as shift-out-of-bounds.
>
> UBSAN: shift-out-of-bounds in drivers/block/null_blk/zoned.c:21:14
> shift exponent -1 is negative
> Call Trace:
> null_zone_no drivers/block/null_blk/zoned.c:21 [inline]
> null_process_zoned_cmd+0xf76/0xf80 drivers/block/null_blk/zoned.c:728
> null_handle_cmd drivers/block/null_blk/main.c:1455 [inline]
> null_queue_rq+0x8bc/0xe70 drivers/block/null_blk/main.c:1703
> __blk_mq_issue_directly block/blk-mq.c:2694 [inline]
> blk_mq_try_issue_directly+0x3f4/0x880 block/blk-mq.c:2754
> blk_mq_submit_bio+0x20c0/0x2a40 block/blk-mq.c:3208
> submit_bio_noacct_nocheck+0x2f4/0xa40 block/blk-core.c:790
> block_read_full_folio+0x7a6/0x810 fs/buffer.c:2463
> filemap_read_folio+0x12c/0x3a0 mm/filemap.c:2510
> read_part_sector+0xb6/0x2b0 block/partitions/core.c:724
> adfspart_check_ICS+0xb1/0x960 block/partitions/acorn.c:357
> check_partition block/partitions/core.c:143 [inline]
> blk_add_partitions block/partitions/core.c:591 [inline]
> bdev_disk_changed+0x851/0x17a0 block/partitions/core.c:695
> blkdev_get_whole+0x372/0x510 block/bdev.c:751
> add_disk_final block/genhd.c:412 [inline]
> add_disk_fwnode+0x24b/0x3a0 block/genhd.c:606
> null_add_dev+0x130b/0x1d70 drivers/block/null_blk/main.c:2052
> nullb_device_power_store+0x240/0x380 drivers/block/null_blk/main.c:501
> configfs_write_iter+0x337/0x430 fs/configfs/file.c:229
>
> Syzkaller triggers this by creating a zoned null_blk device via
> configfs. The Call Trace shows configfs_write_iter in configfs/file.c
> handling a write to power file, which calls nullb_device_power_store in
> main.c, which calls null_add_dev in main.c, which calls add_disk in
> genhd.c, which triggers partition scan via bdev_disk_changed in
> partitions/core.c.
>
> A zoned null_blk device with zone_size 0 should not be legal. Existing
> code tries to reject it via is_power_of_2() check in zoned.c and
> !zone_size check in main.c, but syzkaller can still reach
> null_zone_no() with zone_size_sects 0 via two paths:
>
> 1. Direct 0 via configfs: zone_size attribute store in main.c has
> NULLB_DEVICE_ATTR(zone_size, ulong, NULL) with no validation callback,
> so echo 0 > zone_size succeeds before power store. If zoned is false
> at power store time, the !zone_size check in main.c is skipped, and
> later zoned set true leaves zone_size 0.
>
> 2. Large value overflow: mb_to_sects() in zoned.c does
> (sector_t)mb * SZ_1M >> SECTOR_SHIFT which is mb * 2048. If mb is
> 1UL << 53 (9PB), mb * 2048 overflows 64-bit to 0. The value is
> power-of-two so is_power_of_2() passes, but mb_to_sects() returns 0.
>
> Check for zero zone_size explicitly in null_init_zoned_dev() in
> zoned.c, returning -EINVAL with "must be non-zero power-of-two".
> Check for zero zone_size_sects after mb_to_sects() conversion,
> returning -EINVAL for overflow case. Keep defensive check in
> null_zone_no() returning 0 for zero sectors to avoid shift out-of-bounds
> even if zero slips through.
>
> This change should be safe because zone_size is set once in
> null_init_zoned_dev() under device lock and never changes after, and 0
> is never valid for a zoned device. Returning -EINVAL at init time fails
> device creation early with clear error, while defensive return 0 in
> null_zone_no() makes zoned command fail via offline zone check.
> No new locking is introduced.
>
> Reported-by: syzbot+abd6a8dca0f2b7726060@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=abd6a8dca0f2b7726060
> Link: https://lore.kernel.org/all/6a75205c.01d0871a.3a0d52.0033.GAE@google.com/
> Fixes: 8a3cf049af68 ("null_blk: add zoned block device emulation")
> Cc: stable@vger.kernel.org
> Assisted-by: Hermes:muse-spark-1.2 syzkaller
> Signed-off-by: Rik van Riel <riel@surriel.com>
Looks good.
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-08 15:56 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-08 15:42 RFC PATCH] null_blk: fix UBSAN shift-out-of-bounds when zone_size is 0 or overflows Rik van Riel
2026-08-08 15:56 ` Damien Le Moal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox