In the SET_GLOBAL ioctl, v3d_perfmon_find() bumps the reference count on
the perfmon it returns, but v3d_perfmon_set_global_ioctl() and
v3d_perfmon_delete() fail to release that reference on several paths:

  1. v3d_perfmon_set_global_ioctl() leaks the reference on its error
     paths.

  2. CLEAR_GLOBAL leaks both the find reference and the reference
     previously stashed in v3d->global_perfmon by the SET_GLOBAL ioctl
     that configured it.

  3. Destroying a perfmon that is the current global perfmon leaks the
     reference stashed by the SET_GLOBAL ioctl.

Release each of these references explicitly.

Cc: [email protected]
Fixes: c6eabbab359c ("drm/v3d: Add DRM_IOCTL_V3D_PERFMON_SET_GLOBAL")
Signed-off-by: Maíra Canal <[email protected]>
---
 drivers/gpu/drm/v3d/v3d_perfmon.c | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c 
b/drivers/gpu/drm/v3d/v3d_perfmon.c
index 02451fc09dbb..48ae748247be 100644
--- a/drivers/gpu/drm/v3d/v3d_perfmon.c
+++ b/drivers/gpu/drm/v3d/v3d_perfmon.c
@@ -319,8 +319,11 @@ static void v3d_perfmon_delete(struct v3d_file_priv 
*v3d_priv,
        if (perfmon == v3d->active_perfmon)
                v3d_perfmon_stop(v3d, perfmon, false);
 
-       /* If the global perfmon is being destroyed, set it to NULL */
-       cmpxchg(&v3d->global_perfmon, perfmon, NULL);
+       /* If the global perfmon is being destroyed, clean it and release
+        * the reference stashed in v3d_perfmon_set_global_ioctl().
+        */
+       if (cmpxchg(&v3d->global_perfmon, perfmon, NULL) == perfmon)
+               v3d_perfmon_put(perfmon);
 
        v3d_perfmon_put(perfmon);
 }
@@ -471,16 +474,27 @@ int v3d_perfmon_set_global_ioctl(struct drm_device *dev, 
void *data,
 
        /* If the request is to clear the global performance monitor */
        if (req->flags & DRM_V3D_PERFMON_CLEAR_GLOBAL) {
-               if (!v3d->global_perfmon)
+               struct v3d_perfmon *old;
+
+               /* DRM_V3D_PERFMON_CLEAR_GLOBAL doesn't check if
+                * v3d->global_perfmon == perfmon. Therefore, there
+                * is no need to keep perfmon's reference.
+                */
+               v3d_perfmon_put(perfmon);
+
+               old = xchg(&v3d->global_perfmon, NULL);
+               if (!old)
                        return -EINVAL;
 
-               xchg(&v3d->global_perfmon, NULL);
+               v3d_perfmon_put(old);
 
                return 0;
        }
 
-       if (cmpxchg(&v3d->global_perfmon, NULL, perfmon))
+       if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) {
+               v3d_perfmon_put(perfmon);
                return -EBUSY;
+       }
 
        return 0;
 }

-- 
2.54.0

Reply via email to