dm bufio: fix integer overflow when limiting maximum cache size
authorEric Biggers <ebiggers@google.com>
Thu, 16 Nov 2017 00:38:09 +0000 (16:38 -0800)
committerBen Hutchings <ben@decadent.org.uk>
Tue, 13 Feb 2018 18:32:17 +0000 (18:32 +0000)
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>
[bwh: Backported to 3.2: keep open-coded VMALLOC_TOTAL]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
drivers/md/dm-bufio.c

index 3bf7267..ec24a1c 100644 (file)
@@ -839,7 +839,8 @@ static void __get_memory_limit(struct dm_bufio_client *c,
                buffers = DM_BUFIO_MIN_BUFFERS;
 
        *limit_buffers = buffers;
                buffers = DM_BUFIO_MIN_BUFFERS;
 
        *limit_buffers = buffers;
-       *threshold_buffers = buffers * DM_BUFIO_WRITEBACK_PERCENT / 100;
+       *threshold_buffers = mult_frac(buffers,
+                                      DM_BUFIO_WRITEBACK_PERCENT, 100);
 }
 
 /*
 }
 
 /*
@@ -1620,19 +1621,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);
 
        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
 
        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_END - VMALLOC_START, DM_BUFIO_VMALLOC_PERCENT, 100))
+               mem = mult_frac(VMALLOC_END - VMALLOC_START, DM_BUFIO_VMALLOC_PERCENT, 100);
 #endif
 
        dm_bufio_default_cache_size = mem;
 #endif
 
        dm_bufio_default_cache_size = mem;