| From 74d4108d9e681dbbe4a2940ed8fdff1f6868184c Mon Sep 17 00:00:00 2001 |
| From: Eric Biggers <ebiggers@google.com> |
| Date: Wed, 15 Nov 2017 16:38:09 -0800 |
| Subject: dm bufio: fix integer overflow when limiting maximum cache size |
| |
| From: Eric Biggers <ebiggers@google.com> |
| |
| commit 74d4108d9e681dbbe4a2940ed8fdff1f6868184c upstream. |
| |
| The default max_cache_size_bytes for dm-bufio is meant to be the lesser |
| of 25% of the size of the vmalloc area and 2% of the size of lowmem. |
| However, on 32-bit systems the intermediate result in the expression |
| |
| (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100 |
| |
| overflows, causing the wrong result to be computed. For example, on a |
| 32-bit system where the vmalloc area is 520093696 bytes, the result is |
| 1174405 rather than the expected 130023424, which makes the maximum |
| cache size much too small (far less than 2% of lowmem). This causes |
| severe performance problems for dm-verity users on affected systems. |
| |
| Fix this by using mult_frac() to correctly multiply by a percentage. Do |
| this for all places in dm-bufio that multiply by a percentage. Also |
| replace (VMALLOC_END - VMALLOC_START) with VMALLOC_TOTAL, which contrary |
| to the comment is now defined in include/linux/vmalloc.h. |
| |
| Depends-on: 9993bc635 ("sched/x86: Fix overflow in cyc2ns_offset") |
| Fixes: 95d402f057f2 ("dm: add bufio") |
| Signed-off-by: Eric Biggers <ebiggers@google.com> |
| Signed-off-by: Mike Snitzer <snitzer@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/md/dm-bufio.c | 15 ++++++--------- |
| 1 file changed, 6 insertions(+), 9 deletions(-) |
| |
| --- a/drivers/md/dm-bufio.c |
| +++ b/drivers/md/dm-bufio.c |
| @@ -876,7 +876,8 @@ static void __get_memory_limit(struct dm |
| buffers = c->minimum_buffers; |
| |
| *limit_buffers = buffers; |
| - *threshold_buffers = buffers * DM_BUFIO_WRITEBACK_PERCENT / 100; |
| + *threshold_buffers = mult_frac(buffers, |
| + DM_BUFIO_WRITEBACK_PERCENT, 100); |
| } |
| |
| /* |
| @@ -1764,19 +1765,15 @@ static int __init dm_bufio_init(void) |
| memset(&dm_bufio_caches, 0, sizeof dm_bufio_caches); |
| memset(&dm_bufio_cache_names, 0, sizeof dm_bufio_cache_names); |
| |
| - mem = (__u64)((totalram_pages - totalhigh_pages) * |
| - DM_BUFIO_MEMORY_PERCENT / 100) << PAGE_SHIFT; |
| + mem = (__u64)mult_frac(totalram_pages - totalhigh_pages, |
| + DM_BUFIO_MEMORY_PERCENT, 100) << PAGE_SHIFT; |
| |
| if (mem > ULONG_MAX) |
| mem = ULONG_MAX; |
| |
| #ifdef CONFIG_MMU |
| - /* |
| - * Get the size of vmalloc space the same way as VMALLOC_TOTAL |
| - * in fs/proc/internal.h |
| - */ |
| - if (mem > (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100) |
| - mem = (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100; |
| + if (mem > mult_frac(VMALLOC_TOTAL, DM_BUFIO_VMALLOC_PERCENT, 100)) |
| + mem = mult_frac(VMALLOC_TOTAL, DM_BUFIO_VMALLOC_PERCENT, 100); |
| #endif |
| |
| dm_bufio_default_cache_size = mem; |