diff options
| author | Tvrtko Ursulin <tvrtko.ursulin@intel.com> | 2017-08-03 05:13:12 -0400 |
|---|---|---|
| committer | Tvrtko Ursulin <tvrtko.ursulin@intel.com> | 2017-09-07 05:48:28 -0400 |
| commit | c125906b839b794c580a5de911de65bd2c63aaee (patch) | |
| tree | d0a71a8b3ceb1d76b0b06387436bb7fab2801ef2 /lib | |
| parent | c4860ad60564838994b74e7ee7dd12ceeda0f520 (diff) | |
lib/scatterlist: Avoid potential scatterlist entry overflow
Since the scatterlist length field is an unsigned int, make
sure that sg_alloc_table_from_pages does not overflow it while
coalescing pages to a single entry.
v2: Drop reference to future use. Use UINT_MAX.
v3: max_segment must be page aligned.
v4: Do not rely on compiler to optimise out the rounddown.
(Joonas Lahtinen)
v5: Simplified loops and use post-increments rather than
pre-increments. Use PAGE_MASK and fix comment typo.
(Andy Shevchenko)
v6: Commit spelling fix.
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: linux-kernel@vger.kernel.org
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20170803091312.22875-1-tvrtko.ursulin@linux.intel.com
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/scatterlist.c | 31 |
1 files changed, 20 insertions, 11 deletions
diff --git a/lib/scatterlist.c b/lib/scatterlist.c index dee0c5004e2f..7b2e74da2c44 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c | |||
| @@ -394,17 +394,22 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, | |||
| 394 | unsigned int offset, unsigned long size, | 394 | unsigned int offset, unsigned long size, |
| 395 | gfp_t gfp_mask) | 395 | gfp_t gfp_mask) |
| 396 | { | 396 | { |
| 397 | unsigned int chunks; | 397 | const unsigned int max_segment = SCATTERLIST_MAX_SEGMENT; |
| 398 | unsigned int i; | 398 | unsigned int chunks, cur_page, seg_len, i; |
| 399 | unsigned int cur_page; | ||
| 400 | int ret; | 399 | int ret; |
| 401 | struct scatterlist *s; | 400 | struct scatterlist *s; |
| 402 | 401 | ||
| 403 | /* compute number of contiguous chunks */ | 402 | /* compute number of contiguous chunks */ |
| 404 | chunks = 1; | 403 | chunks = 1; |
| 405 | for (i = 1; i < n_pages; ++i) | 404 | seg_len = 0; |
| 406 | if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) | 405 | for (i = 1; i < n_pages; i++) { |
| 407 | ++chunks; | 406 | seg_len += PAGE_SIZE; |
| 407 | if (seg_len >= max_segment || | ||
| 408 | page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) { | ||
| 409 | chunks++; | ||
| 410 | seg_len = 0; | ||
| 411 | } | ||
| 412 | } | ||
| 408 | 413 | ||
| 409 | ret = sg_alloc_table(sgt, chunks, gfp_mask); | 414 | ret = sg_alloc_table(sgt, chunks, gfp_mask); |
| 410 | if (unlikely(ret)) | 415 | if (unlikely(ret)) |
| @@ -413,17 +418,21 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, | |||
| 413 | /* merging chunks and putting them into the scatterlist */ | 418 | /* merging chunks and putting them into the scatterlist */ |
| 414 | cur_page = 0; | 419 | cur_page = 0; |
| 415 | for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { | 420 | for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { |
| 416 | unsigned long chunk_size; | 421 | unsigned int j, chunk_size; |
| 417 | unsigned int j; | ||
| 418 | 422 | ||
| 419 | /* look for the end of the current chunk */ | 423 | /* look for the end of the current chunk */ |
| 420 | for (j = cur_page + 1; j < n_pages; ++j) | 424 | seg_len = 0; |
| 421 | if (page_to_pfn(pages[j]) != | 425 | for (j = cur_page + 1; j < n_pages; j++) { |
| 426 | seg_len += PAGE_SIZE; | ||
| 427 | if (seg_len >= max_segment || | ||
| 428 | page_to_pfn(pages[j]) != | ||
| 422 | page_to_pfn(pages[j - 1]) + 1) | 429 | page_to_pfn(pages[j - 1]) + 1) |
| 423 | break; | 430 | break; |
| 431 | } | ||
| 424 | 432 | ||
| 425 | chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset; | 433 | chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset; |
| 426 | sg_set_page(s, pages[cur_page], min(size, chunk_size), offset); | 434 | sg_set_page(s, pages[cur_page], |
| 435 | min_t(unsigned long, size, chunk_size), offset); | ||
| 427 | size -= chunk_size; | 436 | size -= chunk_size; |
| 428 | offset = 0; | 437 | offset = 0; |
| 429 | cur_page = j; | 438 | cur_page = j; |
