Re: [PATCH V3 2/2] perf/x86: Reset the dirty counter to prevent the leak for an RDPMC task
On 4/14/2021 9:51 AM, Namhyung Kim wrote: Hi Kan, On Wed, Apr 14, 2021 at 4:04 AM wrote: diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index dd9f3c2..0d4a1a3 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -1585,6 +1585,8 @@ static void x86_pmu_del(struct perf_event *event, int flags) if (cpuc->txn_flags & PERF_PMU_TXN_ADD) goto do_del; + __set_bit(event->hw.idx, cpuc->dirty); + /* * Not a TXN, therefore cleanup properly. */ @@ -2304,12 +2306,46 @@ static int x86_pmu_event_init(struct perf_event *event) return err; } +void x86_pmu_clear_dirty_counters(void) +{ + struct cpu_hw_events *cpuc = this_cpu_ptr(_hw_events); + int i; + + if (bitmap_empty(cpuc->dirty, X86_PMC_IDX_MAX)) + return; Maybe you can check it after clearing assigned counters. It should be very likely that the cpuc->dirty is non-empty. Move it after the clearing can skip the for_each_set_bit() and bitmap_zero(). OK. I will change it in V4. Thanks, Kan
Re: [PATCH V3 2/2] perf/x86: Reset the dirty counter to prevent the leak for an RDPMC task
Hi Kan, On Wed, Apr 14, 2021 at 4:04 AM wrote: > diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c > index dd9f3c2..0d4a1a3 100644 > --- a/arch/x86/events/core.c > +++ b/arch/x86/events/core.c > @@ -1585,6 +1585,8 @@ static void x86_pmu_del(struct perf_event *event, int > flags) > if (cpuc->txn_flags & PERF_PMU_TXN_ADD) > goto do_del; > > + __set_bit(event->hw.idx, cpuc->dirty); > + > /* > * Not a TXN, therefore cleanup properly. > */ > @@ -2304,12 +2306,46 @@ static int x86_pmu_event_init(struct perf_event > *event) > return err; > } > > +void x86_pmu_clear_dirty_counters(void) > +{ > + struct cpu_hw_events *cpuc = this_cpu_ptr(_hw_events); > + int i; > + > + if (bitmap_empty(cpuc->dirty, X86_PMC_IDX_MAX)) > + return; Maybe you can check it after clearing assigned counters. Thanks, Namhyung > + > +/* Don't need to clear the assigned counter. */ > + for (i = 0; i < cpuc->n_events; i++) > + __clear_bit(cpuc->assign[i], cpuc->dirty); > + > + for_each_set_bit(i, cpuc->dirty, X86_PMC_IDX_MAX) { > + /* Metrics and fake events don't have corresponding HW > counters. */ > + if (is_metric_idx(i) || (i == INTEL_PMC_IDX_FIXED_VLBR)) > + continue; > + else if (i >= INTEL_PMC_IDX_FIXED) > + wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + (i - > INTEL_PMC_IDX_FIXED), 0); > + else > + wrmsrl(x86_pmu_event_addr(i), 0); > + } > + > + bitmap_zero(cpuc->dirty, X86_PMC_IDX_MAX); > +}
Re: [PATCH V3 2/2] perf/x86: Reset the dirty counter to prevent the leak for an RDPMC task
On 4/13/2021 4:33 PM, kernel test robot wrote: Hi, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on tip/perf/core] [also build test WARNING on tip/master linux/master linus/master v5.12-rc7 next-20210413] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/kan-liang-linux-intel-com/perf-x86-Move-cpuc-running-into-P4-specific-code/20210414-030649 base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git cface0326a6c2ae5c8f47bd466f07624b3e348a7 config: i386-tinyconfig (attached as .config) compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 reproduce (this is a W=1 build): # https://github.com/0day-ci/linux/commit/83f02393e1b5a2723294d8697f4fd5473d70602c git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review kan-liang-linux-intel-com/perf-x86-Move-cpuc-running-into-P4-specific-code/20210414-030649 git checkout 83f02393e1b5a2723294d8697f4fd5473d70602c # save the attached .config to linux build tree make W=1 ARCH=i386 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot All warnings (new ones prefixed by >>): arch/x86/events/core.c:2309:6: warning: no previous prototype for 'x86_pmu_clear_dirty_counters' [-Wmissing-prototypes] 2309 | void x86_pmu_clear_dirty_counters(void) | ^~~~ vim +/x86_pmu_clear_dirty_counters +2309 arch/x86/events/core.c 2308 2309void x86_pmu_clear_dirty_counters(void) Should be "static void x86_pmu_clear_dirty_counters(void)". I will send V4 shortly to fix it. Thanks, Kan 2310 { 2311 struct cpu_hw_events *cpuc = this_cpu_ptr(_hw_events); 2312 int i; 2313 2314 if (bitmap_empty(cpuc->dirty, X86_PMC_IDX_MAX)) 2315 return; 2316 2317 /* Don't need to clear the assigned counter. */ 2318 for (i = 0; i < cpuc->n_events; i++) 2319 __clear_bit(cpuc->assign[i], cpuc->dirty); 2320 2321 for_each_set_bit(i, cpuc->dirty, X86_PMC_IDX_MAX) { 2322 /* Metrics and fake events don't have corresponding HW counters. */ 2323 if (is_metric_idx(i) || (i == INTEL_PMC_IDX_FIXED_VLBR)) 2324 continue; 2325 else if (i >= INTEL_PMC_IDX_FIXED) 2326 wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + (i - INTEL_PMC_IDX_FIXED), 0); 2327 else 2328 wrmsrl(x86_pmu_event_addr(i), 0); 2329 } 2330 2331 bitmap_zero(cpuc->dirty, X86_PMC_IDX_MAX); 2332 } 2333 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org
Re: [PATCH V3 2/2] perf/x86: Reset the dirty counter to prevent the leak for an RDPMC task
On 4/13/2021 8:34 PM, Andy Lutomirski wrote: On Tue, Apr 13, 2021 at 12:05 PM wrote: From: Kan Liang The counter value of a perf task may leak to another RDPMC task. For example, a perf stat task as below is running on CPU 0. perf stat -e 'branches,cycles' -- taskset -c 0 ./workload I assume this doesn't fix the leak if the sensitive counter is systemwide? Right. Could Intel please add proper security and ideally virtualization for this? Ideally RDPMC permission would be a bitmask for all RDPMC-able counters, not just a single on/off switch. Yes, we are working on it. For now, I think this patch is what we can do so far. Thanks, Kan
Re: [PATCH V3 2/2] perf/x86: Reset the dirty counter to prevent the leak for an RDPMC task
On Tue, Apr 13, 2021 at 12:05 PM wrote: > > From: Kan Liang > > The counter value of a perf task may leak to another RDPMC task. > For example, a perf stat task as below is running on CPU 0. > > perf stat -e 'branches,cycles' -- taskset -c 0 ./workload I assume this doesn't fix the leak if the sensitive counter is systemwide? Could Intel please add proper security and ideally virtualization for this? Ideally RDPMC permission would be a bitmask for all RDPMC-able counters, not just a single on/off switch. -Andy
Re: [PATCH V3 2/2] perf/x86: Reset the dirty counter to prevent the leak for an RDPMC task
Hi, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on tip/perf/core] [also build test WARNING on tip/master linux/master linus/master v5.12-rc7 next-20210413] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/kan-liang-linux-intel-com/perf-x86-Move-cpuc-running-into-P4-specific-code/20210414-030649 base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git cface0326a6c2ae5c8f47bd466f07624b3e348a7 config: x86_64-randconfig-a014-20210413 (attached as .config) compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 9829f5e6b1bca9b61efc629770d28bb9014dec45) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install x86_64 cross compiling tool for clang build # apt-get install binutils-x86-64-linux-gnu # https://github.com/0day-ci/linux/commit/83f02393e1b5a2723294d8697f4fd5473d70602c git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review kan-liang-linux-intel-com/perf-x86-Move-cpuc-running-into-P4-specific-code/20210414-030649 git checkout 83f02393e1b5a2723294d8697f4fd5473d70602c # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot All warnings (new ones prefixed by >>): >> arch/x86/events/core.c:2309:6: warning: no previous prototype for function >> 'x86_pmu_clear_dirty_counters' [-Wmissing-prototypes] void x86_pmu_clear_dirty_counters(void) ^ arch/x86/events/core.c:2309:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void x86_pmu_clear_dirty_counters(void) ^ static 1 warning generated. vim +/x86_pmu_clear_dirty_counters +2309 arch/x86/events/core.c 2308 > 2309 void x86_pmu_clear_dirty_counters(void) 2310 { 2311 struct cpu_hw_events *cpuc = this_cpu_ptr(_hw_events); 2312 int i; 2313 2314 if (bitmap_empty(cpuc->dirty, X86_PMC_IDX_MAX)) 2315 return; 2316 2317 /* Don't need to clear the assigned counter. */ 2318 for (i = 0; i < cpuc->n_events; i++) 2319 __clear_bit(cpuc->assign[i], cpuc->dirty); 2320 2321 for_each_set_bit(i, cpuc->dirty, X86_PMC_IDX_MAX) { 2322 /* Metrics and fake events don't have corresponding HW counters. */ 2323 if (is_metric_idx(i) || (i == INTEL_PMC_IDX_FIXED_VLBR)) 2324 continue; 2325 else if (i >= INTEL_PMC_IDX_FIXED) 2326 wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + (i - INTEL_PMC_IDX_FIXED), 0); 2327 else 2328 wrmsrl(x86_pmu_event_addr(i), 0); 2329 } 2330 2331 bitmap_zero(cpuc->dirty, X86_PMC_IDX_MAX); 2332 } 2333 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org .config.gz Description: application/gzip
Re: [PATCH V3 2/2] perf/x86: Reset the dirty counter to prevent the leak for an RDPMC task
Hi, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on tip/perf/core] [also build test WARNING on tip/master linux/master linus/master v5.12-rc7 next-20210413] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/kan-liang-linux-intel-com/perf-x86-Move-cpuc-running-into-P4-specific-code/20210414-030649 base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git cface0326a6c2ae5c8f47bd466f07624b3e348a7 config: i386-tinyconfig (attached as .config) compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 reproduce (this is a W=1 build): # https://github.com/0day-ci/linux/commit/83f02393e1b5a2723294d8697f4fd5473d70602c git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review kan-liang-linux-intel-com/perf-x86-Move-cpuc-running-into-P4-specific-code/20210414-030649 git checkout 83f02393e1b5a2723294d8697f4fd5473d70602c # save the attached .config to linux build tree make W=1 ARCH=i386 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot All warnings (new ones prefixed by >>): >> arch/x86/events/core.c:2309:6: warning: no previous prototype for >> 'x86_pmu_clear_dirty_counters' [-Wmissing-prototypes] 2309 | void x86_pmu_clear_dirty_counters(void) | ^~~~ vim +/x86_pmu_clear_dirty_counters +2309 arch/x86/events/core.c 2308 > 2309 void x86_pmu_clear_dirty_counters(void) 2310 { 2311 struct cpu_hw_events *cpuc = this_cpu_ptr(_hw_events); 2312 int i; 2313 2314 if (bitmap_empty(cpuc->dirty, X86_PMC_IDX_MAX)) 2315 return; 2316 2317 /* Don't need to clear the assigned counter. */ 2318 for (i = 0; i < cpuc->n_events; i++) 2319 __clear_bit(cpuc->assign[i], cpuc->dirty); 2320 2321 for_each_set_bit(i, cpuc->dirty, X86_PMC_IDX_MAX) { 2322 /* Metrics and fake events don't have corresponding HW counters. */ 2323 if (is_metric_idx(i) || (i == INTEL_PMC_IDX_FIXED_VLBR)) 2324 continue; 2325 else if (i >= INTEL_PMC_IDX_FIXED) 2326 wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + (i - INTEL_PMC_IDX_FIXED), 0); 2327 else 2328 wrmsrl(x86_pmu_event_addr(i), 0); 2329 } 2330 2331 bitmap_zero(cpuc->dirty, X86_PMC_IDX_MAX); 2332 } 2333 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org .config.gz Description: application/gzip
[PATCH V3 2/2] perf/x86: Reset the dirty counter to prevent the leak for an RDPMC task
From: Kan Liang The counter value of a perf task may leak to another RDPMC task. For example, a perf stat task as below is running on CPU 0. perf stat -e 'branches,cycles' -- taskset -c 0 ./workload In the meantime, an RDPMC task, which is also running on CPU 0, may read the GP counters periodically. (The RDPMC task creates a fixed event, but read four GP counters.) $ taskset -c 0 ./rdpmc_read_all_counters index 0x0 value 0x8001e5970f99 index 0x1 value 0x8005d750edb6 index 0x2 value 0x0 index 0x3 value 0x0 index 0x0 value 0x8002358e48a5 index 0x1 value 0x8006bd1e3bc9 index 0x2 value 0x0 index 0x3 value 0x0 It is a potential security issue. Once the attacker knows what the other thread is counting. The PerfMon counter can be used as a side-channel to attack cryptosystems. The counter value of the perf stat task leaks to the RDPMC task because perf never clears the counter when it's stopped. Two methods were considered to address the issue. - Unconditionally reset the counter in x86_pmu_del(). It can bring extra overhead even when there is no RDPMC task running. - Only reset the un-assigned dirty counters when the RDPMC task is scheduled in. The method is implemented here. The dirty counter is a counter, on which the assigned event has been deleted, but the counter is not reset. To track the dirty counters, add a 'dirty' variable in the struct cpu_hw_events. The current code doesn't reset the counter when the assigned event is deleted. Set the corresponding bit in the 'dirty' variable in x86_pmu_del(), if the RDPMC feature is available on the system. The security issue can only be found with an RDPMC task. The event for an RDPMC task requires the mmap buffer. This can be used to detect an RDPMC task. Once the event is detected in the event_mapped(), enable sched_task(), which is invoked in each context switch. Add a check in the sched_task() to clear the dirty counters, when the RDPMC task is scheduled in. Only the current un-assigned dirty counters are reset, bacuase the RDPMC assigned dirty counters will be updated soon. The RDPMC instruction is also supported on the older platforms. Add sched_task() for the core_pmu. The core_pmu doesn't support large PEBS and LBR callstack, the intel_pmu_pebs/lbr_sched_task() will be ignored. The RDPMC is not Intel-only feature. Add the dirty counters clear code in the X86 generic code. After applying the patch, $ taskset -c 0 ./rdpmc_read_all_counters index 0x0 value 0x0 index 0x1 value 0x0 index 0x2 value 0x0 index 0x3 value 0x0 index 0x0 value 0x0 index 0x1 value 0x0 index 0x2 value 0x0 index 0x3 value 0x0 Performance The performance of a context switch only be impacted when there are two or more perf users and one of the users must be an RDPMC user. In other cases, there is no performance impact. The worst-case occurs when there are two users: the RDPMC user only applies one counter; while the other user applies all available counters. When the RDPMC task is scheduled in, all the counters, other than the RDPMC assigned one, have to be reset. Here is the test result for the worst-case. The test is implemented on an Ice Lake platform, which has 8 GP counters and 3 fixed counters (Not include SLOTS counter). The lat_ctx is used to measure the context switching time. lat_ctx -s 128K -N 1000 processes 2 I instrument the lat_ctx to open all 8 GP counters and 3 fixed counters for one task. The other task opens a fixed counter and enable RDPMC. Without the patch: The context switch time is 4.97 us With the patch: The context switch time is 5.16 us There is ~4% performance drop for the context switching time in the worst-case. Suggested-by: Peter Zijlstra (Intel) Signed-off-by: Kan Liang --- The V2 can be found here. https://lore.kernel.org/lkml/20200821195754.20159-3-kan.li...@linux.intel.com/ Changes since V2: - Unconditionally set cpuc->dirty. The worst case for an RDPMC task is that we may have to clear all counters for the first time in x86_pmu_event_mapped. After that, the sched_task() will clear/update the 'dirty'. Only the real 'dirty' counters are clear. For a non-RDPMC task, it's harmless to unconditionally set the cpuc->dirty. - Remove the !is_sampling_event() check - Move the code into X86 generic file, because RDPMC is not a Intel-only feature. Changes since V1: - Drop the old method, which unconditionally reset the counter in x86_pmu_del(). Only reset the dirty counters when a RDPMC task is sheduled in. arch/x86/events/core.c | 47 arch/x86/events/perf_event.h | 1 + 2 files changed, 48 insertions(+) diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index dd9f3c2..0d4a1a3 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -1585,6 +1585,8 @@ static void x86_pmu_del(struct perf_event *event, int flags) if