]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bcachefs: Fix alloc_req use after free
authorAlan Huang <mmpgouride@gmail.com>
Fri, 13 Jun 2025 14:54:59 +0000 (22:54 +0800)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 16 Jun 2025 02:11:55 +0000 (22:11 -0400)
Now the alloc_req is allocated from the bump allocator, if there is
reallocation, the memory of alloc_req would be frees, fix by delaying the
reallocation to transaction restart, it has to restart anyway.

Reported-by: syzbot+2887a13a5c387e616a68@syzkaller.appspotmail.com
Signed-off-by: Alan Huang <mmpgouride@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/btree_iter.c
fs/bcachefs/btree_types.h

index 55de2e474705cea386e68e505c2208d7611d004d..e2747c57ba2adc5599f315c77ffed5ecb67b8c2a 100644 (file)
@@ -3216,25 +3216,32 @@ void *__bch2_trans_kmalloc(struct btree_trans *trans, size_t size, unsigned long
                mutex_unlock(&s->lock);
        }
 
-       if (trans->used_mempool) {
+       if (trans->used_mempool || new_bytes > BTREE_TRANS_MEM_MAX) {
                EBUG_ON(trans->mem_bytes >= new_bytes);
                return ERR_PTR(-BCH_ERR_ENOMEM_trans_kmalloc);
        }
 
-       new_mem = krealloc(trans->mem, new_bytes, GFP_NOWAIT|__GFP_NOWARN);
+       if (old_bytes) {
+               trans->realloc_bytes_required = new_bytes;
+               trace_and_count(c, trans_restart_mem_realloced, trans, _RET_IP_, new_bytes);
+               return ERR_PTR(btree_trans_restart_ip(trans,
+                                       BCH_ERR_transaction_restart_mem_realloced, _RET_IP_));
+       }
+
+       EBUG_ON(trans->mem);
+
+       new_mem = kmalloc(new_bytes, GFP_NOWAIT|__GFP_NOWARN);
        if (unlikely(!new_mem)) {
                bch2_trans_unlock(trans);
 
-               new_mem = krealloc(trans->mem, new_bytes, GFP_KERNEL);
+               new_mem = kmalloc(new_bytes, GFP_KERNEL);
                if (!new_mem && new_bytes <= BTREE_TRANS_MEM_MAX) {
                        new_mem = mempool_alloc(&c->btree_trans_mem_pool, GFP_KERNEL);
                        new_bytes = BTREE_TRANS_MEM_MAX;
                        trans->used_mempool = true;
-                       kfree(trans->mem);
                }
 
-               if (!new_mem)
-                       return ERR_PTR(-BCH_ERR_ENOMEM_trans_kmalloc);
+               EBUG_ON(!new_mem);
 
                trans->mem = new_mem;
                trans->mem_bytes = new_bytes;
@@ -3247,14 +3254,6 @@ void *__bch2_trans_kmalloc(struct btree_trans *trans, size_t size, unsigned long
        trans->mem = new_mem;
        trans->mem_bytes = new_bytes;
 
-       if (old_bytes) {
-               trace_and_count(c, trans_restart_mem_realloced, trans, _RET_IP_, new_bytes);
-               return ERR_PTR(btree_trans_restart_ip(trans,
-                                       BCH_ERR_transaction_restart_mem_realloced, _RET_IP_));
-       }
-
-       bch2_trans_kmalloc_trace(trans, size, ip);
-
        p = trans->mem + trans->mem_top;
        trans->mem_top += size;
        memset(p, 0, size);
@@ -3315,6 +3314,27 @@ u32 bch2_trans_begin(struct btree_trans *trans)
        trans->restart_count++;
        trans->mem_top                  = 0;
 
+       if (trans->restarted == BCH_ERR_transaction_restart_mem_realloced) {
+               EBUG_ON(!trans->mem || !trans->mem_bytes);
+               unsigned new_bytes = trans->realloc_bytes_required;
+               void *new_mem = krealloc(trans->mem, new_bytes, GFP_NOWAIT|__GFP_NOWARN);
+               if (unlikely(!new_mem)) {
+                       bch2_trans_unlock(trans);
+                       new_mem = krealloc(trans->mem, new_bytes, GFP_KERNEL);
+
+                       EBUG_ON(new_bytes > BTREE_TRANS_MEM_MAX);
+
+                       if (!new_mem) {
+                               new_mem = mempool_alloc(&trans->c->btree_trans_mem_pool, GFP_KERNEL);
+                               new_bytes = BTREE_TRANS_MEM_MAX;
+                               trans->used_mempool = true;
+                               kfree(trans->mem);
+                       }
+                }
+               trans->mem = new_mem;
+               trans->mem_bytes = new_bytes;
+       }
+
        trans_for_each_path(trans, path, i) {
                path->should_be_locked = false;
 
index 3aa4a602bd02689d5c2f8eb78e3137b6b1a2a798..112170fd9c8fb5ef4da9064b99f23fb99f9e87f6 100644 (file)
@@ -497,6 +497,7 @@ struct btree_trans {
        void                    *mem;
        unsigned                mem_top;
        unsigned                mem_bytes;
+       unsigned                realloc_bytes_required;
 #ifdef CONFIG_BCACHEFS_TRANS_KMALLOC_TRACE
        darray_trans_kmalloc_trace trans_kmalloc_trace;
 #endif