From: Gagan Grover Date: Wed, 12 Oct 2016 11:35:06 +0000 (+0530) Subject: gpu: nvgpu: fix use-after-free in case of error notifier X-Git-Tag: tegra-l4t-r24.2.1~5 X-Git-Url: http://rtime.felk.cvut.cz/gitweb/sojka/nv-tegra/linux-3.10.git/commitdiff_plain/e78b0572a7cee72a4542f9667f297ba38f29b57e 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 Reviewed-on: http://git-master/r/1233988 Signed-off-by: Gagan Grover Reviewed-on: http://git-master/r/1236662 Reviewed-by: Deepak Nibade GVS: Gerrit_Virtual_Submit Reviewed-by: Gaurav Singh (SW-TEGRA) Tested-by: Gaurav Singh (SW-TEGRA) Reviewed-by: Bibek Basu --- diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c index 5e51c62b32a..d53e45da68c 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c @@ -605,8 +605,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); @@ -627,16 +626,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; @@ -650,13 +656,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); @@ -664,6 +675,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 @@ -1986,6 +1998,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); mutex_init(&c->last_submit.fence_lock); INIT_DELAYED_WORK(&c->clean_up.wq, gk20a_channel_clean_up_jobs); diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h index 00721c6c172..465fe64d350 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h @@ -170,6 +170,7 @@ struct channel_gk20a { struct dma_buf *error_notifier_ref; struct nvgpu_notification *error_notifier; void *error_notifier_va; + struct mutex error_notifier_mutex; struct mutex sync_lock; struct gk20a_channel_sync *sync;