* [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter()
@ 2026-04-14 2:14 Dapeng Mi
2026-04-14 2:14 ` [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack Dapeng Mi
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Dapeng Mi @ 2026-04-14 2:14 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao, Dapeng Mi, stable
In intel_pmu_lbr_filter(), the 'type' variable is bitwise ORed with
'to_plm' (which contains X86_BR_USER and/or X86_BR_KERNEL bits). Because
of this, 'type' can never equal X86_BR_NONE (0) after the assignment.
As a result, the subsequent check 'if (type == X86_BR_NONE)' is dead code
and the entries with X86_BR_NONE type would not be skipped eventually.
Correct this by masking out the X86_BR_KERNEL and X86_BR_USER bits
before performing the X86_BR_NONE comparison.
Cc: stable@vger.kernel.org
Fixes: 47125db27e47 ("perf/x86/intel/lbr: Support Architectural LBR")
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/lbr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
index 72f2adcda7c6..16977e4c6f8a 100644
--- a/arch/x86/events/intel/lbr.c
+++ b/arch/x86/events/intel/lbr.c
@@ -1245,7 +1245,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
}
/* if type does not correspond, then discard */
- if (type == X86_BR_NONE || (br_sel & type) != type) {
+ if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
cpuc->lbr_entries[i].from = 0;
compress = true;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack
2026-04-14 2:14 [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter() Dapeng Mi
@ 2026-04-14 2:14 ` Dapeng Mi
2026-04-14 3:16 ` sashiko-bot
2026-04-29 20:57 ` Chen, Zide
2026-04-14 2:53 ` [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter() sashiko-bot
2026-04-29 20:58 ` Chen, Zide
2 siblings, 2 replies; 10+ messages in thread
From: Dapeng Mi @ 2026-04-14 2:14 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Zide Chen,
Falcon Thomas, Xudong Hao, Dapeng Mi, stable
Prior to the arch-LBR which supports CPL filtering, the kernel address
could be leaked to user space even PERF_SAMPLE_BRANCH_USER is required.
e.g., run below command on Intel Tigerlake platform,
```
$./perf record -e cycles:p -o - --branch-filter any,save_type,u -- \
./perf bench syscall basic --loop 1000 | \
./perf script -i - --fields brstack|tr ' ' '\n'| \
grep -E '0x[89a-f][0-9a-f]{15}'
Total time: 0.000 [sec]
0.219000 usecs/op
4,566,210 ops/sec
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.551 MB - ]
0xffffffff93c001c8/0x7f12a2b1d647/P/-/-/16959/SYSRET/-
0xffffffff93c001c8/0x7f12a2b1d5c2/P/-/-/17535/SYSRET/-
0xffffffff93c01928/0x7f12a2861000/P/-/-/6719/ERET/-
0xffffffff93c01928/0x7f12a297a000/P/-/-/8575/ERET/-
```
The SYSRET/ERET branch calls are found the in the LBR stack, whose "from"
addresses are obviously kernel address.
Currently intel_pmu_lbr_filter() only filters out the LBR entries whose
"to" address is a kernel address but doesn't check the "from" address.
To fix the issue, extend the software filtering to both "from" and "to"
addresses.
Cc: stable@vger.kernel.org
Reported-by: Ian Rogers <irogers@google.com>
Fixes: 47125db27e47 ("perf/x86/intel/lbr: Support Architectural LBR")
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
---
arch/x86/events/intel/lbr.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
index 16977e4c6f8a..deef81c16571 100644
--- a/arch/x86/events/intel/lbr.c
+++ b/arch/x86/events/intel/lbr.c
@@ -1212,7 +1212,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
{
u64 from, to;
int br_sel = cpuc->br_sel;
- int i, j, type, to_plm;
+ int i, j, type, to_plm, from_plm;
bool compress = false;
/* if sampling all branches, then nothing to filter */
@@ -1244,8 +1244,15 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
type |= X86_BR_NO_TX;
}
- /* if type does not correspond, then discard */
- if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
+ from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER;
+ /*
+ * If type does not correspond, then discard.
+ * Especially filter out the entries whose from or to address
+ * is a kernel address while only X86_BR_USER is set. This prevents
+ * kernel address from being leaked into a user-space-only LBR stack.
+ */
+ if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type ||
+ (!(br_sel & X86_BR_KERNEL) && (from_plm & X86_BR_KERNEL))) {
cpuc->lbr_entries[i].from = 0;
compress = true;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter()
2026-04-14 2:14 [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter() Dapeng Mi
2026-04-14 2:14 ` [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack Dapeng Mi
@ 2026-04-14 2:53 ` sashiko-bot
2026-04-14 3:00 ` Mi, Dapeng
2026-04-29 20:58 ` Chen, Zide
2 siblings, 1 reply; 10+ messages in thread
From: sashiko-bot @ 2026-04-14 2:53 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Sashiko AI review found 1 potential issue(s):
- [Medium] Meaningful code and commit message mismatch due to a false premise
--
commit 98c2839085a2b609f94d9f3f338217d29525fd08
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter()
This commit modifies the branch filter logic to mask out privilege level
bits before checking if a branch type is X86_BR_NONE.
> In intel_pmu_lbr_filter(), the 'type' variable is bitwise ORed with
> 'to_plm' (which contains X86_BR_USER and/or X86_BR_KERNEL bits). Because
> of this, 'type' can never equal X86_BR_NONE (0) after the assignment.
>
> As a result, the subsequent check 'if (type == X86_BR_NONE)' is dead code
> and the entries with X86_BR_NONE type would not be skipped eventually.
Is this description entirely accurate?
Looking at intel_pmu_lbr_filter(), the bitwise OR with to_plm only happens
in the X86_FEATURE_ARCH_LBR path:
intel_pmu_lbr_filter() {
...
if (static_cpu_has(X86_FEATURE_ARCH_LBR) &&
type <= ARCH_LBR_BR_TYPE_KNOWN_MAX) {
to_plm = kernel_ip(to) ? X86_BR_KERNEL : X86_BR_USER;
type = arch_lbr_br_type_map[type] | to_plm;
} else
type = branch_type(from, to, cpuc->lbr_entries[i].abort);
...
}
In the software fallback path, type is assigned the result of branch_type().
If branch_type() cannot decode the branch and returns X86_BR_NONE (0),
wouldn't type evaluate to exactly X86_BR_NONE without any to_plm bits set?
> diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
> index 72f2adcda7c68..16977e4c6f8a5 100644
> --- a/arch/x86/events/intel/lbr.c
> +++ b/arch/x86/events/intel/lbr.c
> @@ -1245,7 +1245,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
> }
>
> /* if type does not correspond, then discard */
> - if (type == X86_BR_NONE || (br_sel & type) != type) {
> + if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
While masking out ~X86_BR_PLM doesn't appear to break the software fallback
behavior since (0 & ~X86_BR_PLM) == 0, the original check for X86_BR_NONE
appears to have been functional and necessary for that path.
Could the commit message be updated to clarify that the check was not actually
dead code for the software fallback path?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260414021440.928068-1-dapeng1.mi@linux.intel.com?part=1
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter()
2026-04-14 2:53 ` [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter() sashiko-bot
@ 2026-04-14 3:00 ` Mi, Dapeng
0 siblings, 0 replies; 10+ messages in thread
From: Mi, Dapeng @ 2026-04-14 3:00 UTC (permalink / raw)
To: sashiko; +Cc: linux-perf-users
On 4/14/2026 10:53 AM, sashiko-bot@kernel.org wrote:
> Sashiko AI review found 1 potential issue(s):
> - [Medium] Meaningful code and commit message mismatch due to a false premise
> --
>
> commit 98c2839085a2b609f94d9f3f338217d29525fd08
> Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
>
> perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter()
>
> This commit modifies the branch filter logic to mask out privilege level
> bits before checking if a branch type is X86_BR_NONE.
>
>> In intel_pmu_lbr_filter(), the 'type' variable is bitwise ORed with
>> 'to_plm' (which contains X86_BR_USER and/or X86_BR_KERNEL bits). Because
>> of this, 'type' can never equal X86_BR_NONE (0) after the assignment.
>>
>> As a result, the subsequent check 'if (type == X86_BR_NONE)' is dead code
>> and the entries with X86_BR_NONE type would not be skipped eventually.
> Is this description entirely accurate?
>
> Looking at intel_pmu_lbr_filter(), the bitwise OR with to_plm only happens
> in the X86_FEATURE_ARCH_LBR path:
>
> intel_pmu_lbr_filter() {
> ...
> if (static_cpu_has(X86_FEATURE_ARCH_LBR) &&
> type <= ARCH_LBR_BR_TYPE_KNOWN_MAX) {
> to_plm = kernel_ip(to) ? X86_BR_KERNEL : X86_BR_USER;
> type = arch_lbr_br_type_map[type] | to_plm;
> } else
> type = branch_type(from, to, cpuc->lbr_entries[i].abort);
> ...
> }
>
> In the software fallback path, type is assigned the result of branch_type().
> If branch_type() cannot decode the branch and returns X86_BR_NONE (0),
> wouldn't type evaluate to exactly X86_BR_NONE without any to_plm bits set?
No, the branch_type() would bitwise ored the to_plm at last as below code
shows.
```
/*
* branch priv level determined by target as
* is done by HW when LBR_SELECT is implemented
*/
if (ret != X86_BR_NONE)
ret |= to_plm;
```
Thanks.
>
>> diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
>> index 72f2adcda7c68..16977e4c6f8a5 100644
>> --- a/arch/x86/events/intel/lbr.c
>> +++ b/arch/x86/events/intel/lbr.c
>> @@ -1245,7 +1245,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
>> }
>>
>> /* if type does not correspond, then discard */
>> - if (type == X86_BR_NONE || (br_sel & type) != type) {
>> + if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
> While masking out ~X86_BR_PLM doesn't appear to break the software fallback
> behavior since (0 & ~X86_BR_PLM) == 0, the original check for X86_BR_NONE
> appears to have been functional and necessary for that path.
>
> Could the commit message be updated to clarify that the check was not actually
> dead code for the software fallback path?
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack
2026-04-14 2:14 ` [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack Dapeng Mi
@ 2026-04-14 3:16 ` sashiko-bot
2026-04-14 5:41 ` Mi, Dapeng
2026-04-29 20:57 ` Chen, Zide
1 sibling, 1 reply; 10+ messages in thread
From: sashiko-bot @ 2026-04-14 3:16 UTC (permalink / raw)
To: Dapeng Mi; +Cc: linux-perf-users
Sashiko AI review found 1 potential issue(s):
- [High] The patch addresses kernel address leakage in Intel's LBR software filter but overlooks identical vulnerabilities in AMD's LBR and BRS software filters.
--
commit 8dbb741871f5824e6032a6a5fa35f5db78494ff9
Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
perf/x86/intel: Fix kernel address leakages in LBR stack
This commit fixes a kernel address leakage in the Intel LBR stack by extending
the software filtering to check both the source and destination addresses.
Prior to this change, kernel-to-user branches like SYSRET could leak kernel
addresses to user space even when PERF_SAMPLE_BRANCH_USER was requested.
> diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
> index 16977e4c6f8a5..deef81c16571a 100644
> --- a/arch/x86/events/intel/lbr.c
> +++ b/arch/x86/events/intel/lbr.c
[ ... ]
> @@ -1244,8 +1244,15 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
> type |= X86_BR_NO_TX;
> }
>
> - /* if type does not correspond, then discard */
> - if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
> + from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER;
> + /*
> + * If type does not correspond, then discard.
> + * Especially filter out the entries whose from or to address
> + * is a kernel address while only X86_BR_USER is set. This prevents
> + * kernel address from being leaked into a user-space-only LBR stack.
> + */
> + if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type ||
> + (!(br_sel & X86_BR_KERNEL) && (from_plm & X86_BR_KERNEL))) {
> cpuc->lbr_entries[i].from = 0;
> compress = true;
> }
This patch successfully addresses the kernel address leakage in Intel's LBR
software filter, but does it leave AMD platforms exposed to the same KASLR
bypass?
Looking at arch/x86/events/amd/lbr.c, amd_pmu_lbr_filter() appears to classify
branch privilege levels based exclusively on the destination address via
branch_type_fused(). Does this mean it classifies kernel-to-user branches as
user-space and leaks the source kernel address?
Similarly in arch/x86/events/amd/brs.c, amd_brs_drain() filters branches
using amd_brs_match_plm() which explicitly checks only kernel_ip(to). Would
this also blindly accept the branch and log the source kernel address?
Should equivalent privilege level checks for the source address be added to
the AMD routines to fully resolve the issue across platforms?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260414021440.928068-1-dapeng1.mi@linux.intel.com?part=2
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack
2026-04-14 3:16 ` sashiko-bot
@ 2026-04-14 5:41 ` Mi, Dapeng
0 siblings, 0 replies; 10+ messages in thread
From: Mi, Dapeng @ 2026-04-14 5:41 UTC (permalink / raw)
To: sashiko; +Cc: linux-perf-users
On 4/14/2026 11:16 AM, sashiko-bot@kernel.org wrote:
> Sashiko AI review found 1 potential issue(s):
> - [High] The patch addresses kernel address leakage in Intel's LBR software filter but overlooks identical vulnerabilities in AMD's LBR and BRS software filters.
> --
>
> commit 8dbb741871f5824e6032a6a5fa35f5db78494ff9
> Author: Dapeng Mi <dapeng1.mi@linux.intel.com>
>
> perf/x86/intel: Fix kernel address leakages in LBR stack
>
> This commit fixes a kernel address leakage in the Intel LBR stack by extending
> the software filtering to check both the source and destination addresses.
> Prior to this change, kernel-to-user branches like SYSRET could leak kernel
> addresses to user space even when PERF_SAMPLE_BRANCH_USER was requested.
>
>> diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
>> index 16977e4c6f8a5..deef81c16571a 100644
>> --- a/arch/x86/events/intel/lbr.c
>> +++ b/arch/x86/events/intel/lbr.c
> [ ... ]
>> @@ -1244,8 +1244,15 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
>> type |= X86_BR_NO_TX;
>> }
>>
>> - /* if type does not correspond, then discard */
>> - if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
>> + from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER;
>> + /*
>> + * If type does not correspond, then discard.
>> + * Especially filter out the entries whose from or to address
>> + * is a kernel address while only X86_BR_USER is set. This prevents
>> + * kernel address from being leaked into a user-space-only LBR stack.
>> + */
>> + if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type ||
>> + (!(br_sel & X86_BR_KERNEL) && (from_plm & X86_BR_KERNEL))) {
>> cpuc->lbr_entries[i].from = 0;
>> compress = true;
>> }
> This patch successfully addresses the kernel address leakage in Intel's LBR
> software filter, but does it leave AMD platforms exposed to the same KASLR
> bypass?
>
> Looking at arch/x86/events/amd/lbr.c, amd_pmu_lbr_filter() appears to classify
> branch privilege levels based exclusively on the destination address via
> branch_type_fused(). Does this mean it classifies kernel-to-user branches as
> user-space and leaks the source kernel address?
>
> Similarly in arch/x86/events/amd/brs.c, amd_brs_drain() filters branches
> using amd_brs_match_plm() which explicitly checks only kernel_ip(to). Would
> this also blindly accept the branch and log the source kernel address?
>
> Should equivalent privilege level checks for the source address be added to
> the AMD routines to fully resolve the issue across platforms?
AMD guys have known the issue, suppose they would post the fix in a
separate patch. Thanks.
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack
2026-04-14 2:14 ` [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack Dapeng Mi
2026-04-14 3:16 ` sashiko-bot
@ 2026-04-29 20:57 ` Chen, Zide
2026-04-30 1:22 ` Mi, Dapeng
1 sibling, 1 reply; 10+ messages in thread
From: Chen, Zide @ 2026-04-29 20:57 UTC (permalink / raw)
To: Dapeng Mi, Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Falcon Thomas,
Xudong Hao, stable
On 4/13/2026 7:14 PM, Dapeng Mi wrote:
> Prior to the arch-LBR which supports CPL filtering, the kernel address
> could be leaked to user space even PERF_SAMPLE_BRANCH_USER is required.
This sounds correct to catch these branches, since only the target CPL
counts. The software filtering is implemented to match the HW behavior:
Legacy LBR.
CPL_EQ_0: When set, do not capture branches ending in ring 0
CPL_NEQ_0: When set, do not capture branches ending in ring >0
Arch LBR:
For operations which change the CPL, the operation is recorded in LBRs
only if the CPL at the end of the operation is enabled for LBR
recording. In cases where the CPL transitions from a value that is
filtered out to a value that is enabled for LBR
recording, the FROM_IP address for the recorded CPL transition branch or
event will be 0FFFFFFFFFFFFFFFFH.
>
> e.g., run below command on Intel Tigerlake platform,
>
> ```
> $./perf record -e cycles:p -o - --branch-filter any,save_type,u -- \
> ./perf bench syscall basic --loop 1000 | \
> ./perf script -i - --fields brstack|tr ' ' '\n'| \
> grep -E '0x[89a-f][0-9a-f]{15}'
>
> Total time: 0.000 [sec]
>
> 0.219000 usecs/op
> 4,566,210 ops/sec
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.551 MB - ]
> 0xffffffff93c001c8/0x7f12a2b1d647/P/-/-/16959/SYSRET/-
> 0xffffffff93c001c8/0x7f12a2b1d5c2/P/-/-/17535/SYSRET/-
> 0xffffffff93c01928/0x7f12a2861000/P/-/-/6719/ERET/-
> 0xffffffff93c01928/0x7f12a297a000/P/-/-/8575/ERET/-
> ```
Thus, filtering with USR=1, it's correct that ERET/SYSRET show up in the
above command running on TG because the target CPL is 3. However, the
from address should be hidden with 0xFFFFFFFFFFFFFFFF.
Instead, this appears a bug on platforms with CPL filtering (SPR, etc.)
that filters out these branches. This is because for ERET/SYSRET, the
br_type is 8 (OTHER_BRANCH), so even on arch LBR, it gets the type from
branch_type() which incorrectly translates type 8 to 0 (X86_BR_NONE).
Therefore, something needs to be done in get_branch_type() to handle
OTHER_BRANCH.
> The SYSRET/ERET branch calls are found the in the LBR stack, whose "from"
> addresses are obviously kernel address.
>
> Currently intel_pmu_lbr_filter() only filters out the LBR entries whose
> "to" address is a kernel address but doesn't check the "from" address.
>
> To fix the issue, extend the software filtering to both "from" and "to"
> addresses.
>
> Cc: stable@vger.kernel.org
> Reported-by: Ian Rogers <irogers@google.com>
> Fixes: 47125db27e47 ("perf/x86/intel/lbr: Support Architectural LBR")
> Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
> ---
> arch/x86/events/intel/lbr.c | 13 ++++++++++---
> 1 file changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
> index 16977e4c6f8a..deef81c16571 100644
> --- a/arch/x86/events/intel/lbr.c
> +++ b/arch/x86/events/intel/lbr.c
> @@ -1212,7 +1212,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
> {
> u64 from, to;
> int br_sel = cpuc->br_sel;
> - int i, j, type, to_plm;
> + int i, j, type, to_plm, from_plm;
> bool compress = false;
>
> /* if sampling all branches, then nothing to filter */
> @@ -1244,8 +1244,15 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
> type |= X86_BR_NO_TX;
> }
>
> - /* if type does not correspond, then discard */
> - if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
> + from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER;
> + /*
> + * If type does not correspond, then discard.
> + * Especially filter out the entries whose from or to address
> + * is a kernel address while only X86_BR_USER is set. This prevents
> + * kernel address from being leaked into a user-space-only LBR stack.
> + */
> + if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type ||
> + (!(br_sel & X86_BR_KERNEL) && (from_plm & X86_BR_KERNEL))) {
> cpuc->lbr_entries[i].from = 0;
> compress = true;
> }
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter()
2026-04-14 2:14 [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter() Dapeng Mi
2026-04-14 2:14 ` [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack Dapeng Mi
2026-04-14 2:53 ` [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter() sashiko-bot
@ 2026-04-29 20:58 ` Chen, Zide
2026-04-30 0:42 ` Mi, Dapeng
2 siblings, 1 reply; 10+ messages in thread
From: Chen, Zide @ 2026-04-29 20:58 UTC (permalink / raw)
To: Dapeng Mi, Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Falcon Thomas,
Xudong Hao, stable
On 4/13/2026 7:14 PM, Dapeng Mi wrote:
> In intel_pmu_lbr_filter(), the 'type' variable is bitwise ORed with
> 'to_plm' (which contains X86_BR_USER and/or X86_BR_KERNEL bits). Because
> of this, 'type' can never equal X86_BR_NONE (0) after the assignment.
Nit: In legacy LBR case, it could if get_branch_type() returns X86_BR_NONE.
>
> As a result, the subsequent check 'if (type == X86_BR_NONE)' is dead code
> and the entries with X86_BR_NONE type would not be skipped eventually.
>
> Correct this by masking out the X86_BR_KERNEL and X86_BR_USER bits
> before performing the X86_BR_NONE comparison.
>
> Cc: stable@vger.kernel.org
> Fixes: 47125db27e47 ("perf/x86/intel/lbr: Support Architectural LBR")
> Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
> ---
> arch/x86/events/intel/lbr.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
> index 72f2adcda7c6..16977e4c6f8a 100644
> --- a/arch/x86/events/intel/lbr.c
> +++ b/arch/x86/events/intel/lbr.c
> @@ -1245,7 +1245,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
> }
>
> /* if type does not correspond, then discard */
> - if (type == X86_BR_NONE || (br_sel & type) != type) {
> + if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
> cpuc->lbr_entries[i].from = 0;
> compress = true;
> }
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter()
2026-04-29 20:58 ` Chen, Zide
@ 2026-04-30 0:42 ` Mi, Dapeng
0 siblings, 0 replies; 10+ messages in thread
From: Mi, Dapeng @ 2026-04-30 0:42 UTC (permalink / raw)
To: Chen, Zide, Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Falcon Thomas,
Xudong Hao, stable
On 4/30/2026 4:58 AM, Chen, Zide wrote:
>
> On 4/13/2026 7:14 PM, Dapeng Mi wrote:
>> In intel_pmu_lbr_filter(), the 'type' variable is bitwise ORed with
>> 'to_plm' (which contains X86_BR_USER and/or X86_BR_KERNEL bits). Because
>> of this, 'type' can never equal X86_BR_NONE (0) after the assignment.
> Nit: In legacy LBR case, it could if get_branch_type() returns X86_BR_NONE.
Yeah, it's correct and need to change the description slightly.
Thanks.
>
>> As a result, the subsequent check 'if (type == X86_BR_NONE)' is dead code
>> and the entries with X86_BR_NONE type would not be skipped eventually.
>>
>> Correct this by masking out the X86_BR_KERNEL and X86_BR_USER bits
>> before performing the X86_BR_NONE comparison.
>>
>> Cc: stable@vger.kernel.org
>> Fixes: 47125db27e47 ("perf/x86/intel/lbr: Support Architectural LBR")
>> Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
>> ---
>> arch/x86/events/intel/lbr.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
>> index 72f2adcda7c6..16977e4c6f8a 100644
>> --- a/arch/x86/events/intel/lbr.c
>> +++ b/arch/x86/events/intel/lbr.c
>> @@ -1245,7 +1245,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
>> }
>>
>> /* if type does not correspond, then discard */
>> - if (type == X86_BR_NONE || (br_sel & type) != type) {
>> + if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
>> cpuc->lbr_entries[i].from = 0;
>> compress = true;
>> }
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack
2026-04-29 20:57 ` Chen, Zide
@ 2026-04-30 1:22 ` Mi, Dapeng
0 siblings, 0 replies; 10+ messages in thread
From: Mi, Dapeng @ 2026-04-30 1:22 UTC (permalink / raw)
To: Chen, Zide, Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Ian Rogers, Adrian Hunter, Alexander Shishkin,
Andi Kleen, Eranian Stephane
Cc: linux-kernel, linux-perf-users, Dapeng Mi, Falcon Thomas,
Xudong Hao, stable
On 4/30/2026 4:57 AM, Chen, Zide wrote:
>
> On 4/13/2026 7:14 PM, Dapeng Mi wrote:
>> Prior to the arch-LBR which supports CPL filtering, the kernel address
>> could be leaked to user space even PERF_SAMPLE_BRANCH_USER is required.
> This sounds correct to catch these branches, since only the target CPL
> counts. The software filtering is implemented to match the HW behavior:
>
> Legacy LBR.
> CPL_EQ_0: When set, do not capture branches ending in ring 0
> CPL_NEQ_0: When set, do not capture branches ending in ring >0
>
> Arch LBR:
> For operations which change the CPL, the operation is recorded in LBRs
> only if the CPL at the end of the operation is enabled for LBR
> recording. In cases where the CPL transitions from a value that is
> filtered out to a value that is enabled for LBR
> recording, the FROM_IP address for the recorded CPL transition branch or
> event will be 0FFFFFFFFFFFFFFFFH.
Yes, this is exactly what HW does.
>
>> e.g., run below command on Intel Tigerlake platform,
>>
>> ```
>> $./perf record -e cycles:p -o - --branch-filter any,save_type,u -- \
>> ./perf bench syscall basic --loop 1000 | \
>> ./perf script -i - --fields brstack|tr ' ' '\n'| \
>> grep -E '0x[89a-f][0-9a-f]{15}'
>>
>> Total time: 0.000 [sec]
>>
>> 0.219000 usecs/op
>> 4,566,210 ops/sec
>> [ perf record: Woken up 1 times to write data ]
>> [ perf record: Captured and wrote 0.551 MB - ]
>> 0xffffffff93c001c8/0x7f12a2b1d647/P/-/-/16959/SYSRET/-
>> 0xffffffff93c001c8/0x7f12a2b1d5c2/P/-/-/17535/SYSRET/-
>> 0xffffffff93c01928/0x7f12a2861000/P/-/-/6719/ERET/-
>> 0xffffffff93c01928/0x7f12a297a000/P/-/-/8575/ERET/-
>> ```
> Thus, filtering with USR=1, it's correct that ERET/SYSRET show up in the
> above command running on TG because the target CPL is 3. However, the
> from address should be hidden with 0xFFFFFFFFFFFFFFFF.
Hmm, in theory forcing the "from" IP to 0xFFFFFFFFFFFFFFFF seems a better
idea, don't lose SYSRET/ERET trace and no kernel address leakage as well.
It works for legacy LBR, but arch-LBR HW already forces the "from" IP to
0xFFFFFFFFFFFFFFFF , SW has no way to figure out the real branch type, so
the branch type has to be converted X86_BR_NONE which leads to this entry
is dropped.
Beside, I'm not sure if this would break current test case. @Ian, how's
your idea on this?
>
> Instead, this appears a bug on platforms with CPL filtering (SPR, etc.)
> that filters out these branches. This is because for ERET/SYSRET, the
> br_type is 8 (OTHER_BRANCH), so even on arch LBR, it gets the type from
> branch_type() which incorrectly translates type 8 to 0 (X86_BR_NONE).
As above explained, this doesn't work for arch-LBR.
Thanks.
>
> Therefore, something needs to be done in get_branch_type() to handle
> OTHER_BRANCH.
>
>> The SYSRET/ERET branch calls are found the in the LBR stack, whose "from"
>> addresses are obviously kernel address.
>>
>> Currently intel_pmu_lbr_filter() only filters out the LBR entries whose
>> "to" address is a kernel address but doesn't check the "from" address.
>>
>> To fix the issue, extend the software filtering to both "from" and "to"
>> addresses.
>>
>> Cc: stable@vger.kernel.org
>> Reported-by: Ian Rogers <irogers@google.com>
>> Fixes: 47125db27e47 ("perf/x86/intel/lbr: Support Architectural LBR")
>> Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
>> ---
>> arch/x86/events/intel/lbr.c | 13 ++++++++++---
>> 1 file changed, 10 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
>> index 16977e4c6f8a..deef81c16571 100644
>> --- a/arch/x86/events/intel/lbr.c
>> +++ b/arch/x86/events/intel/lbr.c
>> @@ -1212,7 +1212,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
>> {
>> u64 from, to;
>> int br_sel = cpuc->br_sel;
>> - int i, j, type, to_plm;
>> + int i, j, type, to_plm, from_plm;
>> bool compress = false;
>>
>> /* if sampling all branches, then nothing to filter */
>> @@ -1244,8 +1244,15 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
>> type |= X86_BR_NO_TX;
>> }
>>
>> - /* if type does not correspond, then discard */
>> - if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type) {
>> + from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER;
>> + /*
>> + * If type does not correspond, then discard.
>> + * Especially filter out the entries whose from or to address
>> + * is a kernel address while only X86_BR_USER is set. This prevents
>> + * kernel address from being leaked into a user-space-only LBR stack.
>> + */
>> + if ((type & ~X86_BR_PLM) == X86_BR_NONE || (br_sel & type) != type ||
>> + (!(br_sel & X86_BR_KERNEL) && (from_plm & X86_BR_KERNEL))) {
>> cpuc->lbr_entries[i].from = 0;
>> compress = true;
>> }
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-04-30 1:22 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-14 2:14 [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter() Dapeng Mi
2026-04-14 2:14 ` [PATCH 2/2] perf/x86/intel: Fix kernel address leakages in LBR stack Dapeng Mi
2026-04-14 3:16 ` sashiko-bot
2026-04-14 5:41 ` Mi, Dapeng
2026-04-29 20:57 ` Chen, Zide
2026-04-30 1:22 ` Mi, Dapeng
2026-04-14 2:53 ` [PATCH 1/2] perf/x86/intel: Fix redundant branch type check in intel_pmu_lbr_filter() sashiko-bot
2026-04-14 3:00 ` Mi, Dapeng
2026-04-29 20:58 ` Chen, Zide
2026-04-30 0:42 ` Mi, Dapeng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox