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 <dnibade@nvidia.com>
Reviewed-on: http://git-master/r/1233988
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
Deepak Nibade
2016-10-10 16:33:32 +05:30
committed by mobile promotions
parent 3bbd641976
commit bb5fd16c67
2 changed files with 19 additions and 5 deletions

View File

@@ -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);

View File

@@ -177,6 +177,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;