From 83ce758fc41bcad25a1aa8995849a8578e31d2ad Mon Sep 17 00:00:00 2001 From: Rob Springer Date: Tue, 28 May 2019 11:27:21 -0700 Subject: [PATCH] staging: gasket: release taken eventfd contexts Port of change by Rob Springer to fix leak of eventfd context objects in gasket framework interrupt code. Bug: 131928433 Bug: 80495595 Reported-by: Jann Horn Reported-by: Neel Natu Test: Resnet50PB, PortraitSegmentation Change-Id: I6bdea5ab4d3a7435e7f51c96817f0542d185b22c Signed-off-by: Todd Poynor --- gasket_interrupt.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/gasket_interrupt.c b/gasket_interrupt.c index 2cd262b..3cf7235 100644 --- a/gasket_interrupt.c +++ b/gasket_interrupt.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #ifdef GASKET_KERNEL_TRACE_SUPPORT #define CREATE_TRACE_POINTS @@ -60,6 +61,9 @@ struct gasket_interrupt_data { /* The eventfd "callback" data for each interrupt. */ struct eventfd_ctx **eventfd_ctxs; + /* Spinlock to protect read/write races to eventfd_ctxs. */ + rwlock_t eventfd_ctx_lock; + /* The number of times each interrupt has been called. */ ulong *interrupt_counts; @@ -148,9 +152,11 @@ gasket_handle_interrupt(struct gasket_interrupt_data *interrupt_data, struct eventfd_ctx *ctx; trace_gasket_interrupt_event(interrupt_data->name, interrupt_index); + read_lock(&interrupt_data->eventfd_ctx_lock); ctx = interrupt_data->eventfd_ctxs[interrupt_index]; if (ctx) eventfd_signal(ctx, 1); + read_unlock(&interrupt_data->eventfd_ctx_lock); ++(interrupt_data->interrupt_counts[interrupt_index]); } @@ -353,6 +359,8 @@ int gasket_interrupt_init(struct gasket_dev *gasket_dev) return -ENOMEM; } + rwlock_init(&interrupt_data->eventfd_ctx_lock); + switch (interrupt_data->type) { case PCI_MSIX: ret = gasket_interrupt_msix_init(interrupt_data); @@ -386,9 +394,11 @@ gasket_interrupt_msix_cleanup(struct gasket_interrupt_data *interrupt_data) { int i; - for (i = 0; i < interrupt_data->num_configured; i++) + for (i = 0; i < interrupt_data->num_configured; i++) { + gasket_interrupt_clear_eventfd(interrupt_data, i); free_irq(interrupt_data->msix_entries[i].vector, interrupt_data); + } interrupt_data->num_configured = 0; if (interrupt_data->msix_configured) @@ -496,24 +506,38 @@ int gasket_interrupt_system_status(struct gasket_dev *gasket_dev) int gasket_interrupt_set_eventfd(struct gasket_interrupt_data *interrupt_data, int interrupt, int event_fd) { - struct eventfd_ctx *ctx = eventfd_ctx_fdget(event_fd); - - if (IS_ERR(ctx)) - return PTR_ERR(ctx); + struct eventfd_ctx *ctx; + ulong flags; if (interrupt < 0 || interrupt >= interrupt_data->num_interrupts) return -EINVAL; + ctx = eventfd_ctx_fdget(event_fd); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + /* Put the old eventfd ctx before setting, else we leak the ref. */ + write_lock_irqsave(&interrupt_data->eventfd_ctx_lock, flags); + if (interrupt_data->eventfd_ctxs[interrupt] != NULL) + eventfd_ctx_put(interrupt_data->eventfd_ctxs[interrupt]); interrupt_data->eventfd_ctxs[interrupt] = ctx; + write_unlock_irqrestore(&interrupt_data->eventfd_ctx_lock, flags); return 0; } int gasket_interrupt_clear_eventfd(struct gasket_interrupt_data *interrupt_data, int interrupt) { + ulong flags; + if (interrupt < 0 || interrupt >= interrupt_data->num_interrupts) return -EINVAL; + /* Put the old eventfd ctx before clearing, else we leak the ref. */ + write_lock_irqsave(&interrupt_data->eventfd_ctx_lock, flags); + if (interrupt_data->eventfd_ctxs[interrupt] != NULL) + eventfd_ctx_put(interrupt_data->eventfd_ctxs[interrupt]); interrupt_data->eventfd_ctxs[interrupt] = NULL; + write_unlock_irqrestore(&interrupt_data->eventfd_ctx_lock, flags); return 0; }