]> git.ipfire.org Git - thirdparty/linux.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)
committerJan Kara <jack@suse.cz>
Wed, 26 Jun 2024 10:54:11 +0000 (12:54 +0200)
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>
fs/udf/balloc.c

index a76490b2ca19c872e71f70b552b4fb8152077f13..d8fc11765d6127473356241a1ffe0af84808b9cf 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"
@@ -123,7 +124,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;
@@ -132,19 +132,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);
@@ -374,7 +364,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;
@@ -383,16 +372,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);
 
@@ -667,6 +646,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,