diff mbox series

[rc,1/2] iommufd: Do not allow creating areas without READ or WRITE

Message ID 1-v1-1211e1294c27+4b1-iommu_no_prot_jgg@nvidia.com
State New
Headers show
Series Fix maps created without READ or WRITE | expand

Commit Message

Jason Gunthorpe Aug. 22, 2024, 2:45 p.m. UTC
This results in passing 0 or just IOMMU_CACHE to iommu_map(). Most of
the page table formats don't like this:

  amdv1 - -EINVAL
  armv7s - returns 0, doesn't update mapped
  arm-lpae - returns 0 doesn't update mapped
  dart - returns 0, doesn't update mapped
  VT-D - returns -EINVAL

Unfortunately the three formats that return 0 cause serious problems:

 - Returning ret = but not uppdating mapped from domain->map_pages()
   causes an infinite loop in __iommu_map()

 - Not writing ioptes means that VFIO/iommufd have no way to recover them
   and we will have memory leaks and worse during unmap

Since almost nothing can support this, and it is a useless thing to do,
block it early in iommufd.

Cc: stable@kernel.org
Fixes: aad37e71d5c4 ("iommufd: IOCTLs for the io_pagetable")
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/iommufd/ioas.c            | 8 ++++++++
 tools/testing/selftests/iommu/iommufd.c | 6 +++---
 2 files changed, 11 insertions(+), 3 deletions(-)

Comments

Nicolin Chen Aug. 23, 2024, 4:29 p.m. UTC | #1
On Thu, Aug 22, 2024 at 11:45:54AM -0300, Jason Gunthorpe wrote:
> This results in passing 0 or just IOMMU_CACHE to iommu_map(). Most of
> the page table formats don't like this:
> 
>   amdv1 - -EINVAL
>   armv7s - returns 0, doesn't update mapped
>   arm-lpae - returns 0 doesn't update mapped
>   dart - returns 0, doesn't update mapped
>   VT-D - returns -EINVAL
> 
> Unfortunately the three formats that return 0 cause serious problems:
> 
>  - Returning ret = but not uppdating mapped from domain->map_pages()
>    causes an infinite loop in __iommu_map()
> 
>  - Not writing ioptes means that VFIO/iommufd have no way to recover them
>    and we will have memory leaks and worse during unmap
> 
> Since almost nothing can support this, and it is a useless thing to do,
> block it early in iommufd.
> 
> Cc: stable@kernel.org
> Fixes: aad37e71d5c4 ("iommufd: IOCTLs for the io_pagetable")
> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>

I also tried both patches with io-pgtable-arm and didn't see any
issue, since they tends to fix a corner case I think.

Nicolin
Tian, Kevin Aug. 26, 2024, 6:20 a.m. UTC | #2
> From: Jason Gunthorpe <jgg@nvidia.com>
> Sent: Thursday, August 22, 2024 10:46 PM
> 
> This results in passing 0 or just IOMMU_CACHE to iommu_map(). Most of
> the page table formats don't like this:
> 
>   amdv1 - -EINVAL
>   armv7s - returns 0, doesn't update mapped
>   arm-lpae - returns 0 doesn't update mapped
>   dart - returns 0, doesn't update mapped
>   VT-D - returns -EINVAL
> 
> Unfortunately the three formats that return 0 cause serious problems:
> 
>  - Returning ret = but not uppdating mapped from domain->map_pages()
>    causes an infinite loop in __iommu_map()
> 
>  - Not writing ioptes means that VFIO/iommufd have no way to recover them
>    and we will have memory leaks and worse during unmap
> 
> Since almost nothing can support this, and it is a useless thing to do,
> block it early in iommufd.
> 
> Cc: stable@kernel.org
> Fixes: aad37e71d5c4 ("iommufd: IOCTLs for the io_pagetable")
> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

Reviewed-by: Kevin Tian <kevin.tian@intel.com>
diff mbox series

Patch

diff --git a/drivers/iommu/iommufd/ioas.c b/drivers/iommu/iommufd/ioas.c
index 82428e44a837ca..2c4b2bb11e78ce 100644
--- a/drivers/iommu/iommufd/ioas.c
+++ b/drivers/iommu/iommufd/ioas.c
@@ -213,6 +213,10 @@  int iommufd_ioas_map(struct iommufd_ucmd *ucmd)
 	if (cmd->iova >= ULONG_MAX || cmd->length >= ULONG_MAX)
 		return -EOVERFLOW;
 
+	if (!(cmd->flags &
+	      (IOMMU_IOAS_MAP_WRITEABLE | IOMMU_IOAS_MAP_READABLE)))
+		return -EINVAL;
+
 	ioas = iommufd_get_ioas(ucmd->ictx, cmd->ioas_id);
 	if (IS_ERR(ioas))
 		return PTR_ERR(ioas);
@@ -253,6 +257,10 @@  int iommufd_ioas_copy(struct iommufd_ucmd *ucmd)
 	    cmd->dst_iova >= ULONG_MAX)
 		return -EOVERFLOW;
 
+	if (!(cmd->flags &
+	      (IOMMU_IOAS_MAP_WRITEABLE | IOMMU_IOAS_MAP_READABLE)))
+		return -EINVAL;
+
 	src_ioas = iommufd_get_ioas(ucmd->ictx, cmd->src_ioas_id);
 	if (IS_ERR(src_ioas))
 		return PTR_ERR(src_ioas);
diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c
index 6343f4053bd46e..4927b9add5add9 100644
--- a/tools/testing/selftests/iommu/iommufd.c
+++ b/tools/testing/selftests/iommu/iommufd.c
@@ -825,7 +825,7 @@  TEST_F(iommufd_ioas, copy_area)
 {
 	struct iommu_ioas_copy copy_cmd = {
 		.size = sizeof(copy_cmd),
-		.flags = IOMMU_IOAS_MAP_FIXED_IOVA,
+		.flags = IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE,
 		.dst_ioas_id = self->ioas_id,
 		.src_ioas_id = self->ioas_id,
 		.length = PAGE_SIZE,
@@ -1318,7 +1318,7 @@  TEST_F(iommufd_ioas, copy_sweep)
 {
 	struct iommu_ioas_copy copy_cmd = {
 		.size = sizeof(copy_cmd),
-		.flags = IOMMU_IOAS_MAP_FIXED_IOVA,
+		.flags = IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE,
 		.src_ioas_id = self->ioas_id,
 		.dst_iova = MOCK_APERTURE_START,
 		.length = MOCK_PAGE_SIZE,
@@ -1608,7 +1608,7 @@  TEST_F(iommufd_mock_domain, user_copy)
 	};
 	struct iommu_ioas_copy copy_cmd = {
 		.size = sizeof(copy_cmd),
-		.flags = IOMMU_IOAS_MAP_FIXED_IOVA,
+		.flags = IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE,
 		.dst_ioas_id = self->ioas_id,
 		.dst_iova = MOCK_APERTURE_START,
 		.length = BUFFER_SIZE,