Skip to content

Commit

Permalink
udf: prevent integer overflow in udf_bitmap_free_blocks()
Browse files Browse the repository at this point in the history
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/[email protected]
Suggested-by: Jan Kara <[email protected]>
Signed-off-by: Roman Smirnov <[email protected]>
Signed-off-by: Jan Kara <[email protected]>
  • Loading branch information
Roman Smirnov authored and jankara committed Jun 26, 2024
1 parent ebbe26f commit 56e69e5
Showing 1 changed file with 13 additions and 23 deletions.
36 changes: 13 additions & 23 deletions fs/udf/balloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "udfdecl.h"

#include <linux/bitops.h>
#include <linux/overflow.h>

#include "udf_i.h"
#include "udf_sb.h"
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 56e69e5

Please sign in to comment.