From bb5fd16c67287e53db5165a974ea15ec3be09fe9 Mon Sep 17 00:00:00 2001 From: Deepak Nibade Date: Mon, 10 Oct 2016 16:33:32 +0530 Subject: gpu: nvgpu: fix use-after-free in case of error notifier A use-after-free scenario is possible where one thread in gk20a_free_error_notifiers() is trying to free the error notifier and another thread in gk20a_set_error_notifier() is still using the error notifier Fix this by introducing mutex error_notifier_mutex for error notifier accesses Take mutex in gk20a_free_error_notifiers() and in gk20a_set_error_notifier() before accessing notifier In gk20a_init_error_notifier(), set the pointer ch->error_notifier_ref inside the mutex and only after notifier is completely initialized Bug 1824788 Change-Id: I47e1ab57d54f391799f5a0999840b663fd34585f Signed-off-by: Deepak Nibade Reviewed-on: http://git-master/r/1233988 Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/gpu/nvgpu/gk20a/channel_gk20a.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/nvgpu/gk20a/channel_gk20a.c') diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c index 92b43c8a..6a69de3e 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c @@ -742,8 +742,7 @@ static int gk20a_init_error_notifier(struct channel_gk20a *ch, dmabuf = dma_buf_get(args->mem); - if (ch->error_notifier_ref) - gk20a_free_error_notifiers(ch); + gk20a_free_error_notifiers(ch); if (IS_ERR(dmabuf)) { pr_err("Invalid handle: %d\n", args->mem); @@ -764,16 +763,23 @@ static int gk20a_init_error_notifier(struct channel_gk20a *ch, return -ENOMEM; } - /* set channel notifiers pointer */ - ch->error_notifier_ref = dmabuf; ch->error_notifier = va + args->offset; ch->error_notifier_va = va; memset(ch->error_notifier, 0, sizeof(struct nvgpu_notification)); + + /* set channel notifiers pointer */ + mutex_lock(&ch->error_notifier_mutex); + ch->error_notifier_ref = dmabuf; + mutex_unlock(&ch->error_notifier_mutex); + return 0; } void gk20a_set_error_notifier(struct channel_gk20a *ch, __u32 error) { + bool notifier_set = false; + + mutex_lock(&ch->error_notifier_mutex); if (ch->error_notifier_ref) { struct timespec time_data; u64 nsec; @@ -787,13 +793,18 @@ void gk20a_set_error_notifier(struct channel_gk20a *ch, __u32 error) ch->error_notifier->info32 = error; ch->error_notifier->status = 0xffff; + notifier_set = true; + } + mutex_unlock(&ch->error_notifier_mutex); + + if (notifier_set) gk20a_err(dev_from_gk20a(ch->g), "error notifier set to %d for ch %d", error, ch->hw_chid); - } } static void gk20a_free_error_notifiers(struct channel_gk20a *ch) { + mutex_lock(&ch->error_notifier_mutex); if (ch->error_notifier_ref) { dma_buf_vunmap(ch->error_notifier_ref, ch->error_notifier_va); dma_buf_put(ch->error_notifier_ref); @@ -801,6 +812,7 @@ static void gk20a_free_error_notifiers(struct channel_gk20a *ch) ch->error_notifier = NULL; ch->error_notifier_va = NULL; } + mutex_unlock(&ch->error_notifier_mutex); } /* Returns delta of cyclic integers a and b. If a is ahead of b, delta @@ -2387,6 +2399,7 @@ int gk20a_init_channel_support(struct gk20a *g, u32 chid) c->referenceable = false; init_waitqueue_head(&c->ref_count_dec_wq); mutex_init(&c->ioctl_lock); + mutex_init(&c->error_notifier_mutex); spin_lock_init(&c->jobs_lock); raw_spin_lock_init(&c->timeout.lock); mutex_init(&c->sync_lock); -- cgit v1.2.2