]> www.infradead.org Git - users/hch/dma-mapping.git/commitdiff
nilfs2: fix incorrect inode allocation from reserved inodes
authorRyusuke Konishi <konishi.ryusuke@gmail.com>
Sun, 23 Jun 2024 05:11:35 +0000 (14:11 +0900)
committerAndrew Morton <akpm@linux-foundation.org>
Wed, 3 Jul 2024 19:29:25 +0000 (12:29 -0700)
If the bitmap block that manages the inode allocation status is corrupted,
nilfs_ifile_create_inode() may allocate a new inode from the reserved
inode area where it should not be allocated.

Previous fix commit d325dc6eb763 ("nilfs2: fix use-after-free bug of
struct nilfs_root"), fixed the problem that reserved inodes with inode
numbers less than NILFS_USER_INO (=11) were incorrectly reallocated due to
bitmap corruption, but since the start number of non-reserved inodes is
read from the super block and may change, in which case inode allocation
may occur from the extended reserved inode area.

If that happens, access to that inode will cause an IO error, causing the
file system to degrade to an error state.

Fix this potential issue by adding a wraparound option to the common
metadata object allocation routine and by modifying
nilfs_ifile_create_inode() to disable the option so that it only allocates
inodes with inode numbers greater than or equal to the inode number read
in "nilfs->ns_first_ino", regardless of the bitmap status of reserved
inodes.

Link: https://lkml.kernel.org/r/20240623051135.4180-4-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Cc: Hillf Danton <hdanton@sina.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/nilfs2/alloc.c
fs/nilfs2/alloc.h
fs/nilfs2/dat.c
fs/nilfs2/ifile.c

index 89caef7513db3509b1a83090324b47536a3f8e5e..ba50388ee4bf1558f9d7f60754e3397552a92383 100644 (file)
@@ -377,11 +377,12 @@ void *nilfs_palloc_block_get_entry(const struct inode *inode, __u64 nr,
  * @target: offset number of an entry in the group (start point)
  * @bsize: size in bits
  * @lock: spin lock protecting @bitmap
+ * @wrap: whether to wrap around
  */
 static int nilfs_palloc_find_available_slot(unsigned char *bitmap,
                                            unsigned long target,
                                            unsigned int bsize,
-                                           spinlock_t *lock)
+                                           spinlock_t *lock, bool wrap)
 {
        int pos, end = bsize;
 
@@ -397,6 +398,8 @@ static int nilfs_palloc_find_available_slot(unsigned char *bitmap,
 
                end = target;
        }
+       if (!wrap)
+               return -ENOSPC;
 
        /* wrap around */
        for (pos = 0; pos < end; pos++) {
@@ -495,9 +498,10 @@ int nilfs_palloc_count_max_entries(struct inode *inode, u64 nused, u64 *nmaxp)
  * nilfs_palloc_prepare_alloc_entry - prepare to allocate a persistent object
  * @inode: inode of metadata file using this allocator
  * @req: nilfs_palloc_req structure exchanged for the allocation
+ * @wrap: whether to wrap around
  */
 int nilfs_palloc_prepare_alloc_entry(struct inode *inode,
-                                    struct nilfs_palloc_req *req)
+                                    struct nilfs_palloc_req *req, bool wrap)
 {
        struct buffer_head *desc_bh, *bitmap_bh;
        struct nilfs_palloc_group_desc *desc;
@@ -516,7 +520,7 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode,
        entries_per_group = nilfs_palloc_entries_per_group(inode);
 
        for (i = 0; i < ngroups; i += n) {
-               if (group >= ngroups) {
+               if (group >= ngroups && wrap) {
                        /* wrap around */
                        group = 0;
                        maxgroup = nilfs_palloc_group(inode, req->pr_entry_nr,
@@ -550,7 +554,14 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode,
                        bitmap_kaddr = kmap_local_page(bitmap_bh->b_page);
                        bitmap = bitmap_kaddr + bh_offset(bitmap_bh);
                        pos = nilfs_palloc_find_available_slot(
-                               bitmap, group_offset, entries_per_group, lock);
+                               bitmap, group_offset, entries_per_group, lock,
+                               wrap);
+                       /*
+                        * Since the search for a free slot in the second and
+                        * subsequent bitmap blocks always starts from the
+                        * beginning, the wrap flag only has an effect on the
+                        * first search.
+                        */
                        kunmap_local(bitmap_kaddr);
                        if (pos >= 0)
                                goto found;
index b667e869ac076ad49af3a603069f3b1013720cf5..d825a9faca6d9608ce6e73f5282b627dc6dc6701 100644 (file)
@@ -50,8 +50,8 @@ struct nilfs_palloc_req {
        struct buffer_head *pr_entry_bh;
 };
 
-int nilfs_palloc_prepare_alloc_entry(struct inode *,
-                                    struct nilfs_palloc_req *);
+int nilfs_palloc_prepare_alloc_entry(struct inode *inode,
+                                    struct nilfs_palloc_req *req, bool wrap);
 void nilfs_palloc_commit_alloc_entry(struct inode *,
                                     struct nilfs_palloc_req *);
 void nilfs_palloc_abort_alloc_entry(struct inode *, struct nilfs_palloc_req *);
index 180fc8d36213df3b73a5be9ad744bfefd97f4b47..fc1caf63a42aee249aa5aa8e912b8d7a7f923654 100644 (file)
@@ -75,7 +75,7 @@ int nilfs_dat_prepare_alloc(struct inode *dat, struct nilfs_palloc_req *req)
 {
        int ret;
 
-       ret = nilfs_palloc_prepare_alloc_entry(dat, req);
+       ret = nilfs_palloc_prepare_alloc_entry(dat, req, true);
        if (ret < 0)
                return ret;
 
index 612e609158b520ccbb3f0ae97a7e9e26f5a644db..1e86b9303b7ca17c5bd1a86e487de44d0a5ad6f9 100644 (file)
@@ -56,13 +56,10 @@ int nilfs_ifile_create_inode(struct inode *ifile, ino_t *out_ino,
        struct nilfs_palloc_req req;
        int ret;
 
-       req.pr_entry_nr = 0;  /*
-                              * 0 says find free inode from beginning
-                              * of a group. dull code!!
-                              */
+       req.pr_entry_nr = NILFS_FIRST_INO(ifile->i_sb);
        req.pr_entry_bh = NULL;
 
-       ret = nilfs_palloc_prepare_alloc_entry(ifile, &req);
+       ret = nilfs_palloc_prepare_alloc_entry(ifile, &req, false);
        if (!ret) {
                ret = nilfs_palloc_get_entry_block(ifile, req.pr_entry_nr, 1,
                                                   &req.pr_entry_bh);