lkml.org 
[lkml]   [2018]   [Mar]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH AUTOSEL for 3.18 33/63] iommu/iova: Fix underflow bug in __alloc_and_insert_iova_range
    Date
    From: Nate Watterson <nwatters@codeaurora.org>

    [ Upstream commit 5016bdb796b3726eec043ca0ce3be981f712c756 ]

    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: Nate Watterson <nwatters@codeaurora.org>
    Signed-off-by: Joerg Roedel <jroedel@suse.de>
    Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
    ---
    drivers/iommu/iova.c | 2 +-
    1 file changed, 1 insertion(+), 1 deletion(-)

    diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
    index f6b17e6af2fb..c9e54a0b4bd1 100644
    --- a/drivers/iommu/iova.c
    +++ b/drivers/iommu/iova.c
    @@ -118,7 +118,7 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
    break; /* found a free slot */
    }
    adjust_limit_pfn:
    - limit_pfn = curr_iova->pfn_lo - 1;
    + limit_pfn = curr_iova->pfn_lo ? (curr_iova->pfn_lo - 1) : 0;
    move_left:
    prev = curr;
    curr = rb_prev(curr);
    --
    2.14.1
    \
     
     \ /
      Last update: 2018-03-03 23:50    [W:4.344 / U:0.284 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site