btrfs-progs: Fix a buffer overflow causing segfault in fstests/btrfs/069
authorQu Wenruo <quwenruo@cn.fujitsu.com>
Tue, 6 Jan 2015 09:30:44 +0000 (17:30 +0800)
committerDavid Sterba <dsterba@suse.cz>
Fri, 9 Jan 2015 17:46:16 +0000 (18:46 +0100)
The newly introduced search_chunk_tree_for_fs_info() won't count devid 0
in fi_arg->num_devices, which will cause buffer overflow since later
get_device_info() will fill di_args with devid.

This can be trigger by fstests/btrfs/069 and any operations needs to
iterate over all the devices like 'fi show' or 'dev stat' while
replacing.

The fix is do an extra probe specifically for devid 0 after
search_chunk_tree_for_fs_info() and change num_devices if needed.

Reported-by: Tsutomu Itoh <t-itoh@jp.fujitsu.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: Gui Hecheng <guihc.fnst@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
utils.c

diff --git a/utils.c b/utils.c
index 868b15e..9b08144 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -1943,8 +1943,10 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
        int ret = 0;
        int ndevs = 0;
        int i = 0;
+       int replacing = 0;
        struct btrfs_fs_devices *fs_devices_mnt = NULL;
        struct btrfs_ioctl_dev_info_args *di_args;
+       struct btrfs_ioctl_dev_info_args tmp;
        char mp[BTRFS_PATH_NAME_MAX + 1];
        DIR *dirstream = NULL;
 
@@ -2012,6 +2014,19 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
                ret = search_chunk_tree_for_fs_info(fd, fi_args);
                if (ret)
                        goto out;
+
+               /*
+                * search_chunk_tree_for_fs_info() will lacks the devid 0
+                * so manual probe for it here.
+                */
+               ret = get_device_info(fd, 0, &tmp);
+               if (!ret) {
+                       fi_args->num_devices++;
+                       ndevs++;
+                       replacing = 1;
+                       if (i == 0)
+                               i++;
+               }
        }
 
        if (!fi_args->num_devices)
@@ -2023,6 +2038,8 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args,
                goto out;
        }
 
+       if (replacing)
+               memcpy(di_args, &tmp, sizeof(tmp));
        for (; i <= fi_args->max_id; ++i) {
                ret = get_device_info(fd, i, &di_args[ndevs]);
                if (ret == -ENODEV)