Skip to content

Commit 52f5285

Browse files
committed
iommufd: Add additional invariant assertions
These are on performance paths so we protect them using the CONFIG_IOMMUFD_TEST to not take a hit during normal operation. These are useful when running the test suite and syzkaller to find data structure inconsistencies early. Link: https://lore.kernel.org/r/[email protected] Tested-by: Yi Liu <[email protected]> Tested-by: Matthew Rosato <[email protected]> # s390 Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent e26eed4 commit 52f5285

File tree

4 files changed

+70
-2
lines changed

4 files changed

+70
-2
lines changed

drivers/iommu/iommufd/device.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,11 @@ int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
625625
struct iopt_area *area;
626626
int rc;
627627

628+
/* Driver's ops don't support pin_pages */
629+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST) &&
630+
WARN_ON(access->iova_alignment != PAGE_SIZE || !access->ops->unmap))
631+
return -EINVAL;
632+
628633
if (!length)
629634
return -EINVAL;
630635
if (check_add_overflow(iova, length - 1, &last_iova))

drivers/iommu/iommufd/io_pagetable.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,11 @@ static int iopt_alloc_area_pages(struct io_pagetable *iopt,
251251
(uintptr_t)elm->pages->uptr + elm->start_byte, length);
252252
if (rc)
253253
goto out_unlock;
254+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST) &&
255+
WARN_ON(iopt_check_iova(iopt, *dst_iova, length))) {
256+
rc = -EINVAL;
257+
goto out_unlock;
258+
}
254259
} else {
255260
rc = iopt_check_iova(iopt, *dst_iova, length);
256261
if (rc)
@@ -277,6 +282,8 @@ static int iopt_alloc_area_pages(struct io_pagetable *iopt,
277282

278283
static void iopt_abort_area(struct iopt_area *area)
279284
{
285+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
286+
WARN_ON(area->pages);
280287
if (area->iopt) {
281288
down_write(&area->iopt->iova_rwsem);
282289
interval_tree_remove(&area->node, &area->iopt->area_itree);
@@ -642,6 +649,9 @@ void iopt_destroy_table(struct io_pagetable *iopt)
642649
{
643650
struct interval_tree_node *node;
644651

652+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
653+
iopt_remove_reserved_iova(iopt, NULL);
654+
645655
while ((node = interval_tree_iter_first(&iopt->allowed_itree, 0,
646656
ULONG_MAX))) {
647657
interval_tree_remove(node, &iopt->allowed_itree);
@@ -688,6 +698,8 @@ static void iopt_unfill_domain(struct io_pagetable *iopt,
688698
continue;
689699

690700
mutex_lock(&pages->mutex);
701+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
702+
WARN_ON(!area->storage_domain);
691703
if (area->storage_domain == domain)
692704
area->storage_domain = storage_domain;
693705
mutex_unlock(&pages->mutex);
@@ -792,6 +804,16 @@ static int iopt_check_iova_alignment(struct io_pagetable *iopt,
792804
(iopt_area_length(area) & align_mask) ||
793805
(area->page_offset & align_mask))
794806
return -EADDRINUSE;
807+
808+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST)) {
809+
struct iommufd_access *access;
810+
unsigned long index;
811+
812+
xa_for_each(&iopt->access_list, index, access)
813+
if (WARN_ON(access->iova_alignment >
814+
new_iova_alignment))
815+
return -EADDRINUSE;
816+
}
795817
return 0;
796818
}
797819

drivers/iommu/iommufd/io_pagetable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ static inline size_t iopt_area_length(struct iopt_area *area)
101101
static inline unsigned long iopt_area_start_byte(struct iopt_area *area,
102102
unsigned long iova)
103103
{
104+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
105+
WARN_ON(iova < iopt_area_iova(area) ||
106+
iova > iopt_area_last_iova(area));
104107
return (iova - iopt_area_iova(area)) + area->page_offset +
105108
iopt_area_index(area) * PAGE_SIZE;
106109
}

drivers/iommu/iommufd/pages.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,20 @@ void interval_tree_double_span_iter_next(
162162

163163
static void iopt_pages_add_npinned(struct iopt_pages *pages, size_t npages)
164164
{
165-
pages->npinned += npages;
165+
int rc;
166+
167+
rc = check_add_overflow(pages->npinned, npages, &pages->npinned);
168+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
169+
WARN_ON(rc || pages->npinned > pages->npages);
166170
}
167171

168172
static void iopt_pages_sub_npinned(struct iopt_pages *pages, size_t npages)
169173
{
170-
pages->npinned -= npages;
174+
int rc;
175+
176+
rc = check_sub_overflow(pages->npinned, npages, &pages->npinned);
177+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
178+
WARN_ON(rc || pages->npinned > pages->npages);
171179
}
172180

173181
static void iopt_pages_err_unpin(struct iopt_pages *pages,
@@ -189,6 +197,9 @@ static void iopt_pages_err_unpin(struct iopt_pages *pages,
189197
static unsigned long iopt_area_index_to_iova(struct iopt_area *area,
190198
unsigned long index)
191199
{
200+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
201+
WARN_ON(index < iopt_area_index(area) ||
202+
index > iopt_area_last_index(area));
192203
index -= iopt_area_index(area);
193204
if (index == 0)
194205
return iopt_area_iova(area);
@@ -198,6 +209,9 @@ static unsigned long iopt_area_index_to_iova(struct iopt_area *area,
198209
static unsigned long iopt_area_index_to_iova_last(struct iopt_area *area,
199210
unsigned long index)
200211
{
212+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
213+
WARN_ON(index < iopt_area_index(area) ||
214+
index > iopt_area_last_index(area));
201215
if (index == iopt_area_last_index(area))
202216
return iopt_area_last_iova(area);
203217
return iopt_area_iova(area) - area->page_offset +
@@ -286,6 +300,8 @@ static void batch_skip_carry(struct pfn_batch *batch, unsigned int skip_pfns)
286300
{
287301
if (!batch->total_pfns)
288302
return;
303+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
304+
WARN_ON(batch->total_pfns != batch->npfns[0]);
289305
skip_pfns = min(batch->total_pfns, skip_pfns);
290306
batch->pfns[0] += skip_pfns;
291307
batch->npfns[0] -= skip_pfns;
@@ -301,6 +317,8 @@ static int __batch_init(struct pfn_batch *batch, size_t max_pages, void *backup,
301317
batch->pfns = temp_kmalloc(&size, backup, backup_len);
302318
if (!batch->pfns)
303319
return -ENOMEM;
320+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST) && WARN_ON(size < elmsz))
321+
return -EINVAL;
304322
batch->array_size = size / elmsz;
305323
batch->npfns = (u32 *)(batch->pfns + batch->array_size);
306324
batch_clear(batch);
@@ -429,6 +447,10 @@ static int batch_iommu_map_small(struct iommu_domain *domain,
429447
unsigned long start_iova = iova;
430448
int rc;
431449

450+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
451+
WARN_ON(paddr % PAGE_SIZE || iova % PAGE_SIZE ||
452+
size % PAGE_SIZE);
453+
432454
while (size) {
433455
rc = iommu_map(domain, iova, paddr, PAGE_SIZE, prot);
434456
if (rc)
@@ -718,6 +740,10 @@ static int pfn_reader_user_pin(struct pfn_reader_user *user,
718740
uintptr_t uptr;
719741
long rc;
720742

743+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST) &&
744+
WARN_ON(last_index < start_index))
745+
return -EINVAL;
746+
721747
if (!user->upages) {
722748
/* All undone in pfn_reader_destroy() */
723749
user->upages_len =
@@ -956,6 +982,10 @@ static int pfn_reader_fill_span(struct pfn_reader *pfns)
956982
struct iopt_area *area;
957983
int rc;
958984

985+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST) &&
986+
WARN_ON(span->last_used < start_index))
987+
return -EINVAL;
988+
959989
if (span->is_used == 1) {
960990
batch_from_xarray(&pfns->batch, &pfns->pages->pinned_pfns,
961991
start_index, span->last_used);
@@ -1008,6 +1038,10 @@ static int pfn_reader_next(struct pfn_reader *pfns)
10081038
while (pfns->batch_end_index != pfns->last_index + 1) {
10091039
unsigned int npfns = pfns->batch.total_pfns;
10101040

1041+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST) &&
1042+
WARN_ON(interval_tree_double_span_iter_done(&pfns->span)))
1043+
return -EINVAL;
1044+
10111045
rc = pfn_reader_fill_span(pfns);
10121046
if (rc)
10131047
return rc;
@@ -1091,6 +1125,10 @@ static int pfn_reader_first(struct pfn_reader *pfns, struct iopt_pages *pages,
10911125
{
10921126
int rc;
10931127

1128+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST) &&
1129+
WARN_ON(last_index < start_index))
1130+
return -EINVAL;
1131+
10941132
rc = pfn_reader_init(pfns, pages, start_index, last_index);
10951133
if (rc)
10961134
return rc;

0 commit comments

Comments
 (0)