Skip to content
  • Nate Watterson's avatar
    iommu/iova: Fix underflow bug in __alloc_and_insert_iova_range · 5016bdb7
    Nate Watterson authored
    
    
    Normally, calling alloc_iova() using an iova_domain with insufficient
    pfns remaining between start_pfn and dma_limit will fail and return a
    NULL pointer. Unexpectedly, if such a "full" iova_domain contains an
    iova with pfn_lo == 0, the alloc_iova() call will instead succeed and
    return an iova containing invalid pfns.
    
    This is caused by an underflow bug in __alloc_and_insert_iova_range()
    that occurs after walking the "full" iova tree when the search ends
    at the iova with pfn_lo == 0 and limit_pfn is then adjusted to be just
    below that (-1). This (now huge) limit_pfn gives the impression that a
    vast amount of space is available between it and start_pfn and thus
    a new iova is allocated with the invalid pfn_hi value, 0xFFF.... .
    
    To rememdy this, a check is introduced to ensure that adjustments to
    limit_pfn will not underflow.
    
    This issue has been observed in the wild, and is easily reproduced with
    the following sample code.
    
    	struct iova_domain *iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
    	struct iova *rsvd_iova, *good_iova, *bad_iova;
    	unsigned long limit_pfn = 3;
    	unsigned long start_pfn = 1;
    	unsigned long va_size = 2;
    
    	init_iova_domain(iovad, SZ_4K, start_pfn, limit_pfn);
    	rsvd_iova = reserve_iova(iovad, 0, 0);
    	good_iova = alloc_iova(iovad, va_size, limit_pfn, true);
    	bad_iova = alloc_iova(iovad, va_size, limit_pfn, true);
    
    Prior to the patch, this yielded:
    	*rsvd_iova == {0, 0}   /* Expected */
    	*good_iova == {2, 3}   /* Expected */
    	*bad_iova  == {-2, -1} /* Oh no... */
    
    After the patch, bad_iova is NULL as expected since inadequate
    space remains between limit_pfn and start_pfn after allocating
    good_iova.
    
    Signed-off-by: default avatarNate Watterson <nwatters@codeaurora.org>
    Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
    5016bdb7