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 <jannh@google.com>
Reported-by: Neel Natu <neelnatu@google.com>
Test: Resnet50PB, PortraitSegmentation
Change-Id: I6bdea5ab4d3a7435e7f51c96817f0542d185b22c
Signed-off-by: Todd Poynor <toddpoynor@google.com>
This commit is contained in:
Rob Springer
2019-05-28 11:27:21 -07:00
committed by Leonid Lobachev
parent 56f0b552fe
commit 83ce758fc4

View File

@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/printk.h>
#include <linux/rwlock.h>
#include <linux/version.h>
#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;
}