Make sure the timer belonging to the delayed work in the LinuxKPI

gets drained before invoking the work function. Else the timer
mutex may still be in use which can lead to use-after-free situations,
because the work function might free the work structure before returning.

MFC after:	1 week
Sponsored by:	Mellanox Technologies
This commit is contained in:
hselasky 2017-10-04 13:13:38 +00:00
parent 9eddd5d91f
commit 3a9dfc3d72
2 changed files with 20 additions and 1 deletions

View File

@ -215,6 +215,7 @@ extern struct workqueue_struct *system_power_efficient_wq;
extern void linux_init_delayed_work(struct delayed_work *, work_func_t);
extern void linux_work_fn(void *, int);
extern void linux_delayed_work_fn(void *, int);
extern struct workqueue_struct *linux_create_workqueue_common(const char *, int);
extern void linux_destroy_workqueue(struct workqueue_struct *);
extern bool linux_queue_work_on(int cpu, struct workqueue_struct *, struct work_struct *);

View File

@ -260,6 +260,23 @@ linux_work_fn(void *context, int pending)
WQ_EXEC_UNLOCK(wq);
}
void
linux_delayed_work_fn(void *context, int pending)
{
struct delayed_work *dwork = context;
/*
* Make sure the timer belonging to the delayed work gets
* drained before invoking the work function. Else the timer
* mutex may still be in use which can lead to use-after-free
* situations, because the work function might free the work
* structure before returning.
*/
callout_drain(&dwork->timer.callout);
linux_work_fn(&dwork->work, pending);
}
static void
linux_delayed_work_timer_fn(void *arg)
{
@ -550,7 +567,8 @@ void
linux_init_delayed_work(struct delayed_work *dwork, work_func_t func)
{
memset(dwork, 0, sizeof(*dwork));
INIT_WORK(&dwork->work, func);
dwork->work.func = func;
TASK_INIT(&dwork->work.work_task, 0, linux_delayed_work_fn, dwork);
mtx_init(&dwork->timer.mtx, spin_lock_name("lkpi-dwork"), NULL,
MTX_DEF | MTX_NOWITNESS);
callout_init_mtx(&dwork->timer.callout, &dwork->timer.mtx, 0);