#include #include #include #include #include "/home/user/linux-7.0/fs/erofs/internal.h" MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Trigger erofs ishare uninit variable bug"); static char *target = "/mnt/erofs1/file.bin"; module_param(target, charp, 0444); static int __init trigger_init(void) { struct path p; struct inode *inode; struct erofs_inode *vi, *tmp; int ret, cnt; pr_err("TRIGGER: target=%s\n", target); ret = kern_path(target, LOOKUP_FOLLOW, &p); if (ret) { pr_err("TRIGGER: kern_path: %d\n", ret); return ret; } inode = p.dentry->d_inode; vi = EROFS_I(inode); pr_err("TRIGGER: ino=%lu mapping=%px i_data=%px redir=%d\n", inode->i_ino, inode->i_mapping, &inode->i_data, inode->i_mapping != &inode->i_data); pr_err("TRIGGER: ishare_list empty=%d sb_type=%s\n", list_empty(&vi->ishare_list), inode->i_sb->s_type->name); /* Walk the ishare_list from this inode's perspective */ cnt = 0; list_for_each_entry(tmp, &vi->ishare_list, ishare_list) { struct inode *ti = &tmp->vfs_inode; pr_err("TRIGGER: peer[%d] ino=%lu sb=%s mapping=%px i_data=%px redir=%d\n", cnt, ti->i_ino, ti->i_sb->s_type->name, ti->i_mapping, &ti->i_data, ti->i_mapping != &ti->i_data); cnt++; if (cnt > 20) break; } pr_err("TRIGGER: %d peers in list\n", cnt); /* * If any peer has redirected mapping, use its mapping->host * as the shared inode. Or if a peer is on erofs_anon_fs_type, * that IS the shared inode. */ cnt = 0; list_for_each_entry(tmp, &vi->ishare_list, ishare_list) { struct inode *ti = &tmp->vfs_inode; if (ti->i_sb->s_type == &erofs_anon_fs_type) { struct erofs_inode *vis = tmp; struct erofs_inode *real_iter; int rcnt = 0; pr_err("TRIGGER: FOUND shared inode! ino=%lu\n", ti->i_ino); /* Count real inodes in the shared inode's list */ spin_lock(&vis->ishare_lock); list_for_each_entry(real_iter, &vis->ishare_list, ishare_list) { pr_err("TRIGGER: real[%d] ino=%lu state=0x%lx cnt=%d\n", rcnt, real_iter->vfs_inode.i_ino, (unsigned long)inode_state_read(&real_iter->vfs_inode), atomic_read(&real_iter->vfs_inode.i_count)); rcnt++; } spin_unlock(&vis->ishare_lock); pr_err("TRIGGER: %d real inodes. Setting I_FREEING...\n", rcnt); /* Set I_FREEING on all real inodes */ spin_lock(&vis->ishare_lock); list_for_each_entry(real_iter, &vis->ishare_list, ishare_list) { spin_lock(&real_iter->vfs_inode.i_lock); inode_state_set(&real_iter->vfs_inode, I_FREEING); spin_unlock(&real_iter->vfs_inode.i_lock); } spin_unlock(&vis->ishare_lock); /* Test igrab — same as erofs_real_inode() */ { struct erofs_inode *vi_iter; struct inode *realinode; int got = 0; spin_lock(&vis->ishare_lock); list_for_each_entry(vi_iter, &vis->ishare_list, ishare_list) { realinode = igrab(&vi_iter->vfs_inode); pr_err("TRIGGER: igrab(ino=%lu) = %s\n", vi_iter->vfs_inode.i_ino, realinode ? "OK" : "NULL"); if (realinode) { got = 1; break; } } spin_unlock(&vis->ishare_lock); if (!got) { pr_err("TRIGGER: ========================================\n"); pr_err("TRIGGER: *** BUG CONFIRMED ***\n"); pr_err("TRIGGER: ALL igrab returned NULL\n"); pr_err("TRIGGER: realinode is UNINITIALIZED\n"); pr_err("TRIGGER: ========================================\n"); } else { pr_err("TRIGGER: igrab succeeded (bug not triggered)\n"); iput(realinode); } } /* Restore inode states */ spin_lock(&vis->ishare_lock); list_for_each_entry(real_iter, &vis->ishare_list, ishare_list) { spin_lock(&real_iter->vfs_inode.i_lock); inode_state_clear_raw(&real_iter->vfs_inode, I_FREEING); spin_unlock(&real_iter->vfs_inode.i_lock); } spin_unlock(&vis->ishare_lock); break; } cnt++; if (cnt > 20) break; } if (cnt > 20) pr_err("TRIGGER: shared inode not found in list\n"); path_put(&p); pr_err("TRIGGER: done\n"); return -EAGAIN; } static void __exit trigger_exit(void) {} module_init(trigger_init); module_exit(trigger_exit);