]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ntfs: fix empty_buf and ra lifetime bugs in ntfs_empty_logfile()
authorDaeMyung Kang <charsyam@gmail.com>
Sun, 10 May 2026 02:13:11 +0000 (11:13 +0900)
committerNamjae Jeon <linkinjeon@kernel.org>
Sun, 10 May 2026 07:45:28 +0000 (16:45 +0900)
ntfs_empty_logfile() has three related allocator bugs around the
@empty_buf and @ra buffers it uses inside the per-cluster loop.

When the loop encounters a runlist entry with LCN_RL_NOT_MAPPED, the
function kvfrees @empty_buf and goes to map_vcn to remap.  @empty_buf
is not cleared.  If ntfs_map_runlist_nolock() fails on re-entry,
control jumps to the err label which kvfrees @empty_buf a second time.

In the same branch, @ra is left allocated.  When the remap succeeds
the function falls through the @empty_buf re-allocation and the @ra
re-allocation, overwriting the previous @ra pointer and leaking it.

The success path frees @empty_buf with kfree() instead of kvfree().
kvzalloc() may fall back to vmalloc(), in which case kfree() does not
correctly release the memory.

A KASAN-enabled QEMU harness mirroring this control flow reports
"BUG: KASAN: double-free" when the second ntfs_map_runlist_nolock()
fails.

Clear both @empty_buf and @ra after the in-loop releases so the err
path is a no-op when the buffers have already been freed and so the
remap-success path does not leak the previous @ra.  Switch the success
path to kvfree() to match the @empty_buf allocator.

Fixes: 5218cd102aec ("ntfs: update misc operations")
Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
fs/ntfs/logfile.c

index 3f8d1640f1d501d4ac7407feeab0826ad0cd56e0..d3f25d8e29f9d28b956cce21fe52655631a935e7 100644 (file)
@@ -710,6 +710,9 @@ map_vcn:
                if (unlikely(lcn == LCN_RL_NOT_MAPPED)) {
                        vcn = rl->vcn;
                        kvfree(empty_buf);
+                       empty_buf = NULL;
+                       kfree(ra);
+                       ra = NULL;
                        goto map_vcn;
                }
                /* If this run is not valid abort with an error. */
@@ -753,7 +756,7 @@ map_vcn:
                } while (start < end);
        } while ((++rl)->vcn < end_vcn);
        up_write(&log_ni->runlist.lock);
-       kfree(empty_buf);
+       kvfree(empty_buf);
        kfree(ra);
        truncate_inode_pages(log_vi->i_mapping, 0);
        /* Set the flag so we do not have to do it again on remount. */