]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
udf: prevent integer overflow in udf_bitmap_free_blocks()
authorRoman Smirnov <r.smirnov@omp.ru>
Thu, 20 Jun 2024 07:24:13 +0000 (10:24 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 14 Aug 2024 13:34:10 +0000 (15:34 +0200)
[ Upstream commit 56e69e59751d20993f243fb7dd6991c4e522424c ]

An overflow may occur if the function is called with the last
block and an offset greater than zero. It is necessary to add
a check to avoid this.

Found by Linux Verification Center (linuxtesting.org) with Svace.

[JK: Make test cover also unalloc table freeing]

Link: https://patch.msgid.link/20240620072413.7448-1-r.smirnov@omp.ru
Suggested-by: Jan Kara <jack@suse.com>
Signed-off-by: Roman Smirnov <r.smirnov@omp.ru>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/udf/balloc.c

index 558ad046972ad53c7a48a69c51f46e03869e3411..bb471ec36404672f381b86a9d764cf01bc988f44 100644 (file)
@@ -18,6 +18,7 @@
 #include "udfdecl.h"
 
 #include <linux/bitops.h>
+#include <linux/overflow.h>
 
 #include "udf_i.h"
 #include "udf_sb.h"
@@ -140,7 +141,6 @@ static void udf_bitmap_free_blocks(struct super_block *sb,
 {
        struct udf_sb_info *sbi = UDF_SB(sb);
        struct buffer_head *bh = NULL;
-       struct udf_part_map *partmap;
        unsigned long block;
        unsigned long block_group;
        unsigned long bit;
@@ -149,19 +149,9 @@ static void udf_bitmap_free_blocks(struct super_block *sb,
        unsigned long overflow;
 
        mutex_lock(&sbi->s_alloc_mutex);
-       partmap = &sbi->s_partmaps[bloc->partitionReferenceNum];
-       if (bloc->logicalBlockNum + count < count ||
-           (bloc->logicalBlockNum + count) > partmap->s_partition_len) {
-               udf_debug("%u < %d || %u + %u > %u\n",
-                         bloc->logicalBlockNum, 0,
-                         bloc->logicalBlockNum, count,
-                         partmap->s_partition_len);
-               goto error_return;
-       }
-
+       /* We make sure this cannot overflow when mounting the filesystem */
        block = bloc->logicalBlockNum + offset +
                (sizeof(struct spaceBitmapDesc) << 3);
-
        do {
                overflow = 0;
                block_group = block >> (sb->s_blocksize_bits + 3);
@@ -391,7 +381,6 @@ static void udf_table_free_blocks(struct super_block *sb,
                                  uint32_t count)
 {
        struct udf_sb_info *sbi = UDF_SB(sb);
-       struct udf_part_map *partmap;
        uint32_t start, end;
        uint32_t elen;
        struct kernel_lb_addr eloc;
@@ -400,16 +389,6 @@ static void udf_table_free_blocks(struct super_block *sb,
        struct udf_inode_info *iinfo;
 
        mutex_lock(&sbi->s_alloc_mutex);
-       partmap = &sbi->s_partmaps[bloc->partitionReferenceNum];
-       if (bloc->logicalBlockNum + count < count ||
-           (bloc->logicalBlockNum + count) > partmap->s_partition_len) {
-               udf_debug("%u < %d || %u + %u > %u\n",
-                         bloc->logicalBlockNum, 0,
-                         bloc->logicalBlockNum, count,
-                         partmap->s_partition_len);
-               goto error_return;
-       }
-
        iinfo = UDF_I(table);
        udf_add_free_space(sb, sbi->s_partition, count);
 
@@ -684,6 +663,17 @@ void udf_free_blocks(struct super_block *sb, struct inode *inode,
 {
        uint16_t partition = bloc->partitionReferenceNum;
        struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
+       uint32_t blk;
+
+       if (check_add_overflow(bloc->logicalBlockNum, offset, &blk) ||
+           check_add_overflow(blk, count, &blk) ||
+           bloc->logicalBlockNum + count > map->s_partition_len) {
+               udf_debug("Invalid request to free blocks: (%d, %u), off %u, "
+                         "len %u, partition len %u\n",
+                         partition, bloc->logicalBlockNum, offset, count,
+                         map->s_partition_len);
+               return;
+       }
 
        if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) {
                udf_bitmap_free_blocks(sb, map->s_uspace.s_bitmap,