mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-24 11:25:43 +00:00
iommufd for 6.8 rc
Four syzkaller found bugs: - Corruption during error unwind in iommufd_access_change_ioas() - Overlapping IDs in the test suite due to out of order destruction - Missing locking for access->ioas in the test suite - False failures in the test suite validation logic with huge pages -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQRRRCHOFoQz/8F5bUaFwuHvBreFYQUCZeH0gQAKCRCFwuHvBreF YcC5AP91VuNxoExC6Zx9c8i1U2SR5IbvVAHT572qgdhaKoVzugEAtfKJA2ELheL8 s1F+TDOo8fR/aawIAPzK5GCuHc5oIwg= =S44j -----END PGP SIGNATURE----- Merge tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd Pull iommufd fixes from Jason Gunthorpe: "Four syzkaller found bugs: - Corruption during error unwind in iommufd_access_change_ioas() - Overlapping IDs in the test suite due to out of order destruction - Missing locking for access->ioas in the test suite - False failures in the test suite validation logic with huge pages" * tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd: iommufd/selftest: Don't check map/unmap pairing with HUGE_PAGES iommufd: Fix protection fault in iommufd_test_syz_conv_iova iommufd/selftest: Fix mock_dev_num bug iommufd: Fix iopt_access_list_id overwrite bug
This commit is contained in:
commit
e613c90dda
2 changed files with 54 additions and 24 deletions
|
@ -1330,20 +1330,23 @@ int iopt_disable_large_pages(struct io_pagetable *iopt)
|
|||
|
||||
int iopt_add_access(struct io_pagetable *iopt, struct iommufd_access *access)
|
||||
{
|
||||
u32 new_id;
|
||||
int rc;
|
||||
|
||||
down_write(&iopt->domains_rwsem);
|
||||
down_write(&iopt->iova_rwsem);
|
||||
rc = xa_alloc(&iopt->access_list, &access->iopt_access_list_id, access,
|
||||
xa_limit_16b, GFP_KERNEL_ACCOUNT);
|
||||
rc = xa_alloc(&iopt->access_list, &new_id, access, xa_limit_16b,
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
|
||||
rc = iopt_calculate_iova_alignment(iopt);
|
||||
if (rc) {
|
||||
xa_erase(&iopt->access_list, access->iopt_access_list_id);
|
||||
xa_erase(&iopt->access_list, new_id);
|
||||
goto out_unlock;
|
||||
}
|
||||
access->iopt_access_list_id = new_id;
|
||||
|
||||
out_unlock:
|
||||
up_write(&iopt->iova_rwsem);
|
||||
|
|
|
@ -36,7 +36,7 @@ static struct mock_bus_type iommufd_mock_bus_type = {
|
|||
},
|
||||
};
|
||||
|
||||
static atomic_t mock_dev_num;
|
||||
static DEFINE_IDA(mock_dev_ida);
|
||||
|
||||
enum {
|
||||
MOCK_DIRTY_TRACK = 1,
|
||||
|
@ -63,8 +63,8 @@ enum {
|
|||
* In syzkaller mode the 64 bit IOVA is converted into an nth area and offset
|
||||
* value. This has a much smaller randomization space and syzkaller can hit it.
|
||||
*/
|
||||
static unsigned long iommufd_test_syz_conv_iova(struct io_pagetable *iopt,
|
||||
u64 *iova)
|
||||
static unsigned long __iommufd_test_syz_conv_iova(struct io_pagetable *iopt,
|
||||
u64 *iova)
|
||||
{
|
||||
struct syz_layout {
|
||||
__u32 nth_area;
|
||||
|
@ -88,6 +88,21 @@ static unsigned long iommufd_test_syz_conv_iova(struct io_pagetable *iopt,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long iommufd_test_syz_conv_iova(struct iommufd_access *access,
|
||||
u64 *iova)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
mutex_lock(&access->ioas_lock);
|
||||
if (!access->ioas) {
|
||||
mutex_unlock(&access->ioas_lock);
|
||||
return 0;
|
||||
}
|
||||
ret = __iommufd_test_syz_conv_iova(&access->ioas->iopt, iova);
|
||||
mutex_unlock(&access->ioas_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
|
||||
unsigned int ioas_id, u64 *iova, u32 *flags)
|
||||
{
|
||||
|
@ -100,7 +115,7 @@ void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
|
|||
ioas = iommufd_get_ioas(ucmd->ictx, ioas_id);
|
||||
if (IS_ERR(ioas))
|
||||
return;
|
||||
*iova = iommufd_test_syz_conv_iova(&ioas->iopt, iova);
|
||||
*iova = __iommufd_test_syz_conv_iova(&ioas->iopt, iova);
|
||||
iommufd_put_object(ucmd->ictx, &ioas->obj);
|
||||
}
|
||||
|
||||
|
@ -123,6 +138,7 @@ enum selftest_obj_type {
|
|||
struct mock_dev {
|
||||
struct device dev;
|
||||
unsigned long flags;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct selftest_obj {
|
||||
|
@ -430,20 +446,27 @@ static size_t mock_domain_unmap_pages(struct iommu_domain *domain,
|
|||
|
||||
/*
|
||||
* iommufd generates unmaps that must be a strict
|
||||
* superset of the map's performend So every starting
|
||||
* IOVA should have been an iova passed to map, and the
|
||||
* superset of the map's performend So every
|
||||
* starting/ending IOVA should have been an iova passed
|
||||
* to map.
|
||||
*
|
||||
* First IOVA must be present and have been a first IOVA
|
||||
* passed to map_pages
|
||||
* This simple logic doesn't work when the HUGE_PAGE is
|
||||
* turned on since the core code will automatically
|
||||
* switch between the two page sizes creating a break in
|
||||
* the unmap calls. The break can land in the middle of
|
||||
* contiguous IOVA.
|
||||
*/
|
||||
if (first) {
|
||||
WARN_ON(ent && !(xa_to_value(ent) &
|
||||
MOCK_PFN_START_IOVA));
|
||||
first = false;
|
||||
if (!(domain->pgsize_bitmap & MOCK_HUGE_PAGE_SIZE)) {
|
||||
if (first) {
|
||||
WARN_ON(ent && !(xa_to_value(ent) &
|
||||
MOCK_PFN_START_IOVA));
|
||||
first = false;
|
||||
}
|
||||
if (pgcount == 1 &&
|
||||
cur + MOCK_IO_PAGE_SIZE == pgsize)
|
||||
WARN_ON(ent && !(xa_to_value(ent) &
|
||||
MOCK_PFN_LAST_IOVA));
|
||||
}
|
||||
if (pgcount == 1 && cur + MOCK_IO_PAGE_SIZE == pgsize)
|
||||
WARN_ON(ent && !(xa_to_value(ent) &
|
||||
MOCK_PFN_LAST_IOVA));
|
||||
|
||||
iova += MOCK_IO_PAGE_SIZE;
|
||||
ret += MOCK_IO_PAGE_SIZE;
|
||||
|
@ -631,7 +654,7 @@ static void mock_dev_release(struct device *dev)
|
|||
{
|
||||
struct mock_dev *mdev = container_of(dev, struct mock_dev, dev);
|
||||
|
||||
atomic_dec(&mock_dev_num);
|
||||
ida_free(&mock_dev_ida, mdev->id);
|
||||
kfree(mdev);
|
||||
}
|
||||
|
||||
|
@ -653,8 +676,12 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags)
|
|||
mdev->dev.release = mock_dev_release;
|
||||
mdev->dev.bus = &iommufd_mock_bus_type.bus;
|
||||
|
||||
rc = dev_set_name(&mdev->dev, "iommufd_mock%u",
|
||||
atomic_inc_return(&mock_dev_num));
|
||||
rc = ida_alloc(&mock_dev_ida, GFP_KERNEL);
|
||||
if (rc < 0)
|
||||
goto err_put;
|
||||
mdev->id = rc;
|
||||
|
||||
rc = dev_set_name(&mdev->dev, "iommufd_mock%u", mdev->id);
|
||||
if (rc)
|
||||
goto err_put;
|
||||
|
||||
|
@ -1156,7 +1183,7 @@ static int iommufd_test_access_pages(struct iommufd_ucmd *ucmd,
|
|||
}
|
||||
|
||||
if (flags & MOCK_FLAGS_ACCESS_SYZ)
|
||||
iova = iommufd_test_syz_conv_iova(&staccess->access->ioas->iopt,
|
||||
iova = iommufd_test_syz_conv_iova(staccess->access,
|
||||
&cmd->access_pages.iova);
|
||||
|
||||
npages = (ALIGN(iova + length, PAGE_SIZE) -
|
||||
|
@ -1258,8 +1285,8 @@ static int iommufd_test_access_rw(struct iommufd_ucmd *ucmd,
|
|||
}
|
||||
|
||||
if (flags & MOCK_FLAGS_ACCESS_SYZ)
|
||||
iova = iommufd_test_syz_conv_iova(&staccess->access->ioas->iopt,
|
||||
&cmd->access_rw.iova);
|
||||
iova = iommufd_test_syz_conv_iova(staccess->access,
|
||||
&cmd->access_rw.iova);
|
||||
|
||||
rc = iommufd_access_rw(staccess->access, iova, tmp, length, flags);
|
||||
if (rc)
|
||||
|
|
Loading…
Reference in a new issue