From d29f475108c8c8bc104fd8b79a789023e4acc755 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Tue, 13 Aug 2019 21:03:59 -0400 Subject: [PATCH] btrfs-progs: check if adding device would overflow while scanning It's theoretically possible to add multiple devices with sizes that add up to or exceed 16EiB. A file system will be created successfully but will have a superblock with incorrect values for total_bytes and other fields. Kernels up to v5.0 will crash when they encounter this scenario. We need to check for overflow and reject the device if it would overflow. Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=1099147 Reviewed-by: Qu Wenruo Signed-off-by: Jeff Mahoney Signed-off-by: David Sterba --- common/device-scan.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/common/device-scan.c b/common/device-scan.c index 2c5ae225..a51d3b1e 100644 --- a/common/device-scan.c +++ b/common/device-scan.c @@ -26,6 +26,7 @@ #include #include #include +#include "kernel-lib/overflow.h" #include "common/path-utils.h" #include "common/device-scan.h" #include "common/messages.h" @@ -118,7 +119,8 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, struct btrfs_device *device; struct btrfs_dev_item *dev_item; char *buf = NULL; - u64 fs_total_bytes; + const u64 old_size = btrfs_super_total_bytes(super); + u64 new_size; u64 num_devs; int ret; @@ -156,13 +158,20 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, goto out; } + if (check_add_overflow(old_size, device_total_bytes, &new_size)) { + error( + "adding device of %llu bytes would exceed max file system size", + device->total_bytes); + ret = -EOVERFLOW; + goto out; + } + INIT_LIST_HEAD(&device->dev_list); ret = btrfs_add_device(trans, fs_info, device); if (ret) goto out; - fs_total_bytes = btrfs_super_total_bytes(super) + device_total_bytes; - btrfs_set_super_total_bytes(super, fs_total_bytes); + btrfs_set_super_total_bytes(super, new_size); num_devs = btrfs_super_num_devices(super) + 1; btrfs_set_super_num_devices(super, num_devs);