bcachefs: fix invalid free in dio write path
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 31 Dec 2023 01:52:18 +0000 (20:52 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 1 Jan 2024 16:43:03 +0000 (11:43 -0500)
turns out iterate_iovec() mutates __iov, we need to save our own copy

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Reported-by: Marcin Mirosław <marcin@mejor.pl>
fs/bcachefs/fs-io-direct.c

index 9a479e4de6b36a71d1bc4b3c1ef62d8787098179..84e20c3ada6cbb50e3de3a40510db6125856e443 100644 (file)
@@ -216,11 +216,11 @@ struct dio_write {
        struct address_space            *mapping;
        struct bch_inode_info           *inode;
        struct mm_struct                *mm;
+       const struct iovec              *iov;
        unsigned                        loop:1,
                                        extending:1,
                                        sync:1,
-                                       flush:1,
-                                       free_iov:1;
+                                       flush:1;
        struct quota_res                quota_res;
        u64                             written;
 
@@ -312,12 +312,10 @@ static noinline int bch2_dio_write_copy_iov(struct dio_write *dio)
                return -1;
 
        if (dio->iter.nr_segs > ARRAY_SIZE(dio->inline_vecs)) {
-               iov = kmalloc_array(dio->iter.nr_segs, sizeof(*iov),
+               dio->iov = iov = kmalloc_array(dio->iter.nr_segs, sizeof(*iov),
                                    GFP_KERNEL);
                if (unlikely(!iov))
                        return -ENOMEM;
-
-               dio->free_iov = true;
        }
 
        memcpy(iov, dio->iter.__iov, dio->iter.nr_segs * sizeof(*iov));
@@ -381,8 +379,7 @@ static __always_inline long bch2_dio_write_done(struct dio_write *dio)
 
        bch2_pagecache_block_put(inode);
 
-       if (dio->free_iov)
-               kfree(dio->iter.__iov);
+       kfree(dio->iov);
 
        ret = dio->op.error ?: ((long) dio->written << 9);
        bio_put(&dio->op.wbio.bio);
@@ -626,11 +623,11 @@ ssize_t bch2_direct_write(struct kiocb *req, struct iov_iter *iter)
        dio->mapping            = mapping;
        dio->inode              = inode;
        dio->mm                 = current->mm;
+       dio->iov                = NULL;
        dio->loop               = false;
        dio->extending          = extending;
        dio->sync               = is_sync_kiocb(req) || extending;
        dio->flush              = iocb_is_dsync(req) && !c->opts.journal_flush_disabled;
-       dio->free_iov           = false;
        dio->quota_res.sectors  = 0;
        dio->written            = 0;
        dio->iter               = *iter;