[PATCH 1/1] drivers/perf: Fix kernel panic due to the invalid mon_ctx pointer
Shanker Donthineni
sdonthineni at nvidia.com
Thu Oct 26 15:15:38 PDT 2023
Hi Robin,
On 10/26/23 11:39, Robin Murphy wrote:
> External email: Use caution opening links or attachments
>
>
> [ Since I made the mistake of looking... ]
>
> On 26/10/2023 3:29 pm, Shanker Donthineni wrote:
>> The return pointer from the resctrl_arch_mon_ctx_alloc_no_wait() function
>> is saved in a 32-bit variable 'hwc->idx,' which results in the loss of
>> the upper 32 bits. This, in turn, triggers a kernel panic when attempting
>> to access a corrupted pointer.
>>
>> This patch addresses the issue by utilizing the IDR data structure to
>> keep track of a 32-bit value associated with a pointer (64-bit value).
>
> This seems unnecessaraily complicated - there's plenty of space in that
> anonymous hw_perf_event "driver data" union (I know I cram quite a lot
> in there for arm-cmn) so it should just be a case of picking a different
> appropriately-sized field to (ab)use.
>
It appears that there is already a private pointer named 'pmu_private' in
the 'perf_event' structure. I will submit version 2 of the code with this
modification.
--- a/drivers/perf/resctrl_pmu.c
+++ b/drivers/perf/resctrl_pmu.c
@@ -66,7 +66,6 @@ static struct rdt_resource *resctrl_event_get_resource(u16 event_num)
static void resctrl_pmu_event_destroy(struct perf_event *event)
{
- struct hw_perf_event *hwc = &event->hw;
u16 event_num = get_event(event);
struct rdt_resource *r;
@@ -74,7 +73,7 @@ static void resctrl_pmu_event_destroy(struct perf_event *event)
if (!r)
return;
- resctrl_arch_mon_ctx_free(r, event_num, hwc->idx);
+ resctrl_arch_mon_ctx_free(r, event_num, event->pmu_private);
}
static int resctrl_pmu_event_init(struct perf_event *event)
@@ -144,8 +143,8 @@ static int resctrl_pmu_event_init(struct perf_event *event)
return -EINVAL;
}
- hwc->idx = resctrl_arch_mon_ctx_alloc_no_wait(r, event_num);
- if (hwc->idx == -ENOSPC)
+ event->pmu_private = resctrl_arch_mon_ctx_alloc_no_wait(r, event_num);
+ if (IS_ERR_OR_NULL(event->pmu_private))
return -ENOSPC;
event->destroy = resctrl_pmu_event_destroy;
local64_set(&hwc->prev_count, 0);
@@ -183,7 +182,8 @@ static void resctrl_pmu_event_update(struct perf_event *event)
prev = local64_read(&hwc->prev_count);
err = resctrl_arch_rmid_read(r, d, closid, rmid,
- event_num, &now, hwc->idx);
+ event_num, &now,
+ event->pmu_private);
> Thanks,
> Robin.
>
>> Signed-off-by: Shanker Donthineni <sdonthineni at nvidia.com>
>> ---
>> drivers/perf/resctrl_pmu.c | 27 ++++++++++++++++++++++++---
>> 1 file changed, 24 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/perf/resctrl_pmu.c b/drivers/perf/resctrl_pmu.c
>> index 99a2b90b5d83..68cda619da48 100644
>> --- a/drivers/perf/resctrl_pmu.c
>> +++ b/drivers/perf/resctrl_pmu.c
>> @@ -6,6 +6,8 @@
>> #include <linux/perf_event.h>
>> #include <linux/platform_device.h>
>> #include <linux/resctrl.h>
>> +#include <linux/mutex.h>
>> +#include <linux/idr.h>
>>
>> struct resctrl_pmu {
>> struct pmu pmu;
>> @@ -25,6 +27,9 @@ RESCTRL_PMU_EVENT_ATTR_EXTRACTOR(event, config, 0, 7);
>> RESCTRL_PMU_EVENT_ATTR_EXTRACTOR(domain, config, 16, 23);
>> RESCTRL_PMU_EVENT_ATTR_EXTRACTOR(resctrl_id, config1, 0, 63);
>>
>> +static DEFINE_MUTEX(resctrl_pmu_lock);
>> +static DEFINE_IDR(resctrl_pmu_idr);
>> +
>> static void resctrl_pmu_do_nothing(struct pmu *pmu)
>> {
>> }
>> @@ -69,12 +74,17 @@ static void resctrl_pmu_event_destroy(struct perf_event *event)
>> struct hw_perf_event *hwc = &event->hw;
>> u16 event_num = get_event(event);
>> struct rdt_resource *r;
>> + void *mon_ctx;
>>
>> r = resctrl_event_get_resource(event_num);
>> if (!r)
>> return;
>>
>> - resctrl_arch_mon_ctx_free(r, event_num, hwc->idx);
>> + mutex_lock(&resctrl_pmu_lock);
>> + mon_ctx = idr_remove(&resctrl_pmu_idr, hwc->idx);
>> + mutex_unlock(&resctrl_pmu_lock);
>> +
>> + resctrl_arch_mon_ctx_free(r, event_num, mon_ctx);
>> }
>>
>> static int resctrl_pmu_event_init(struct perf_event *event)
>> @@ -87,6 +97,7 @@ static int resctrl_pmu_event_init(struct perf_event *event)
>> struct rdt_domain *d;
>> u16 event_num, domain_id;
>> u32 closid, rmid;
>> + void *mon_ctx;
>> int err;
>> u64 id;
>>
>> @@ -144,7 +155,12 @@ static int resctrl_pmu_event_init(struct perf_event *event)
>> return -EINVAL;
>> }
>>
>> - hwc->idx = resctrl_arch_mon_ctx_alloc_no_wait(r, event_num);
>> + mon_ctx = resctrl_arch_mon_ctx_alloc_no_wait(r, event_num);
>> +
>> + mutex_lock(&resctrl_pmu_lock);
>> + hwc->idx = idr_alloc(&resctrl_pmu_idr, mon_ctx, 0, INT_MAX, GFP_KERNEL);
>> + mutex_unlock(&resctrl_pmu_lock);
>> +
>> if (hwc->idx == -ENOSPC)
>> return -ENOSPC;
>> event->destroy = resctrl_pmu_event_destroy;
>> @@ -162,6 +178,7 @@ static void resctrl_pmu_event_update(struct perf_event *event)
>> struct rdt_resource *r;
>> struct rdt_domain *d;
>> u32 closid, rmid;
>> + void *mon_ctx;
>> int err;
>>
>> event_num = get_event(event);
>> @@ -179,11 +196,15 @@ static void resctrl_pmu_event_update(struct perf_event *event)
>> if (!cpumask_test_cpu(smp_processor_id(), &d->cpu_mask))
>> return;
>>
>> + mutex_lock(&resctrl_pmu_lock);
>> + mon_ctx = idr_find(&resctrl_pmu_idr, hwc->idx);
>> + mutex_unlock(&resctrl_pmu_lock);
>> +
>> do {
>> prev = local64_read(&hwc->prev_count);
>>
>> err = resctrl_arch_rmid_read(r, d, closid, rmid,
>> - event_num, &now, hwc->idx);
>> + event_num, &now, mon_ctx);
>> if (err)
>> return;
>> } while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
More information about the linux-arm-kernel
mailing list