CVE-2019-18885 (C, published 2019-11-14T14:15Z)
Description: value: fs/btrfs/volumes.c in the Linux kernel before 5.1 allows a btrfs_verify_dev_extents NULL pointer dereference via a crafted btrfs image because fs_devices->devices is mishandled within find_device, aka CID-09ba3bc9dd15.
Severity: LOW (CVSS v3 base score: 5.5)
Weakness: CWE-476 \u2014 NULL Pointer Dereference
The product dereferences a pointer that it expects to be valid but is NULL.
Repository: https://github.com/torvalds/linux
Files changed:
fs/btrfs/dev-replace.c
fs/btrfs/ioctl.c
fs/btrfs/scrub.c
fs/btrfs/volumes.c
fs/btrfs/volumes.h
Commit message: btrfs: merge btrfs_find_device and find_device

Both btrfs_find_device() and find_device() does the same thing except
that the latter does not take the seed device onto account in the device
scanning context. We can merge them.

Signed-off-by: Anand Jain <anand.jain@oracle.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>


Vulnerable code:
							src_devid, NULL, NULL);
							NULL, NULL);
	device = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL);
				NULL);
	dev = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL);
	dev = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL);
/*
 * Find a device specified by @devid or @uuid in the list of @fs_devices, or
 * return NULL.
 *
 * If devid and uuid are both specified, the match must be exact, otherwise
 * only devid is used.
 */
static struct btrfs_device *find_device(struct btrfs_fs_devices *fs_devices,
		u64 devid, const u8 *uuid)
{
	struct btrfs_device *dev;

	list_for_each_entry(dev, &fs_devices->devices, dev_list) {
		if (dev->devid == devid &&
		    (!uuid || !memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE))) {
			return dev;
		}
	}
	return NULL;
}

		device = find_device(fs_devices, devid,
				disk_super->dev_item.uuid);
					   disk_super->metadata_uuid);
					   disk_super->fsid);
					   NULL);
					   fs_uuid);
				       u64 devid, u8 *uuid, u8 *fsid)
			device = find_device(fs_devices, devid, uuid);
			if (device)
				return device;
		fs_devices = fs_devices->seed;
							devid, uuid, NULL);
				   fs_uuid);
	dev = btrfs_find_device(fs_info->fs_devices, stats->devid, NULL, NULL);
	dev = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL);
		dev = find_device(fs_info->fs_devices->seed, devid, NULL);
				       u64 devid, u8 *uuid, u8 *fsid);

Fixed code:
						src_devid, NULL, NULL, true);
							NULL, NULL, true);
	device = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL, true);
				NULL, true);
	dev = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL, true);
	dev = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL, true);
		device = btrfs_find_device(fs_devices, devid,
				disk_super->dev_item.uuid, NULL, false);
					   disk_super->metadata_uuid, true);
					   disk_super->fsid, true);
					   NULL, true);
					   fs_uuid, true);
/*
 * Find a device specified by @devid or @uuid in the list of @fs_devices, or
 * return NULL.
 *
 * If devid and uuid are both specified, the match must be exact, otherwise
 * only devid is used.
 *
 * If @seed is true, traverse through the seed devices.
 */
				       u64 devid, u8 *uuid, u8 *fsid,
				       bool seed)
			list_for_each_entry(device, &fs_devices->devices,
					    dev_list) {
				if (device->devid == devid &&
				    (!uuid || memcmp(device->uuid, uuid,
						     BTRFS_UUID_SIZE) == 0))
					return device;
			}
		if (seed)
			fs_devices = fs_devices->seed;
		else
			return NULL;
							devid, uuid, NULL, true);
				   fs_uuid, true);
	dev = btrfs_find_device(fs_info->fs_devices, stats->devid, NULL, NULL,
				true);
	dev = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL, true);
		dev = btrfs_find_device(fs_info->fs_devices->seed, devid, NULL,
					NULL, false);
				       u64 devid, u8 *uuid, u8 *fsid, bool seed);