| From eb6f8ae911692ea8b348bf4c92399d1f7ed7c264 Mon Sep 17 00:00:00 2001 |
| From: "Matthew Wilcox (Oracle)" <willy@infradead.org> |
| Date: Fri, 31 Jan 2020 05:07:55 -0500 |
| Subject: [PATCH] XArray: Fix xa_find_next for large multi-index entries |
| |
| commit bd40b17ca49d7d110adf456e647701ce74de2241 upstream. |
| |
| Coverity pointed out that xas_sibling() was shifting xa_offset without |
| promoting it to an unsigned long first, so the shift could cause an |
| overflow and we'd get the wrong answer. The fix is obvious, and the |
| new test-case provokes UBSAN to report an error: |
| runtime error: shift exponent 60 is too large for 32-bit type 'int' |
| |
| Fixes: 19c30f4dd092 ("XArray: Fix xa_find_after with multi-index entries") |
| Reported-by: Bjorn Helgaas <bhelgaas@google.com> |
| Reported-by: Kees Cook <keescook@chromium.org> |
| Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org> |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/lib/test_xarray.c b/lib/test_xarray.c |
| index 55c14e8c8859..8c7d7a8468b8 100644 |
| --- a/lib/test_xarray.c |
| +++ b/lib/test_xarray.c |
| @@ -12,6 +12,9 @@ |
| static unsigned int tests_run; |
| static unsigned int tests_passed; |
| |
| +static const unsigned int order_limit = |
| + IS_ENABLED(CONFIG_XARRAY_MULTI) ? BITS_PER_LONG : 1; |
| + |
| #ifndef XA_DEBUG |
| # ifdef __KERNEL__ |
| void xa_dump(const struct xarray *xa) { } |
| @@ -959,6 +962,20 @@ static noinline void check_multi_find_2(struct xarray *xa) |
| } |
| } |
| |
| +static noinline void check_multi_find_3(struct xarray *xa) |
| +{ |
| + unsigned int order; |
| + |
| + for (order = 5; order < order_limit; order++) { |
| + unsigned long index = 1UL << (order - 5); |
| + |
| + XA_BUG_ON(xa, !xa_empty(xa)); |
| + xa_store_order(xa, 0, order - 4, xa_mk_index(0), GFP_KERNEL); |
| + XA_BUG_ON(xa, xa_find_after(xa, &index, ULONG_MAX, XA_PRESENT)); |
| + xa_erase_index(xa, 0); |
| + } |
| +} |
| + |
| static noinline void check_find_1(struct xarray *xa) |
| { |
| unsigned long i, j, k; |
| @@ -1081,6 +1098,7 @@ static noinline void check_find(struct xarray *xa) |
| for (i = 2; i < 10; i++) |
| check_multi_find_1(xa, i); |
| check_multi_find_2(xa); |
| + check_multi_find_3(xa); |
| } |
| |
| /* See find_swap_entry() in mm/shmem.c */ |
| diff --git a/lib/xarray.c b/lib/xarray.c |
| index 1d9fab7db8da..acd1fad2e862 100644 |
| --- a/lib/xarray.c |
| +++ b/lib/xarray.c |
| @@ -1839,7 +1839,8 @@ static bool xas_sibling(struct xa_state *xas) |
| if (!node) |
| return false; |
| mask = (XA_CHUNK_SIZE << node->shift) - 1; |
| - return (xas->xa_index & mask) > (xas->xa_offset << node->shift); |
| + return (xas->xa_index & mask) > |
| + ((unsigned long)xas->xa_offset << node->shift); |
| } |
| |
| /** |
| -- |
| 2.7.4 |
| |