[PATCH 3/8] drm/i915/pmu: Fix crash due to use-after-free

Matt Roper matthew.d.roper at intel.com
Sat Oct 19 00:05:16 UTC 2024


On Fri, Oct 11, 2024 at 03:54:25PM -0700, Lucas De Marchi wrote:
> When an i915 PMU counter is enabled and the driver is then unbound, the
> PMU will be unregistered via perf_pmu_unregister(), however the event
> will still be alive. i915 currently tries to deal with this situation
> by:
> 
> 	a) Marking the pmu as "closed" and shortcut the calls from perf
> 	b) Taking a reference from i915, that is put back when the event
> 	   is destroyed.
> 	c) Setting event_init to NULL to avoid any further event
> 
> (a) is ugly, but may be left as is since it protects not trying to
> access the HW that is now gone. Unless a pmu driver can call
> perf_pmu_unregister() and not receive any more calls, it's a necessary
> ugliness.
> 
> (b) doesn't really work: when the event is destroyed and the i915 ref is
> put it may free the i915 object, that contains the pmu, not only the
> event. After event->destroy() callback, perf still expects the pmu
> object to be alive.
> 
> Instead of pigging back on the event->destroy() to take and put the
> device reference, implement the new get()/put() on the pmu object for
> that purpose.
> 
> (c) is only done to have a flag to avoid some function entrypoints when
> pmu is unregistered.
> 
> Cc: stable at vger.kernel.org # 5.11+
> Signed-off-by: Lucas De Marchi <lucas.demarchi at intel.com>

Reviewed-by: Matt Roper <matthew.d.roper at intel.com>

> ---
>  drivers/gpu/drm/i915/i915_pmu.c | 36 ++++++++++++++++++++-------------
>  1 file changed, 22 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_pmu.c b/drivers/gpu/drm/i915/i915_pmu.c
> index 4d05d98f51b8e..dc9f753369170 100644
> --- a/drivers/gpu/drm/i915/i915_pmu.c
> +++ b/drivers/gpu/drm/i915/i915_pmu.c
> @@ -515,15 +515,6 @@ static enum hrtimer_restart i915_sample(struct hrtimer *hrtimer)
>  	return HRTIMER_RESTART;
>  }
>  
> -static void i915_pmu_event_destroy(struct perf_event *event)
> -{
> -	struct i915_pmu *pmu = event_to_pmu(event);
> -	struct drm_i915_private *i915 = pmu_to_i915(pmu);
> -
> -	drm_WARN_ON(&i915->drm, event->parent);
> -
> -	drm_dev_put(&i915->drm);
> -}
>  
>  static int
>  engine_event_status(struct intel_engine_cs *engine,
> @@ -629,11 +620,6 @@ static int i915_pmu_event_init(struct perf_event *event)
>  	if (ret)
>  		return ret;
>  
> -	if (!event->parent) {
> -		drm_dev_get(&i915->drm);
> -		event->destroy = i915_pmu_event_destroy;
> -	}
> -
>  	return 0;
>  }
>  
> @@ -872,6 +858,24 @@ static int i915_pmu_event_event_idx(struct perf_event *event)
>  	return 0;
>  }
>  
> +static struct pmu *i915_pmu_get(struct pmu *base)
> +{
> +	struct i915_pmu *pmu = container_of(base, struct i915_pmu, base);
> +	struct drm_i915_private *i915 = pmu_to_i915(pmu);
> +
> +	drm_dev_get(&i915->drm);
> +
> +	return base;
> +}
> +
> +static void i915_pmu_put(struct pmu *base)
> +{
> +	struct i915_pmu *pmu = container_of(base, struct i915_pmu, base);
> +	struct drm_i915_private *i915 = pmu_to_i915(pmu);
> +
> +	drm_dev_put(&i915->drm);
> +}
> +
>  struct i915_str_attribute {
>  	struct device_attribute attr;
>  	const char *str;
> @@ -1154,6 +1158,8 @@ static void free_pmu(struct drm_device *dev, void *res)
>  	struct i915_pmu *pmu = res;
>  	struct drm_i915_private *i915 = pmu_to_i915(pmu);
>  
> +	perf_pmu_free(&pmu->base);
> +
>  	free_event_attributes(pmu);
>  	kfree(pmu->base.attr_groups);
>  	if (IS_DGFX(i915))
> @@ -1299,6 +1305,8 @@ void i915_pmu_register(struct drm_i915_private *i915)
>  	pmu->base.stop		= i915_pmu_event_stop;
>  	pmu->base.read		= i915_pmu_event_read;
>  	pmu->base.event_idx	= i915_pmu_event_event_idx;
> +	pmu->base.get		= i915_pmu_get;
> +	pmu->base.put		= i915_pmu_put;
>  
>  	ret = perf_pmu_register(&pmu->base, pmu->name, -1);
>  	if (ret)
> -- 
> 2.47.0
> 

-- 
Matt Roper
Graphics Software Engineer
Linux GPU Platform Enablement
Intel Corporation


More information about the Intel-gfx mailing list