]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ntfs: harden runlist realloc size calculations
authorNamjae Jeon <linkinjeon@kernel.org>
Fri, 10 Jul 2026 05:22:57 +0000 (14:22 +0900)
committerNamjae Jeon <linkinjeon@kernel.org>
Tue, 21 Jul 2026 09:48:24 +0000 (18:48 +0900)
Add a shared helper to safely convert runlist element counts to byte sizes
using overflow checks, and use it in both ntfs_rl_realloc() and
ntfs_rl_realloc_nofail().

Fixes: 11ccc9107dc4 ("ntfs: update runlist handling and cluster allocator")
Co-developed-by: Alper Mudar <kommandant_alper@proton.me>
Signed-off-by: Alper Mudar <kommandant_alper@proton.me>
Tested-by: Alper Mudar <kommandant_alper@proton.me>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
fs/ntfs/runlist.c

index cbb6576cf72582b87702f82a12574de5c242af14..8e0fd400e7f78923e35c57e1de9db6608e1cc06b 100644 (file)
@@ -71,29 +71,46 @@ static inline void ntfs_rl_mc(struct runlist_element *dstbase, int dst,
  * On success, return a pointer to the newly allocated, or recycled, memory.
  * On error, return -errno.
  */
-struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
-               int old_size, int new_size)
+static inline struct runlist_element *ntfs_rl_realloc_gfp(struct runlist_element *rl,
+               int old_size, int new_size, gfp_t gfp)
 {
        struct runlist_element *new_rl;
+       size_t new_bytes;
+
+       if (old_size < 0 || new_size < 0)
+               return ERR_PTR(-EINVAL);
 
-       old_size = old_size * sizeof(*rl);
-       new_size = new_size * sizeof(*rl);
        if (old_size == new_size)
                return rl;
 
-       new_rl = kvzalloc(new_size, GFP_NOFS);
+       if (check_mul_overflow(new_size, sizeof(*rl), &new_bytes))
+               return ERR_PTR(-EINVAL);
+
+       new_rl = kvzalloc(new_bytes, gfp);
        if (unlikely(!new_rl))
                return ERR_PTR(-ENOMEM);
 
        if (likely(rl != NULL)) {
-               if (unlikely(old_size > new_size))
-                       old_size = new_size;
-               memcpy(new_rl, rl, old_size);
+               size_t old_bytes;
+
+               if (check_mul_overflow(old_size, sizeof(*rl), &old_bytes)) {
+                       kvfree(new_rl);
+                       return ERR_PTR(-EINVAL);
+               }
+               if (unlikely(old_bytes > new_bytes))
+                       old_bytes = new_bytes;
+               memcpy(new_rl, rl, old_bytes);
                kvfree(rl);
        }
        return new_rl;
 }
 
+struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
+               int old_size, int new_size)
+{
+       return ntfs_rl_realloc_gfp(rl, old_size, new_size, GFP_NOFS);
+}
+
 /*
  * ntfs_rl_realloc_nofail - Reallocate memory for runlists
  * @rl:                original runlist
@@ -118,21 +135,8 @@ struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
 static inline struct runlist_element *ntfs_rl_realloc_nofail(struct runlist_element *rl,
                int old_size, int new_size)
 {
-       struct runlist_element *new_rl;
-
-       old_size = old_size * sizeof(*rl);
-       new_size = new_size * sizeof(*rl);
-       if (old_size == new_size)
-               return rl;
-
-       new_rl = kvmalloc(new_size, GFP_NOFS | __GFP_NOFAIL);
-       if (likely(rl != NULL)) {
-               if (unlikely(old_size > new_size))
-                       old_size = new_size;
-               memcpy(new_rl, rl, old_size);
-               kvfree(rl);
-       }
-       return new_rl;
+       return ntfs_rl_realloc_gfp(rl, old_size, new_size,
+                       GFP_NOFS | __GFP_NOFAIL);
 }
 
 /*