[syzbot ci] Re: minix: convert to iomap and add direct I/O

5 views
Skip to first unread message

syzbot ci

unread,
1:27 AM (18 hours ago) 1:27 AM
to bra...@kernel.org, h...@infradead.org, ja...@suse.cz, jbin...@gmail.com, jkoo...@xs4all.nl, linux-...@vger.kernel.org, linux-...@vger.kernel.org, syzk...@googlegroups.com, vi...@zeniv.linux.org.uk, syz...@lists.linux.dev, syzkall...@googlegroups.com
syzbot ci has tested the following series

[v2] minix: convert to iomap and add direct I/O
https://lore.kernel.org/all/cover.17826197...@gmail.com
* [PATCH v2 1/4] minix: add iomap infrastructure
* [PATCH v2 2/4] minix: convert address space operations to iomap
* [PATCH v2 3/4] minix: convert file operations to iomap and add
* [PATCH v2 4/4] minix: fix symlink and truncate for iomap

and found the following issues:
* BUG: sleeping function called from invalid context in bdev_getblk
* BUG: sleeping function called from invalid context in find_get_block_common

Full report is available here:
https://ci.syzbot.org/series/3b663c96-cf07-46e0-9b8d-785fe615029d

***

BUG: sleeping function called from invalid context in bdev_getblk

tree: torvalds
URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
base: 780d569e6c4b422290f5cba319eb904b355d64be
arch: amd64
compiler: Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
config: https://ci.syzbot.org/builds/61f38821-60a2-4c7c-a39e-27291869d296/config
syz repro: https://ci.syzbot.org/findings/dda4e5c2-7cbc-498a-98ea-83cad3c680e3/syz_repro

option from the mount to silence this warning.
=======================================================
BUG: sleeping function called from invalid context at ./include/linux/sched/mm.h:323
in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 5865, name: syz.1.18
preempt_count: 0, expected: 0
RCU nest depth: 1, expected: 0
1 lock held by syz.1.18/5865:
#0: ffffffff8e959c20 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:300 [inline]
#0: ffffffff8e959c20 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:840 [inline]
#0: ffffffff8e959c20 (rcu_read_lock){....}-{1:3}, at: path_init+0x124/0x1330 fs/namei.c:2689
CPU: 1 UID: 0 PID: 5865 Comm: syz.1.18 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
__might_resched+0x378/0x4d0 kernel/sched/core.c:9197
might_alloc include/linux/sched/mm.h:323 [inline]
bdev_getblk+0xce/0x6e0 fs/buffer.c:1435
__bread_gfp+0x89/0x380 fs/buffer.c:1493
sb_bread include/linux/buffer_head.h:348 [inline]
minix_get_link+0x143/0x340 fs/minix/namei.c:396
pick_link+0x782/0xfe0 fs/namei.c:2064
step_into_slowpath+0x58a/0x820 fs/namei.c:2127
step_into fs/namei.c:2152 [inline]
open_last_lookups fs/namei.c:4643 [inline]
path_openat+0x224e/0x3830 fs/namei.c:4856
do_file_open+0x23e/0x4a0 fs/namei.c:4888
do_sys_openat2+0x115/0x200 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f9618b9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f9619a29028 EFLAGS: 00000246 ORIG_RAX: 0000000000000101
RAX: ffffffffffffffda RBX: 00007f9618e15fa0 RCX: 00007f9618b9ce59
RDX: 0000000000105042 RSI: 0000200000000080 RDI: ffffffffffffff9c
RBP: 00007f9618c32e6f R08: 0000000000000000 R09: 0000000000000000
R10: 00000000000001ff R11: 0000000000000246 R12: 0000000000000000
R13: 00007f9618e16038 R14: 00007f9618e15fa0 R15: 00007fffce8916b8
</TASK>


***

BUG: sleeping function called from invalid context in find_get_block_common

tree: torvalds
URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
base: 780d569e6c4b422290f5cba319eb904b355d64be
arch: amd64
compiler: Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
config: https://ci.syzbot.org/builds/61f38821-60a2-4c7c-a39e-27291869d296/config
syz repro: https://ci.syzbot.org/findings/2af8da3b-836a-479e-9af0-fef473c2f315/syz_repro

loop2: detected capacity change from 0 to 64
bad symlink on inode 4
BUG: sleeping function called from invalid context at ./include/linux/pagemap.h:1155
in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 5813, name: syz.2.19
preempt_count: 0, expected: 0
RCU nest depth: 1, expected: 0
1 lock held by syz.2.19/5813:
#0: ffffffff8e959c20 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:300 [inline]
#0: ffffffff8e959c20 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:840 [inline]
#0: ffffffff8e959c20 (rcu_read_lock){....}-{1:3}, at: path_init+0x124/0x1330 fs/namei.c:2689
CPU: 1 UID: 0 PID: 5813 Comm: syz.2.19 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
__might_resched+0x378/0x4d0 kernel/sched/core.c:9197
folio_lock include/linux/pagemap.h:1155 [inline]
__find_get_block_slow fs/buffer.c:242 [inline]
find_get_block_common+0x2d0/0xe10 fs/buffer.c:1386
bdev_getblk+0x58/0x6e0 include/linux/gfp.h:-1
__bread_gfp+0x89/0x380 fs/buffer.c:1493
sb_bread include/linux/buffer_head.h:348 [inline]
minix_get_link+0x143/0x340 fs/minix/namei.c:396
pick_link+0x782/0xfe0 fs/namei.c:2064
step_into_slowpath+0x58a/0x820 fs/namei.c:2127
step_into fs/namei.c:2152 [inline]
open_last_lookups fs/namei.c:4643 [inline]
path_openat+0x224e/0x3830 fs/namei.c:4856
do_file_open+0x23e/0x4a0 fs/namei.c:4888
do_sys_openat2+0x115/0x200 fs/open.c:1368
do_sys_open fs/open.c:1374 [inline]
__do_sys_openat fs/open.c:1390 [inline]
__se_sys_openat fs/open.c:1385 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1385
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fd37ef9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fd37fe06028 EFLAGS: 00000246 ORIG_RAX: 0000000000000101
RAX: ffffffffffffffda RBX: 00007fd37f215fa0 RCX: 00007fd37ef9ce59
RDX: 0000000000000042 RSI: 0000200000000080 RDI: ffffffffffffff9c
RBP: 00007fd37f032e6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fd37f216038 R14: 00007fd37f215fa0 R15: 00007ffe5e5f14f8
</TASK>
bad symlink on inode 4


***

If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
Tested-by: syz...@syzkaller.appspotmail.com

---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzk...@googlegroups.com.

To test a patch for this bug, please reply with `#syz test`
(should be on a separate line).

The patch should be attached to the email.
Note: arguments like custom git repos and branches are not supported.

Jeremy Bingham

unread,
2:28 AM (17 hours ago) 2:28 AM
to linux-...@vger.kernel.org, linux-...@vger.kernel.org, bra...@kernel.org, jkoo...@xs4all.nl, ja...@suse.cz, h...@infradead.org, vi...@zeniv.linux.org.uk, syzk...@googlegroups.com, Jeremy Bingham
This is v2 of the minix iomap conversion series. The original v1
submission (3 patches) was tested by syzbot, which found four issues.
Three were straightforward; the fourth, a null pointer dereference in
page_symlink when creating symlinks, required more substantial changes.

The original description follows:

This series converts the minix filesystem from the buffer_head-based
I/O path to the iomap API, and adds direct I/O support in the process.

The conversion is straightforward: minix's indirect block tree mapping
logic (from itree_common.c) is reworked into an iomap_begin/iomap_end
implementation. The iomap_end callback is a no-op since minix has no
extents or transactions to finalize.

Patch 1 adds the iomap infrastructure: the new iomap.c file, wrapper
functions and iomap_ops structs in itree_v1.c and itree_v2.c, and the
relevant declarations in minix.h.

The iomap.c file is #include'd into itree_v1.c and itree_v2.c rather
than compiled as a standalone translation unit. This is because the
minix filesystem versions (V1 vs V2/V3) have different block_t sizes
(16-bit vs 32-bit) and different indirect tree depths. This follows
the existing pattern in minix where itree_common.c is included into
both itree_v1.c and itree_v2.c. Each version provides a thin wrapper
and a corresponding iomap_ops struct.

Patch 2 converts the regular file address space operations to iomap:
read_folio, readahead, writepages (with a writeback callback), bmap,
and folio lifecycle helpers. Directory inodes continue to use
buffer_head-based operations via a new minix_dir_aops, since directory
handling still relies on buffer head chunks for prepare/write_begin.

Patch 3 converts the file_operations: replacing the generic read/write
iterators with iomap-aware versions, adding direct I/O read/write paths
using iomap_dio_rw, and setting FMODE_CAN_ODIRECT in the open handler.

The minix iomap implementation was adapted from the out-of-tree xiafs
iomap conversion. The xiafs module itself borrowed heavily from the
modernized minix kernel module. The exfat iomap changes were an
additional reference for both conversions.

Changes since v1:

* Added a fourth patch to fix the symlink and truncate issues:
- Replaced page_symlink with a custom __page_symlink that writes
the target directly to a data block via minix_new_block +
sb_getblk, bypassing the aops write path (which no longer has
write_begin/write_end). Added a matching custom minix_get_link
that reads the target from the data block via sb_bread, similar
to ext4_get_link. No iomap-based filesystem in the kernel uses
page_symlink; XFS, GFS2, and ext4 all handle symlink storage
directly. The on-disk format is unchanged.
- Fixed a buffer_head/iomap type confusion in truncate:
block_truncate_page attaches buffer_heads to data folios, but
minix_aops now uses iomap which interprets folio->private as
struct iomap_folio_state. truncate() now dispatches between
iomap_truncate_page (for regular files/symlinks) and
block_truncate_page (for directories) based on the inode's aops.
- Added .setattr = minix_setattr to minix_symlink_inode_operations
so symlinks truncate properly through the iomap path.

* Patch 1 (iomap infrastructure): minix_get_block is now exported
(non-static) so the directory aops and iomap writeback path can
use it. Added minix_iomap_ops_ver() inline helper and extern
declarations for minix_aops and the version-specific iomap_ops.
Fixed unsigned -> unsigned int in minix_blocks_needed and
minix_find_first_zero_bit to silence checkpatch warnings.

* Patch 2 (aops conversion): unchanged in approach; minor cleanup
of the writeback callback and minix_bmap conversion.

* Patch 3 (file operations): minix_setattr is now exported for reuse
by the symlink inode operations in patch 4.

Testing: the full series has been tested with mkfs.minix V1/V2/V3,
exercising file creation, read/write, overwrite, append, binary data,
directories, symlinks (full path, relative, directory symlinks), hard
links, truncation (shrink/grow), large files (1MB, exercising indirect
blocks), deep nesting (20 levels), 100 files in one directory,
deletions, remount persistence, and fsck.minix. All pass cleanly. The
four syzbot-reported issues are resolved.

Jeremy Bingham (4):
minix: add iomap infrastructure
minix: convert address space operations to iomap
minix: convert file operations to iomap and add direct I/O
minix: fix symlilnk and truncate for iomap compatibility

fs/minix/file.c | 157 ++++++++++++++++++++++++++++++++++++++--
fs/minix/inode.c | 90 ++++++++++++++++++++---
fs/minix/iomap.c | 114 +++++++++++++++++++++++++++++
fs/minix/itree_common.c | 11 ++-
fs/minix/itree_v1.c | 25 ++++++-
fs/minix/itree_v2.c | 17 ++++-
fs/minix/minix.h | 30 +++++++-
fs/minix/namei.c | 137 ++++++++++++++++++++++++++++++++++-
8 files changed, 558 insertions(+), 23 deletions(-)
create mode 100644 fs/minix/iomap.c

--
2.47.3

Jeremy Bingham

unread,
2:28 AM (17 hours ago) 2:28 AM
to linux-...@vger.kernel.org, linux-...@vger.kernel.org, bra...@kernel.org, jkoo...@xs4all.nl, ja...@suse.cz, h...@infradead.org, vi...@zeniv.linux.org.uk, syzk...@googlegroups.com, Jeremy Bingham
Convert minix regular file and symlink address space operations from
buffer_head to iomap. The new minix_aops uses iomap_dirty_folio,
iomap_invalidate_folio, iomap_bio_read_folio, iomap_bio_readahead,
iomap_writepages, iomap_bmap, and related iomap helpers.
The write_begin/write_end callbacks are removed since buffered writes
now go through iomap_file_buffered_write in file.c.

Directories keep using buffer_heads via a new minix_dir_aops, which
retains the old block_dirty_folio, block_read_full_folio,
block_write_begin, generic_write_end, and mpage_writepages. This is
necessary because directory entry manipulation (minix_prepare_chunk,
minix_write_begin) still uses the buffer_head chunk protocol.

minix_bmap is converted from generic_block_bmap to iomap_bmap.

The minix_get_block function is exported (non-static) so the
directory aops can still use it for block_write_begin and
mpage_writepages.

Signed-off-by: Jeremy Bingham <jbin...@gmail.com>
---
fs/minix/inode.c | 86 +++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 78 insertions(+), 8 deletions(-)

diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index c30cc590698d..2ba6766fce51 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -436,7 +436,32 @@ static int minix_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0;
}

-static int minix_get_block(struct inode *inode, sector_t block,
+static ssize_t minix_writeback_range(struct iomap_writepage_ctx *wpc,
+ struct folio *folio, u64 pos, unsigned int len, u64 end_pos)
+{
+ int error;
+
+ if (pos < wpc->iomap.offset ||
+ pos >= wpc->iomap.offset + wpc->iomap.length) {
+ if (INODE_VERSION(wpc->inode) == MINIX_V1)
+ error = V1_minix_iomap_begin(wpc->inode, pos, len, IOMAP_WRITE,
+ &wpc->iomap, NULL);
+ else
+ error = V2_minix_iomap_begin(wpc->inode, pos, len, IOMAP_WRITE,
+ &wpc->iomap, NULL);
+ if (error)
+ return error;
+ }
+
+ return iomap_add_to_ioend(wpc, folio, pos, end_pos, len);
+}
+
+static const struct iomap_writeback_ops minix_writeback_ops = {
+ .writeback_range = minix_writeback_range,
+ .writeback_submit = iomap_ioend_writeback_submit,
+};
+
+int minix_get_block(struct inode *inode, sector_t block,
struct buffer_head *bh_result, int create)
{
if (INODE_VERSION(inode) == MINIX_V1)
@@ -445,17 +470,45 @@ static int minix_get_block(struct inode *inode, sector_t block,
return V2_minix_get_block(inode, block, bh_result, create);
}

-static int minix_writepages(struct address_space *mapping,
+/* The old minix_writepages, preserved for directory operations. */
+static int minix_block_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
return mpage_writepages(mapping, wbc, minix_get_block);
}

+static int minix_writepages(struct address_space *mapping,
+ struct writeback_control *wbc)
+{
+ struct iomap_writepage_ctx wpc = {
+ .inode = mapping->host,
+ .wbc = wbc,
+ .ops = &minix_writeback_ops,
+ };
+ return iomap_writepages(&wpc);
+}
+
static int minix_read_folio(struct file *file, struct folio *folio)
+{
+ const struct iomap_ops *ops = minix_iomap_ops_ver(folio->mapping->host);
+
+ iomap_bio_read_folio(folio, ops);
+ return 0;
+}
+
+/* The old minix_read_folio, preserved for directory operations. */
+static int minix_block_read_folio(struct file *file, struct folio *folio)
{
return block_read_full_folio(folio, minix_get_block);
}

+static void minix_readahead(struct readahead_control *rac)
+{
+ const struct iomap_ops *ops = minix_iomap_ops_ver(rac->mapping->host);
+
+ iomap_bio_readahead(rac, ops);
+}
+
int minix_prepare_chunk(struct folio *folio, loff_t pos, unsigned len)
{
return __block_write_begin(folio, pos, len, minix_get_block);
@@ -487,19 +540,36 @@ static int minix_write_begin(const struct kiocb *iocb,

static sector_t minix_bmap(struct address_space *mapping, sector_t block)
{
- return generic_block_bmap(mapping,block,minix_get_block);
+ const struct iomap_ops *ops = minix_iomap_ops_ver(mapping->host);
+
+ return iomap_bmap(mapping, block, ops);
}

-static const struct address_space_operations minix_aops = {
- .dirty_folio = block_dirty_folio,
- .invalidate_folio = block_invalidate_folio,
+const struct address_space_operations minix_aops = {
+ .dirty_folio = iomap_dirty_folio,
+ .invalidate_folio = iomap_invalidate_folio,
.read_folio = minix_read_folio,
+ .readahead = minix_readahead,
.writepages = minix_writepages,
+ .migrate_folio = filemap_migrate_folio,
+ .bmap = minix_bmap,
+ .is_partially_uptodate = iomap_is_partially_uptodate,
+ .release_folio = iomap_release_folio,
+ .error_remove_folio = generic_error_remove_folio,
+};
+
+/* A special aops for directories that keeps using the buffer head chunks, at
+ * least for the time being.
+ */
+static const struct address_space_operations minix_dir_aops = {
+ .dirty_folio = block_dirty_folio,
+ .invalidate_folio = block_invalidate_folio,
+ .read_folio = minix_block_read_folio,
.write_begin = minix_write_begin,
.write_end = generic_write_end,
.migrate_folio = buffer_migrate_folio,
.bmap = minix_bmap,
- .direct_IO = noop_direct_IO
+ .writepages = minix_block_writepages,
};

static const struct inode_operations minix_symlink_inode_operations = {
@@ -516,7 +586,7 @@ void minix_set_inode(struct inode *inode, dev_t rdev)
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &minix_dir_inode_operations;
inode->i_fop = &minix_dir_operations;
- inode->i_mapping->a_ops = &minix_aops;
+ inode->i_mapping->a_ops = &minix_dir_aops;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &minix_symlink_inode_operations;
inode_nohighmem(inode);
--
2.47.3

Reply all
Reply to author
Forward
0 new messages