lkml.org 
[lkml]   [2018]   [Mar]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.4 057/134] iommu/iova: Fix underflow bug in __alloc_and_insert_iova_range
    Date
    4.4-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    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>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/iommu/iova.c | 2 +-
    1 file changed, 1 insertion(+), 1 deletion(-)

    --- a/drivers/iommu/iova.c
    +++ b/drivers/iommu/iova.c
    @@ -126,7 +126,7 @@ static int __alloc_and_insert_iova_range
    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);

    \
     
     \ /
      Last update: 2018-03-19 21:24    [W:4.263 / U:1.584 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site