msm: kgsl: Signal fence only if last fence refcount was not put

Currently there is a chance that release for the fence was already called
before we call dma_fence_get during kgsl_timeline_signal and
kgsl_ioctl_timeline_destroy. This can cause use-after-free issue as we can
access fence after release. Fix this by signalling fence only if the last
refcount on the fence was not yet put. This makes sure that release for the
fence will not be called until we are done signalling.

Change-Id: I6bdcefa1f128febb7a0f7aef133757268a3b9ae3
Signed-off-by: Puranam V G Tejaswi <pvgtejas@codeaurora.org>
Signed-off-by: Pranav Patel <quic_pranavp@quicinc.com>
This commit is contained in:
Puranam V G Tejaswi 2021-05-17 14:41:40 +05:30 committed by Pranav Patel
parent e904060a40
commit 2959ed5eb8

View File

@ -270,12 +270,10 @@ void kgsl_timeline_signal(struct kgsl_timeline *timeline, u64 seqno)
timeline->value = seqno; timeline->value = seqno;
spin_lock(&timeline->fence_lock); spin_lock(&timeline->fence_lock);
list_for_each_entry_safe(fence, tmp, &timeline->fences, node) { list_for_each_entry_safe(fence, tmp, &timeline->fences, node)
if (timeline_fence_signaled(&fence->base)) { if (timeline_fence_signaled(&fence->base) &&
dma_fence_get(&fence->base); kref_get_unless_zero(&fence->base.refcount))
list_move(&fence->node, &temp); list_move(&fence->node, &temp);
}
}
spin_unlock(&timeline->fence_lock); spin_unlock(&timeline->fence_lock);
list_for_each_entry_safe(fence, tmp, &temp, node) { list_for_each_entry_safe(fence, tmp, &temp, node) {
@ -548,7 +546,8 @@ long kgsl_ioctl_timeline_destroy(struct kgsl_device_private *dev_priv,
spin_lock(&timeline->fence_lock); spin_lock(&timeline->fence_lock);
list_for_each_entry_safe(fence, tmp, &timeline->fences, node) list_for_each_entry_safe(fence, tmp, &timeline->fences, node)
dma_fence_get(&fence->base); if (!kref_get_unless_zero(&fence->base.refcount))
list_del_init(&fence->node);
list_replace_init(&timeline->fences, &temp); list_replace_init(&timeline->fences, &temp);
spin_unlock(&timeline->fence_lock); spin_unlock(&timeline->fence_lock);