| From 9845664b9ee47ce7ee7ea93caf47d39a9d4552c4 Mon Sep 17 00:00:00 2001 |
| From: David Sterba <dsterba@suse.com> |
| Date: Wed, 14 Feb 2024 16:19:24 +0100 |
| Subject: btrfs: dev-replace: properly validate device names |
| |
| From: David Sterba <dsterba@suse.com> |
| |
| commit 9845664b9ee47ce7ee7ea93caf47d39a9d4552c4 upstream. |
| |
| There's a syzbot report that device name buffers passed to device |
| replace are not properly checked for string termination which could lead |
| to a read out of bounds in getname_kernel(). |
| |
| Add a helper that validates both source and target device name buffers. |
| For devid as the source initialize the buffer to empty string in case |
| something tries to read it later. |
| |
| This was originally analyzed and fixed in a different way by Edward Adam |
| Davis (see links). |
| |
| Link: https://lore.kernel.org/linux-btrfs/000000000000d1a1d1060cc9c5e7@google.com/ |
| Link: https://lore.kernel.org/linux-btrfs/tencent_44CA0665C9836EF9EEC80CB9E7E206DF5206@qq.com/ |
| CC: stable@vger.kernel.org # 4.19+ |
| CC: Edward Adam Davis <eadavis@qq.com> |
| Reported-and-tested-by: syzbot+33f23b49ac24f986c9e8@syzkaller.appspotmail.com |
| Reviewed-by: Boris Burkov <boris@bur.io> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/btrfs/dev-replace.c | 24 ++++++++++++++++++++---- |
| 1 file changed, 20 insertions(+), 4 deletions(-) |
| |
| --- a/fs/btrfs/dev-replace.c |
| +++ b/fs/btrfs/dev-replace.c |
| @@ -566,6 +566,23 @@ leave: |
| return ret; |
| } |
| |
| +static int btrfs_check_replace_dev_names(struct btrfs_ioctl_dev_replace_args *args) |
| +{ |
| + if (args->start.srcdevid == 0) { |
| + if (memchr(args->start.srcdev_name, 0, |
| + sizeof(args->start.srcdev_name)) == NULL) |
| + return -ENAMETOOLONG; |
| + } else { |
| + args->start.srcdev_name[0] = 0; |
| + } |
| + |
| + if (memchr(args->start.tgtdev_name, 0, |
| + sizeof(args->start.tgtdev_name)) == NULL) |
| + return -ENAMETOOLONG; |
| + |
| + return 0; |
| +} |
| + |
| int btrfs_dev_replace_by_ioctl(struct btrfs_fs_info *fs_info, |
| struct btrfs_ioctl_dev_replace_args *args) |
| { |
| @@ -578,10 +595,9 @@ int btrfs_dev_replace_by_ioctl(struct bt |
| default: |
| return -EINVAL; |
| } |
| - |
| - if ((args->start.srcdevid == 0 && args->start.srcdev_name[0] == '\0') || |
| - args->start.tgtdev_name[0] == '\0') |
| - return -EINVAL; |
| + ret = btrfs_check_replace_dev_names(args); |
| + if (ret < 0) |
| + return ret; |
| |
| ret = btrfs_dev_replace_start(fs_info, args->start.tgtdev_name, |
| args->start.srcdevid, |