Skip to content
  • Steven Rostedt (Red Hat)'s avatar
    ring-buffer: Prevent overflow of size in ring_buffer_resize() · 59643d15
    Steven Rostedt (Red Hat) authored
    If the size passed to ring_buffer_resize() is greater than MAX_LONG - BUF_PAGE_SIZE
    then the DIV_ROUND_UP() will return zero.
    
    Here's the details:
    
      # echo 18014398509481980 > /sys/kernel/debug/tracing/buffer_size_kb
    
    tracing_entries_write() processes this and converts kb to bytes.
    
     18014398509481980 << 10 = 18446744073709547520
    
    and this is passed to ring_buffer_resize() as unsigned long size.
    
     size = DIV_ROUND_UP(size, BUF_PAGE_SIZE);
    
    Where DIV_ROUND_UP(a, b) is (a + b - 1)/b
    
    BUF_PAGE_SIZE is 4080 and here
    
     18446744073709547520 + 4080 - 1 = 18446744073709551599
    
    where 18446744073709551599 is still smaller than 2^64
    
     2^64 - 18446744073709551599 = 17
    
    But now 18446744073709551599 / 4080 = 4521260802379792
    
    and size = size * 4080 = 18446744073709551360
    
    This is checked to make sure its still greater than 2 * 4080,
    which it is.
    
    Then we convert to the number of buffer pages needed.
    
     nr_page = DIV_ROUND_UP(size, BUF_PAGE_SIZE)
    
    but this time size is 18446744073709551360 and
    
     2^64 - (18446744073709551360 + 4080 - 1) = -3823
    
    Thus it overflows and the resulting number is less than 4080, which makes
    
      3823 / 4080 = 0
    
    an nr_pages is set to this. As we already checked against the minimum that
    nr_pages may be, this causes the logic to fail as well, and we crash the
    kernel.
    
    There's no reason to have the two DIV_ROUND_UP() (that's just result of
    historical code changes), clean up the code and fix this bug.
    
    Cc: stable@vger.kernel.org # 3.5+
    Fixes: 83f40318
    
     ("ring-buffer: Make removal of ring buffer pages atomic")
    Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
    59643d15