Discarding a cached reader page after a concurrent ring buffer resize
uses the new global subbuf_order for the free_pages() call. This
mismatched order may crashes the kernel or leaks memory because the cached
page was allocated under the old size.

Save the actual free_page order alongside the page address to ensure we
always refer to the correct value and do not rely on the potentially
stalled cpu_buffer->subbuf_order value. The simplest is to make
free_page a buffer_data_read_page which already covers exactly what we
need: a page address and a page order.

Fixes: 8e7b58c27b3c ("ring-buffer: Just update the subbuffers when changing 
their allocation order")
Signed-off-by: Vincent Donnefort <[email protected]>

diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index ec4f5a0c93e8..b6fa258aafe2 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -507,7 +507,7 @@ struct ring_buffer_per_cpu {
        raw_spinlock_t                  reader_lock;    /* serialize readers */
        arch_spinlock_t                 lock;
        struct lock_class_key           lock_key;
-       struct buffer_data_page         *free_page;
+       struct buffer_data_read_page    free_page;
        unsigned long                   nr_pages;
        unsigned int                    current_context;
        struct list_head                *pages;
@@ -2631,7 +2631,7 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu 
*cpu_buffer)
                free_buffer_page(bpage);
        }
 
-       free_pages((unsigned long)cpu_buffer->free_page, 
cpu_buffer->buffer->subbuf_order);
+       free_pages((unsigned long)cpu_buffer->free_page.data, 
cpu_buffer->free_page.order);
 
        kfree(cpu_buffer);
 }
@@ -6962,9 +6962,9 @@ ring_buffer_alloc_read_page(struct trace_buffer *buffer, 
int cpu)
        local_irq_save(flags);
        arch_spin_lock(&cpu_buffer->lock);
 
-       if (cpu_buffer->free_page) {
-               bpage->data = cpu_buffer->free_page;
-               cpu_buffer->free_page = NULL;
+       if (cpu_buffer->free_page.data) {
+               *bpage = cpu_buffer->free_page;
+               cpu_buffer->free_page.data = NULL;
        }
 
        arch_spin_unlock(&cpu_buffer->lock);
@@ -7016,8 +7016,8 @@ void ring_buffer_free_read_page(struct trace_buffer 
*buffer, int cpu,
        local_irq_save(flags);
        arch_spin_lock(&cpu_buffer->lock);
 
-       if (!cpu_buffer->free_page) {
-               cpu_buffer->free_page = dpage;
+       if (!cpu_buffer->free_page.data) {
+               cpu_buffer->free_page = *data_page;
                dpage = NULL;
        }
 
@@ -7390,7 +7390,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer 
*buffer, int order)
        }
 
        for_each_buffer_cpu(buffer, cpu) {
-               struct buffer_data_page *old_free_data_page;
+               struct buffer_data_read_page old_free_data_page;
                struct list_head old_pages;
                unsigned long flags;
 
@@ -7433,7 +7433,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer 
*buffer, int order)
 
                arch_spin_lock(&cpu_buffer->lock);
                old_free_data_page = cpu_buffer->free_page;
-               cpu_buffer->free_page = NULL;
+               cpu_buffer->free_page.data = NULL;
                arch_spin_unlock(&cpu_buffer->lock);
 
                rb_head_page_activate(cpu_buffer);
@@ -7445,7 +7445,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer 
*buffer, int order)
                        list_del_init(&bpage->list);
                        free_buffer_page(bpage);
                }
-               free_pages((unsigned long)old_free_data_page, old_order);
+               free_pages((unsigned long)old_free_data_page.data, 
old_free_data_page.order);
 
                rb_check_pages(cpu_buffer);
        }
-- 
2.55.0.691.gc56d675ccc-goog


Reply via email to