]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/v3d: Fix global performance monitor reference counting
authorMaíra Canal <mcanal@igalia.com>
Sun, 31 May 2026 20:18:55 +0000 (17:18 -0300)
committerMaíra Canal <mcanal@igalia.com>
Thu, 4 Jun 2026 14:37:28 +0000 (11:37 -0300)
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: stable@vger.kernel.org
Fixes: c6eabbab359c ("drm/v3d: Add DRM_IOCTL_V3D_PERFMON_SET_GLOBAL")
Reviewed-by: Iago Toral Quiroga <itoral@igalia.com>
Link: https://patch.msgid.link/20260531-v3d-perfmon-lifetime-v2-1-60ed4485a203@igalia.com
Signed-off-by: Maíra Canal <mcanal@igalia.com>
drivers/gpu/drm/v3d/v3d_perfmon.c

index 8e0249580bbacac507b2d7c0bcac37ef19c1a54e..ecfd446ff75f9e57ca1f31714a756e49e27685fc 100644 (file)
@@ -309,8 +309,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);
 }
@@ -461,16 +464,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;
 }