udf: prevent integer overflow in udf_bitmap_free_blocks()
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>
This commit is contained in:
@ -18,6 +18,7 @@
|
|||||||
#include "udfdecl.h"
|
#include "udfdecl.h"
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/overflow.h>
|
||||||
|
|
||||||
#include "udf_i.h"
|
#include "udf_i.h"
|
||||||
#include "udf_sb.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 udf_sb_info *sbi = UDF_SB(sb);
|
||||||
struct buffer_head *bh = NULL;
|
struct buffer_head *bh = NULL;
|
||||||
struct udf_part_map *partmap;
|
|
||||||
unsigned long block;
|
unsigned long block;
|
||||||
unsigned long block_group;
|
unsigned long block_group;
|
||||||
unsigned long bit;
|
unsigned long bit;
|
||||||
@ -132,19 +132,9 @@ static void udf_bitmap_free_blocks(struct super_block *sb,
|
|||||||
unsigned long overflow;
|
unsigned long overflow;
|
||||||
|
|
||||||
mutex_lock(&sbi->s_alloc_mutex);
|
mutex_lock(&sbi->s_alloc_mutex);
|
||||||
partmap = &sbi->s_partmaps[bloc->partitionReferenceNum];
|
/* We make sure this cannot overflow when mounting the filesystem */
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
block = bloc->logicalBlockNum + offset +
|
block = bloc->logicalBlockNum + offset +
|
||||||
(sizeof(struct spaceBitmapDesc) << 3);
|
(sizeof(struct spaceBitmapDesc) << 3);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
overflow = 0;
|
overflow = 0;
|
||||||
block_group = block >> (sb->s_blocksize_bits + 3);
|
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)
|
uint32_t count)
|
||||||
{
|
{
|
||||||
struct udf_sb_info *sbi = UDF_SB(sb);
|
struct udf_sb_info *sbi = UDF_SB(sb);
|
||||||
struct udf_part_map *partmap;
|
|
||||||
uint32_t start, end;
|
uint32_t start, end;
|
||||||
uint32_t elen;
|
uint32_t elen;
|
||||||
struct kernel_lb_addr eloc;
|
struct kernel_lb_addr eloc;
|
||||||
@ -383,16 +372,6 @@ static void udf_table_free_blocks(struct super_block *sb,
|
|||||||
struct udf_inode_info *iinfo;
|
struct udf_inode_info *iinfo;
|
||||||
|
|
||||||
mutex_lock(&sbi->s_alloc_mutex);
|
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);
|
iinfo = UDF_I(table);
|
||||||
udf_add_free_space(sb, sbi->s_partition, count);
|
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;
|
uint16_t partition = bloc->partitionReferenceNum;
|
||||||
struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
|
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) {
|
if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) {
|
||||||
udf_bitmap_free_blocks(sb, map->s_uspace.s_bitmap,
|
udf_bitmap_free_blocks(sb, map->s_uspace.s_bitmap,
|
||||||
|
Reference in New Issue
Block a user