* [PATCH v2] udf: balloc: prevent integer overflow in udf_bitmap_free_blocks()
@ 2024-06-20 7:24 Roman Smirnov
2024-06-20 13:35 ` Jan Kara
0 siblings, 1 reply; 2+ messages in thread
From: Roman Smirnov @ 2024-06-20 7:24 UTC (permalink / raw)
To: Jan Kara, linux-kernel; +Cc: Roman Smirnov, Sergey Shtylyov, lvc-project
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.
Overflow is also possible when we sum offset and
sizeof(struct spaceBitmapDesc) << 3. For this reason it
is necessary to check overflow of this too. The result is
stored in total_offset.
Found by Linux Verification Center (linuxtesting.org) with Svace.
Suggested-by: Jan Kara <jack@suse.com>
Signed-off-by: Roman Smirnov <r.smirnov@omp.ru>
---
V1 -> V2: Overflow checks have been replaced by functions
fs/udf/balloc.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
index ab3ffc355949..4bf2720111f1 100644
--- a/fs/udf/balloc.c
+++ b/fs/udf/balloc.c
@@ -18,6 +18,7 @@
#include "udfdecl.h"
#include <linux/bitops.h>
+#include <linux/overflow.h>
#include "udf_i.h"
#include "udf_sb.h"
@@ -133,6 +134,7 @@ static void udf_bitmap_free_blocks(struct super_block *sb,
unsigned long block;
unsigned long block_group;
unsigned long bit;
+ unsigned int total_offset;
unsigned long i;
int bitmap_nr;
unsigned long overflow;
@@ -140,17 +142,20 @@ static void udf_bitmap_free_blocks(struct super_block *sb,
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 + count) > partmap->s_partition_len ||
+ check_add_overflow(offset,
+ sizeof(struct spaceBitmapDesc) << 3, &total_offset) ||
+ check_add_overflow(bloc->logicalBlockNum, total_offset, &block)) {
+ udf_debug("%u < %d || %u + %u > %u || %u + %zu > %u || %u + %u + %zu > %u\n",
bloc->logicalBlockNum, 0,
bloc->logicalBlockNum, count,
- partmap->s_partition_len);
+ partmap->s_partition_len,
+ offset, sizeof(struct spaceBitmapDesc) << 3, UINT_MAX,
+ bloc->logicalBlockNum, offset,
+ sizeof(struct spaceBitmapDesc) << 3, UINT_MAX);
goto error_return;
}
- block = bloc->logicalBlockNum + offset +
- (sizeof(struct spaceBitmapDesc) << 3);
-
do {
overflow = 0;
block_group = block >> (sb->s_blocksize_bits + 3);
--
2.34.1
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH v2] udf: balloc: prevent integer overflow in udf_bitmap_free_blocks()
2024-06-20 7:24 [PATCH v2] udf: balloc: prevent integer overflow in udf_bitmap_free_blocks() Roman Smirnov
@ 2024-06-20 13:35 ` Jan Kara
0 siblings, 0 replies; 2+ messages in thread
From: Jan Kara @ 2024-06-20 13:35 UTC (permalink / raw)
To: Roman Smirnov; +Cc: Jan Kara, linux-kernel, Sergey Shtylyov, lvc-project
[-- Attachment #1: Type: text/plain, Size: 969 bytes --]
On Thu 20-06-24 10:24:13, Roman Smirnov wrote:
> 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.
>
> Overflow is also possible when we sum offset and
> sizeof(struct spaceBitmapDesc) << 3. For this reason it
> is necessary to check overflow of this too. The result is
> stored in total_offset.
>
> Found by Linux Verification Center (linuxtesting.org) with Svace.
>
> Suggested-by: Jan Kara <jack@suse.com>
> Signed-off-by: Roman Smirnov <r.smirnov@omp.ru>
Thanks for the patch. In the end I've noticed that unalloc table block
freeing has the same overflow checks and I've decided to move bitmap offset
overflow verification into mount code (so that any bitmap offset for a
block within a partition cannot overflow u32). The resulting patches are
attached for reference and I've queued them in my tree.
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
[-- Attachment #2: 0001-udf-prevent-integer-overflow-in-udf_bitmap_free_bloc.patch --]
[-- Type: text/x-patch, Size: 3602 bytes --]
From 0b058943e21fe0034b3bd7d634ddb1508380f50f Mon Sep 17 00:00:00 2001
From: Roman Smirnov <r.smirnov@omp.ru>
Date: Thu, 20 Jun 2024 10:24:13 +0300
Subject: [PATCH] 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>
---
fs/udf/balloc.c | 36 +++++++++++++-----------------------
1 file changed, 13 insertions(+), 23 deletions(-)
diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
index a76490b2ca19..d8fc11765d61 100644
--- a/fs/udf/balloc.c
+++ b/fs/udf/balloc.c
@@ -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,
--
2.35.3
[-- Attachment #3: 0001-udf-Avoid-excessive-partition-lengths.patch --]
[-- Type: text/x-patch, Size: 2089 bytes --]
From 3533a6bc34badaa3d32c7b0f2816dcf1bade7f8a Mon Sep 17 00:00:00 2001
From: Jan Kara <jack@suse.cz>
Date: Thu, 20 Jun 2024 12:52:17 +0200
Subject: [PATCH] udf: Avoid excessive partition lengths
Avoid mounting filesystems where the partition would overflow the
32-bits used for block number. Also refuse to mount filesystems where
the partition length is so large we cannot safely index bits in a
block bitmap.
Link: https://patch.msgid.link/20240620130403.14731-1-jack@suse.cz
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/udf/super.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 9381a66c6ce5..c7bdda3f9369 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -1110,12 +1110,19 @@ static int udf_fill_partdesc_info(struct super_block *sb,
struct udf_part_map *map;
struct udf_sb_info *sbi = UDF_SB(sb);
struct partitionHeaderDesc *phd;
+ u32 sum;
int err;
map = &sbi->s_partmaps[p_index];
map->s_partition_len = le32_to_cpu(p->partitionLength); /* blocks */
map->s_partition_root = le32_to_cpu(p->partitionStartingLocation);
+ if (check_add_overflow(map->s_partition_root, map->s_partition_len,
+ &sum)) {
+ udf_err(sb, "Partition %d has invalid location %u + %u\n",
+ p_index, map->s_partition_root, map->s_partition_len);
+ return -EFSCORRUPTED;
+ }
if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_READ_ONLY))
map->s_partition_flags |= UDF_PART_FLAG_READ_ONLY;
@@ -1171,6 +1178,14 @@ static int udf_fill_partdesc_info(struct super_block *sb,
bitmap->s_extPosition = le32_to_cpu(
phd->unallocSpaceBitmap.extPosition);
map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_BITMAP;
+ /* Check whether math over bitmap won't overflow. */
+ if (check_add_overflow(map->s_partition_len,
+ sizeof(struct spaceBitmapDesc) << 3,
+ &sum)) {
+ udf_err(sb, "Partition %d it too long (%u)\n", p_index,
+ map->s_partition_len);
+ return -EFSCORRUPTED;
+ }
udf_debug("unallocSpaceBitmap (part %d) @ %u\n",
p_index, bitmap->s_extPosition);
}
--
2.35.3
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2024-06-20 13:35 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-20 7:24 [PATCH v2] udf: balloc: prevent integer overflow in udf_bitmap_free_blocks() Roman Smirnov
2024-06-20 13:35 ` Jan Kara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®