* [PATCH] bpftool: Add CET-aware symbol matching for x86_64 architectures
@ 2025-06-26 6:11 Yuan Chen
2025-06-26 7:11 ` [PATCH v2] " Yuan Chen
2025-06-26 7:49 ` [PATCH v3] " Yuan Chen
0 siblings, 2 replies; 14+ messages in thread
From: Yuan Chen @ 2025-06-26 6:11 UTC (permalink / raw)
To: ast, qmo; +Cc: bpf, linux-kernel, chenyuan_fl, chenyuan
From: chenyuan <chenyuan@kylinos.cn>
Adjust symbol matching logic to account for Control-flow Enforcement
Technology (CET) on x86_64 systems. CET prefixes functions with a 4-byte
'endbr' instruction, shifting the actual entry point to symbol + 4.
Signed-off-by: chenyuan <chenyuan@kylinos.cn>
---
tools/bpf/bpftool/link.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
index 189bf312c206..96c62d8aff8e 100644
--- a/tools/bpf/bpftool/link.c
+++ b/tools/bpf/bpftool/link.c
@@ -744,8 +744,21 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (dd.sym_mapping[i].address != data[j].addr) {
+#if defined(__x86_64__) || defined(__amd64__)
+ /*
+ * On x86_64 architectures with CET (Control-flow Enforcement Technology),
+ * function entry points have a 4-byte 'endbr' instruction prefix.
+ * This causes the actual function address = symbol address + 4.
+ * Here we check if this symbol matches the target address minus 4,
+ * indicating we've found a CET-enabled function entry point.
+ */
+ if (dd.sym_mapping[i].address == data[j].addr - 4)
+ goto found;
+#endif
continue;
+ }
+found:
printf("\n\t%016lx %-16llx %s",
dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
if (dd.sym_mapping[i].module[0] != '\0')
--
2.25.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2] bpftool: Add CET-aware symbol matching for x86_64 architectures
2025-06-26 6:11 [PATCH] bpftool: Add CET-aware symbol matching for x86_64 architectures Yuan Chen
@ 2025-06-26 7:11 ` Yuan Chen
2025-06-26 7:49 ` [PATCH v3] " Yuan Chen
1 sibling, 0 replies; 14+ messages in thread
From: Yuan Chen @ 2025-06-26 7:11 UTC (permalink / raw)
To: ast, qmo; +Cc: bpf, linux-kernel, chenyuan_fl, Yuan Chen
From: Yuan Chen <chenyuan@kylinos.cn>
Adjust symbol matching logic to account for Control-flow Enforcement
Technology (CET) on x86_64 systems. CET prefixes functions with a 4-byte
'endbr' instruction, shifting the actual entry point to symbol + 4.
Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
tools/bpf/bpftool/link.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
index 189bf312c206..96c62d8aff8e 100644
--- a/tools/bpf/bpftool/link.c
+++ b/tools/bpf/bpftool/link.c
@@ -744,8 +744,21 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (dd.sym_mapping[i].address != data[j].addr) {
+#if defined(__x86_64__) || defined(__amd64__)
+ /*
+ * On x86_64 architectures with CET (Control-flow Enforcement Technology),
+ * function entry points have a 4-byte 'endbr' instruction prefix.
+ * This causes the actual function address = symbol address + 4.
+ * Here we check if this symbol matches the target address minus 4,
+ * indicating we've found a CET-enabled function entry point.
+ */
+ if (dd.sym_mapping[i].address == data[j].addr - 4)
+ goto found;
+#endif
continue;
+ }
+found:
printf("\n\t%016lx %-16llx %s",
dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
if (dd.sym_mapping[i].module[0] != '\0')
--
2.25.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v3] bpftool: Add CET-aware symbol matching for x86_64 architectures
2025-06-26 6:11 [PATCH] bpftool: Add CET-aware symbol matching for x86_64 architectures Yuan Chen
2025-06-26 7:11 ` [PATCH v2] " Yuan Chen
@ 2025-06-26 7:49 ` Yuan Chen
2025-06-27 11:08 ` Quentin Monnet
2025-07-01 2:31 ` Yonghong Song
1 sibling, 2 replies; 14+ messages in thread
From: Yuan Chen @ 2025-06-26 7:49 UTC (permalink / raw)
To: ast, qmo; +Cc: bpf, linux-kernel, chenyuan_fl, Yuan Chen
From: Yuan Chen <chenyuan@kylinos.cn>
Adjust symbol matching logic to account for Control-flow Enforcement
Technology (CET) on x86_64 systems. CET prefixes functions with a 4-byte
'endbr' instruction, shifting the actual entry point to symbol + 4.
Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
tools/bpf/bpftool/link.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
index 03513ffffb79..dfd192b4c5ad 100644
--- a/tools/bpf/bpftool/link.c
+++ b/tools/bpf/bpftool/link.c
@@ -307,8 +307,21 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
goto error;
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (dd.sym_mapping[i].address != data[j].addr) {
+#if defined(__x86_64__) || defined(__amd64__)
+ /*
+ * On x86_64 architectures with CET (Control-flow Enforcement Technology),
+ * function entry points have a 4-byte 'endbr' instruction prefix.
+ * This causes the actual function address = symbol address + 4.
+ * Here we check if this symbol matches the target address minus 4,
+ * indicating we've found a CET-enabled function entry point.
+ */
+ if (dd.sym_mapping[i].address == data[j].addr - 4)
+ goto found;
+#endif
continue;
+ }
+found:
jsonw_start_object(json_wtr);
jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
@@ -744,8 +757,21 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (dd.sym_mapping[i].address != data[j].addr) {
+#if defined(__x86_64__) || defined(__amd64__)
+ /*
+ * On x86_64 architectures with CET (Control-flow Enforcement Technology),
+ * function entry points have a 4-byte 'endbr' instruction prefix.
+ * This causes the actual function address = symbol address + 4.
+ * Here we check if this symbol matches the target address minus 4,
+ * indicating we've found a CET-enabled function entry point.
+ */
+ if (dd.sym_mapping[i].address == data[j].addr - 4)
+ goto found;
+#endif
continue;
+ }
+found:
printf("\n\t%016lx %-16llx %s",
dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
if (dd.sym_mapping[i].module[0] != '\0')
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v3] bpftool: Add CET-aware symbol matching for x86_64 architectures
2025-06-26 7:49 ` [PATCH v3] " Yuan Chen
@ 2025-06-27 11:08 ` Quentin Monnet
2025-07-11 6:35 ` chenyuan
2025-07-01 2:31 ` Yonghong Song
1 sibling, 1 reply; 14+ messages in thread
From: Quentin Monnet @ 2025-06-27 11:08 UTC (permalink / raw)
To: Yuan Chen, ast; +Cc: bpf, linux-kernel, Yuan Chen, Jiri Olsa
Thanks! Next time, please try to add all relevant maintainers as
recipients or in copy of your message when submitting patches. You can
get the list with get_maintainer.pl, try running it on your patch or with
"./scripts/get_maintainer.pl -f tools/bpf/bpftool/link.c"
2025-06-26 15:49 UTC+0800 ~ Yuan Chen <chenyuan_fl@163.com>
> From: Yuan Chen <chenyuan@kylinos.cn>
>
> Adjust symbol matching logic to account for Control-flow Enforcement
> Technology (CET) on x86_64 systems. CET prefixes functions with a 4-byte
> 'endbr' instruction, shifting the actual entry point to symbol + 4.
>
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
> tools/bpf/bpftool/link.c | 30 ++++++++++++++++++++++++++++--
> 1 file changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
> index 03513ffffb79..dfd192b4c5ad 100644
> --- a/tools/bpf/bpftool/link.c
> +++ b/tools/bpf/bpftool/link.c
> @@ -307,8 +307,21 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
> goto error;
>
> for (i = 0; i < dd.sym_count; i++) {
> - if (dd.sym_mapping[i].address != data[j].addr)
> + if (dd.sym_mapping[i].address != data[j].addr) {
> +#if defined(__x86_64__) || defined(__amd64__)
I'm not familiar with CET, but from what I read, it's been around since
Tiger Lake processors (2020). Do we have a risk of false positive with
older CPUs? Maybe check that the instruction at
dd.sym_mapping[i].address is endbr32 or endbr34?
> + /*
> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
> + * function entry points have a 4-byte 'endbr' instruction prefix.
> + * This causes the actual function address = symbol address + 4.
> + * Here we check if this symbol matches the target address minus 4,
> + * indicating we've found a CET-enabled function entry point.
> + */
> + if (dd.sym_mapping[i].address == data[j].addr - 4)
> + goto found;
> +#endif
> continue;
> + }
> +found:
> jsonw_start_object(json_wtr);
> jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
I suppose we still want to print dd.sym_mapping[i].address (and not
data[j].addr) when we found it with the CET offset here - just
double-checking.
> jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
> @@ -744,8 +757,21 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
>
> printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
> for (i = 0; i < dd.sym_count; i++) {
> - if (dd.sym_mapping[i].address != data[j].addr)
> + if (dd.sym_mapping[i].address != data[j].addr) {
> +#if defined(__x86_64__) || defined(__amd64__)
> + /*
> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
> + * function entry points have a 4-byte 'endbr' instruction prefix.
> + * This causes the actual function address = symbol address + 4.
> + * Here we check if this symbol matches the target address minus 4,
> + * indicating we've found a CET-enabled function entry point.
> + */
> + if (dd.sym_mapping[i].address == data[j].addr - 4)
> + goto found;
> +#endif
Given that we have twice the same check, I'd move this to a dedicated
wrapper function that we could call from both show_kprobe_multi_json()
and show_kprobe_multi_plain().
> continue;
> + }
> +found:
> printf("\n\t%016lx %-16llx %s",
> dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
> if (dd.sym_mapping[i].module[0] != '\0')
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] bpftool: Add CET-aware symbol matching for x86_64 architectures
2025-06-26 7:49 ` [PATCH v3] " Yuan Chen
2025-06-27 11:08 ` Quentin Monnet
@ 2025-07-01 2:31 ` Yonghong Song
2025-07-11 7:07 ` chenyuan
1 sibling, 1 reply; 14+ messages in thread
From: Yonghong Song @ 2025-07-01 2:31 UTC (permalink / raw)
To: Yuan Chen, ast, qmo; +Cc: bpf, linux-kernel, Yuan Chen
On 6/26/25 12:49 AM, Yuan Chen wrote:
> From: Yuan Chen <chenyuan@kylinos.cn>
>
> Adjust symbol matching logic to account for Control-flow Enforcement
> Technology (CET) on x86_64 systems. CET prefixes functions with a 4-byte
> 'endbr' instruction, shifting the actual entry point to symbol + 4.
>
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
> tools/bpf/bpftool/link.c | 30 ++++++++++++++++++++++++++++--
> 1 file changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
> index 03513ffffb79..dfd192b4c5ad 100644
> --- a/tools/bpf/bpftool/link.c
> +++ b/tools/bpf/bpftool/link.c
> @@ -307,8 +307,21 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
> goto error;
>
> for (i = 0; i < dd.sym_count; i++) {
> - if (dd.sym_mapping[i].address != data[j].addr)
> + if (dd.sym_mapping[i].address != data[j].addr) {
> +#if defined(__x86_64__) || defined(__amd64__)
> + /*
> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
> + * function entry points have a 4-byte 'endbr' instruction prefix.
> + * This causes the actual function address = symbol address + 4.
> + * Here we check if this symbol matches the target address minus 4,
> + * indicating we've found a CET-enabled function entry point.
> + */
> + if (dd.sym_mapping[i].address == data[j].addr - 4)
> + goto found;
> +#endif
In kernel/trace/bpf_trace.c, I see
static inline unsigned long get_entry_ip(unsigned long fentry_ip)
{
#ifdef CONFIG_X86_KERNEL_IBT
if (is_endbr((void *)(fentry_ip - ENDBR_INSN_SIZE)))
fentry_ip -= ENDBR_INSN_SIZE;
#endif
return fentry_ip;
}
Could you explain why arm64 also need to do checking
if (dd.sym_mapping[i].address == data[j].addr - 4)
like x86_64?
> continue;
> + }
> +found:
> jsonw_start_object(json_wtr);
> jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
> jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
> @@ -744,8 +757,21 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
>
> printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
> for (i = 0; i < dd.sym_count; i++) {
> - if (dd.sym_mapping[i].address != data[j].addr)
> + if (dd.sym_mapping[i].address != data[j].addr) {
> +#if defined(__x86_64__) || defined(__amd64__)
> + /*
> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
> + * function entry points have a 4-byte 'endbr' instruction prefix.
> + * This causes the actual function address = symbol address + 4.
> + * Here we check if this symbol matches the target address minus 4,
> + * indicating we've found a CET-enabled function entry point.
> + */
> + if (dd.sym_mapping[i].address == data[j].addr - 4)
> + goto found;
> +#endif
> continue;
> + }
> +found:
> printf("\n\t%016lx %-16llx %s",
> dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
> if (dd.sym_mapping[i].module[0] != '\0')
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re:Re: [PATCH v3] bpftool: Add CET-aware symbol matching for x86_64 architectures
2025-06-27 11:08 ` Quentin Monnet
@ 2025-07-11 6:35 ` chenyuan
0 siblings, 0 replies; 14+ messages in thread
From: chenyuan @ 2025-07-11 6:35 UTC (permalink / raw)
To: Quentin Monnet; +Cc: ast, bpf, linux-kernel, Yuan Chen, Jiri Olsa
Thank you for reviewing the patch and providing valuable feedback! I appreciate your insights on CET
compatibility and code structure. Here are my responses to your points:
1. Maintainer List
I confirm that in future submissions, I will run:
./scripts/get_maintainer.pl -f tools/bpf/bpftool/link.c
to ensure all relevant maintainers are included in the recipient list . This was an oversight in the initial submission.
2. False Positives on Older CPUs
Your concern about older CPUs is valid. To address this:
Current Approach: The patch relies on address offset matching (symbol_addr == target_addr - 4), which is safe because:
Non-CET functions won’t have a valid symbol at target_addr - 4 .
Symbol tables are deterministic, so accidental matches at addr - 4 are statistically negligible.
Instruction Verification: While checking for endbr32/endbr64 would be ideal, user-space cannot directly inspect kernel instruction memory for security and portability reasons.
Could you advise if there are any safe methods to verify the presence of endbr32/endbr64 instructions at kernel symbol addresses from user space?
At 2025-06-27 19:08:48, "Quentin Monnet" <qmo@kernel.org> wrote:
>Thanks! Next time, please try to add all relevant maintainers as
>recipients or in copy of your message when submitting patches. You can
>get the list with get_maintainer.pl, try running it on your patch or with
>"./scripts/get_maintainer.pl -f tools/bpf/bpftool/link.c"
>
>2025-06-26 15:49 UTC+0800 ~ Yuan Chen <chenyuan_fl@163.com>
>> From: Yuan Chen <chenyuan@kylinos.cn>
>>
>> Adjust symbol matching logic to account for Control-flow Enforcement
>> Technology (CET) on x86_64 systems. CET prefixes functions with a 4-byte
>> 'endbr' instruction, shifting the actual entry point to symbol + 4.
>>
>> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
>> ---
>> tools/bpf/bpftool/link.c | 30 ++++++++++++++++++++++++++++--
>> 1 file changed, 28 insertions(+), 2 deletions(-)
>>
>> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
>> index 03513ffffb79..dfd192b4c5ad 100644
>> --- a/tools/bpf/bpftool/link.c
>> +++ b/tools/bpf/bpftool/link.c
>> @@ -307,8 +307,21 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
>> goto error;
>>
>> for (i = 0; i < dd.sym_count; i++) {
>> - if (dd.sym_mapping[i].address != data[j].addr)
>> + if (dd.sym_mapping[i].address != data[j].addr) {
>> +#if defined(__x86_64__) || defined(__amd64__)
>
>
>I'm not familiar with CET, but from what I read, it's been around since
>Tiger Lake processors (2020). Do we have a risk of false positive with
>older CPUs? Maybe check that the instruction at
>dd.sym_mapping[i].address is endbr32 or endbr34?
>
>
>> + /*
>> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
>> + * function entry points have a 4-byte 'endbr' instruction prefix.
>> + * This causes the actual function address = symbol address + 4.
>> + * Here we check if this symbol matches the target address minus 4,
>> + * indicating we've found a CET-enabled function entry point.
>> + */
>> + if (dd.sym_mapping[i].address == data[j].addr - 4)
>> + goto found;
>> +#endif
>> continue;
>> + }
>> +found:
>> jsonw_start_object(json_wtr);
>> jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
>
>
>I suppose we still want to print dd.sym_mapping[i].address (and not
>data[j].addr) when we found it with the CET offset here - just
>double-checking.
>
>
>> jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
>> @@ -744,8 +757,21 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
>>
>> printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
>> for (i = 0; i < dd.sym_count; i++) {
>> - if (dd.sym_mapping[i].address != data[j].addr)
>> + if (dd.sym_mapping[i].address != data[j].addr) {
>> +#if defined(__x86_64__) || defined(__amd64__)
>> + /*
>> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
>> + * function entry points have a 4-byte 'endbr' instruction prefix.
>> + * This causes the actual function address = symbol address + 4.
>> + * Here we check if this symbol matches the target address minus 4,
>> + * indicating we've found a CET-enabled function entry point.
>> + */
>> + if (dd.sym_mapping[i].address == data[j].addr - 4)
>> + goto found;
>> +#endif
>
>
>Given that we have twice the same check, I'd move this to a dedicated
>wrapper function that we could call from both show_kprobe_multi_json()
>and show_kprobe_multi_plain().
>
>
>> continue;
>> + }
>> +found:
>> printf("\n\t%016lx %-16llx %s",
>> dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
>> if (dd.sym_mapping[i].module[0] != '\0')
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re:Re: [PATCH v3] bpftool: Add CET-aware symbol matching for x86_64 architectures
2025-07-01 2:31 ` Yonghong Song
@ 2025-07-11 7:07 ` chenyuan
2025-07-12 0:47 ` Yonghong Song
0 siblings, 1 reply; 14+ messages in thread
From: chenyuan @ 2025-07-11 7:07 UTC (permalink / raw)
To: Yonghong Song; +Cc: ast, qmo, bpf, linux-kernel, Yuan Chen
Thank you for your feedback! Does ARM64 require similar address adjustment detection? In my ARM64
environment with BTI enabled, bpftool correctly retrieves and prints function symbols. Could my verification
method be flawed?
Here’s a detailed explanation:
ARM64 BTI vs. x86 CET: Fundamental Differences
x86 CET (Control-flow Enforcement Technology):
Requires endbr32/endbr64 at function entries. Overwriting these instructions breaks CET protection .
Kernel logic (e.g., bpf_trace.c) adjusts symbol addresses by -4 to skip the endbr prefix .
ARM64 BTI (Branch Target Identification):
Uses BTI instructions as "landing pads" for indirect jumps. Kprobes can safely overwrite BTI instructions without triggering faults because:
Executing BTI, SG, or PACBTI clears EPSR.B (the enforcement flag), allowing subsequent non-BTI instructions .
Non-landing-pad instructions (e.g., probes) only fault if executed before EPSR.B is cleared – which doesn’t occur when probes replace BTI .
https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/armv8-1-m-pointer-authentication-and-branch-target-identification-extension
At 2025-07-01 10:31:41, "Yonghong Song" <yonghong.song@linux.dev> wrote:
>
>
>On 6/26/25 12:49 AM, Yuan Chen wrote:
>> From: Yuan Chen <chenyuan@kylinos.cn>
>>
>> Adjust symbol matching logic to account for Control-flow Enforcement
>> Technology (CET) on x86_64 systems. CET prefixes functions with a 4-byte
>> 'endbr' instruction, shifting the actual entry point to symbol + 4.
>>
>> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
>> ---
>> tools/bpf/bpftool/link.c | 30 ++++++++++++++++++++++++++++--
>> 1 file changed, 28 insertions(+), 2 deletions(-)
>>
>> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
>> index 03513ffffb79..dfd192b4c5ad 100644
>> --- a/tools/bpf/bpftool/link.c
>> +++ b/tools/bpf/bpftool/link.c
>> @@ -307,8 +307,21 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
>> goto error;
>>
>> for (i = 0; i < dd.sym_count; i++) {
>> - if (dd.sym_mapping[i].address != data[j].addr)
>> + if (dd.sym_mapping[i].address != data[j].addr) {
>> +#if defined(__x86_64__) || defined(__amd64__)
>> + /*
>> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
>> + * function entry points have a 4-byte 'endbr' instruction prefix.
>> + * This causes the actual function address = symbol address + 4.
>> + * Here we check if this symbol matches the target address minus 4,
>> + * indicating we've found a CET-enabled function entry point.
>> + */
>> + if (dd.sym_mapping[i].address == data[j].addr - 4)
>> + goto found;
>> +#endif
>
>In kernel/trace/bpf_trace.c, I see
>
>static inline unsigned long get_entry_ip(unsigned long fentry_ip)
>{
>#ifdef CONFIG_X86_KERNEL_IBT
> if (is_endbr((void *)(fentry_ip - ENDBR_INSN_SIZE)))
> fentry_ip -= ENDBR_INSN_SIZE;
>#endif
> return fentry_ip;
>}
>
>Could you explain why arm64 also need to do checking
> if (dd.sym_mapping[i].address == data[j].addr - 4)
>like x86_64?
>
>> continue;
>> + }
>> +found:
>> jsonw_start_object(json_wtr);
>> jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
>> jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
>> @@ -744,8 +757,21 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
>>
>> printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
>> for (i = 0; i < dd.sym_count; i++) {
>> - if (dd.sym_mapping[i].address != data[j].addr)
>> + if (dd.sym_mapping[i].address != data[j].addr) {
>> +#if defined(__x86_64__) || defined(__amd64__)
>> + /*
>> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
>> + * function entry points have a 4-byte 'endbr' instruction prefix.
>> + * This causes the actual function address = symbol address + 4.
>> + * Here we check if this symbol matches the target address minus 4,
>> + * indicating we've found a CET-enabled function entry point.
>> + */
>> + if (dd.sym_mapping[i].address == data[j].addr - 4)
>> + goto found;
>> +#endif
>> continue;
>> + }
>> +found:
>> printf("\n\t%016lx %-16llx %s",
>> dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
>> if (dd.sym_mapping[i].module[0] != '\0')
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] bpftool: Add CET-aware symbol matching for x86_64 architectures
2025-07-11 7:07 ` chenyuan
@ 2025-07-12 0:47 ` Yonghong Song
2025-07-21 12:51 ` chenyuan
0 siblings, 1 reply; 14+ messages in thread
From: Yonghong Song @ 2025-07-12 0:47 UTC (permalink / raw)
To: chenyuan; +Cc: ast, qmo, bpf, linux-kernel, Yuan Chen
On 7/11/25 12:07 AM, chenyuan wrote:
> Thank you for your feedback! Does ARM64 require similar address adjustment detection? In my ARM64
> environment with BTI enabled, bpftool correctly retrieves and prints function symbols. Could my verification
> method be flawed?
> Here’s a detailed explanation:
>
> ARM64 BTI vs. x86 CET: Fundamental Differences
>
> x86 CET (Control-flow Enforcement Technology):
> Requires endbr32/endbr64 at function entries. Overwriting these instructions breaks CET protection .
> Kernel logic (e.g., bpf_trace.c) adjusts symbol addresses by -4 to skip the endbr prefix .
This interpretation is not correct. The adjustment by -4 is not to skip the endbr prefix,
but to get the actual symbol address. For example,
ffffffff83809cb0 <bpf_fentry_test3>:
ffffffff83809cb0: f3 0f 1e fa endbr64
ffffffff83809cb4: 0f 1f 44 00 00 nopl (%rax,%rax)
ffffffff83809cb9: 8d 04 37 leal (%rdi,%rsi), %eax
ffffffff83809cbc: 01 d0 addl %edx, %eax
ffffffff83809cbe: 2e e9 6c d3 c8 00 jmp 0xffffffff84497030 <__x86_return_thunk>
ffffffff83809cc4: 66 66 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:(%rax,%rax)
The fentry_ip argument in func get_entry_ip() is 0xffffffff83809cb4. Adding -4
will get the value 0xffffffff83809cb0 which is the actual start of the function.
> ARM64 BTI (Branch Target Identification):
> Uses BTI instructions as "landing pads" for indirect jumps. Kprobes can safely overwrite BTI instructions without triggering faults because:
> Executing BTI, SG, or PACBTI clears EPSR.B (the enforcement flag), allowing subsequent non-BTI instructions .
> Non-landing-pad instructions (e.g., probes) only fault if executed before EPSR.B is cleared – which doesn’t occur when probes replace BTI .
I am not super familiar with arm64 bti. But from an arm64 kernel, with my config file (based on bpf CI),
I didn't find bti insns for tracable functions. So I double arm64 kernel will need address adjustment.
Otherwise, get_entry_ip() should do adjustment there.
It would be great if you can have an example to show arm64 also needs addr adjustment in bpftool
as in this patch.
>
> https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/armv8-1-m-pointer-authentication-and-branch-target-identification-extension
>
>
>
>
>
>
>
>
>
> At 2025-07-01 10:31:41, "Yonghong Song" <yonghong.song@linux.dev> wrote:
>>
>> On 6/26/25 12:49 AM, Yuan Chen wrote:
>>> From: Yuan Chen <chenyuan@kylinos.cn>
>>>
>>> Adjust symbol matching logic to account for Control-flow Enforcement
>>> Technology (CET) on x86_64 systems. CET prefixes functions with a 4-byte
>>> 'endbr' instruction, shifting the actual entry point to symbol + 4.
>>>
>>> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
>>> ---
>>> tools/bpf/bpftool/link.c | 30 ++++++++++++++++++++++++++++--
>>> 1 file changed, 28 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
>>> index 03513ffffb79..dfd192b4c5ad 100644
>>> --- a/tools/bpf/bpftool/link.c
>>> +++ b/tools/bpf/bpftool/link.c
>>> @@ -307,8 +307,21 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
>>> goto error;
>>>
>>> for (i = 0; i < dd.sym_count; i++) {
>>> - if (dd.sym_mapping[i].address != data[j].addr)
>>> + if (dd.sym_mapping[i].address != data[j].addr) {
>>> +#if defined(__x86_64__) || defined(__amd64__)
>>> + /*
>>> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
>>> + * function entry points have a 4-byte 'endbr' instruction prefix.
>>> + * This causes the actual function address = symbol address + 4.
>>> + * Here we check if this symbol matches the target address minus 4,
>>> + * indicating we've found a CET-enabled function entry point.
>>> + */
>>> + if (dd.sym_mapping[i].address == data[j].addr - 4)
>>> + goto found;
>>> +#endif
>> In kernel/trace/bpf_trace.c, I see
>>
>> static inline unsigned long get_entry_ip(unsigned long fentry_ip)
>> {
>> #ifdef CONFIG_X86_KERNEL_IBT
>> if (is_endbr((void *)(fentry_ip - ENDBR_INSN_SIZE)))
>> fentry_ip -= ENDBR_INSN_SIZE;
>> #endif
>> return fentry_ip;
>> }
>>
>> Could you explain why arm64 also need to do checking
>> if (dd.sym_mapping[i].address == data[j].addr - 4)
>> like x86_64?
>>
>>> continue;
>>> + }
>>> +found:
>>> jsonw_start_object(json_wtr);
>>> jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
>>> jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
>>> @@ -744,8 +757,21 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
>>>
>>> printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
>>> for (i = 0; i < dd.sym_count; i++) {
>>> - if (dd.sym_mapping[i].address != data[j].addr)
>>> + if (dd.sym_mapping[i].address != data[j].addr) {
>>> +#if defined(__x86_64__) || defined(__amd64__)
>>> + /*
>>> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
>>> + * function entry points have a 4-byte 'endbr' instruction prefix.
>>> + * This causes the actual function address = symbol address + 4.
>>> + * Here we check if this symbol matches the target address minus 4,
>>> + * indicating we've found a CET-enabled function entry point.
>>> + */
>>> + if (dd.sym_mapping[i].address == data[j].addr - 4)
>>> + goto found;
>>> +#endif
>>> continue;
>>> + }
>>> +found:
>>> printf("\n\t%016lx %-16llx %s",
>>> dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
>>> if (dd.sym_mapping[i].module[0] != '\0')
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re:Re: [PATCH v3] bpftool: Add CET-aware symbol matching for x86_64 architectures
2025-07-12 0:47 ` Yonghong Song
@ 2025-07-21 12:51 ` chenyuan
2025-07-21 14:53 ` Yonghong Song
0 siblings, 1 reply; 14+ messages in thread
From: chenyuan @ 2025-07-21 12:51 UTC (permalink / raw)
To: Yonghong Song; +Cc: ast, qmo, bpf, linux-kernel, Yuan Chen
Apologies for any inaccuracies in my previous explanation. Below, I'll provide a brief clarification based
on verification across both ARM64 and x86 platforms:
arm64:
Without kprobe/kprobe_multi Hook:
(gdb) disassemble vfs_read
Dump of assembler code for function vfs_read:
0xffffc000803ca308 <+0>: bti c // ARM64 BTI security instruction
0xffffc000803ca30c <+4>: nop
0xffffc000803ca310 <+8>: nop
0xffffc000803ca314 <+12>: paciasp
0xffffc000803ca318 <+16>: sub sp, sp, #0xa0
With kprobe/kprobe_multi Hook:
(gdb) disassemble vfs_read
Dump of assembler code for function vfs_read:
0xffffc000803ca308 <+0>: brk #0x4 // BTI replaced by breakpoint
0xffffc000803ca30c <+4>: mov x9, x30
0xffffc000803ca310 <+8>: nop
0xffffc000803ca314 <+12>: paciasp
0xffffc000803ca318 <+16>: sub sp, sp, #0xa0
kprobe directly overwrites the first instruction (bti c → brk #0x4). Hook address (0xffffc000803ca308) matches
the symbol address exactly.
x86_64:
Without kprobe/kprobe_multi Hook:
(gdb) disassemble vfs_read
Dump of assembler code for function vfs_read:
0xffffffff82112b40 <+0>: endbr64 // x86 CET security instruction
0xffffffff82112b44 <+4>: nopl 0x0(%rax,%rax,1)
0xffffffff82112b49 <+9>: push %r15
0xffffffff82112b4b <+11>: mov %rsi,%r15
0xffffffff82112b4e <+14>: push %r14
0xffffffff82112b50 <+16>: push %r13
With kprobe/kprobe_multi Hook:
(gdb) disassemble vfs_read
Dump of assembler code for function vfs_read:
0xffffffff82112b40 <+0>: endbr64 // Preserved security instruction
0xffffffff82112b44 <+4>: call 0xffffffffa1830000 // Hook replaces nopl
0xffffffff82112b49 <+9>: push %r15
0xffffffff82112b4b <+11>: mov %rsi,%r15
0xffffffff82112b4e <+14>: push %r14
0xffffffff82112b50 <+16>: push %r13
kprobe preserves endbr64 and overwrites the subsequent instruction (nopl → call). Hook address (0xffffffff82112b44)
requires -4 offset (0xffffffff82112b40) to match the symbol address.
ARM64 hooks replace the very first instruction (including security features like BTI), while x86_64 hooks target the instruction
immediately after endbr64, creating a 4-byte offset that must be compensated for when resolving symbol addresses.
At 2025-07-12 08:47:32, "Yonghong Song" <yonghong.song@linux.dev> wrote:
>
>
>On 7/11/25 12:07 AM, chenyuan wrote:
>> Thank you for your feedback! Does ARM64 require similar address adjustment detection? In my ARM64
>> environment with BTI enabled, bpftool correctly retrieves and prints function symbols. Could my verification
>> method be flawed?
>> Here’s a detailed explanation:
>>
>> ARM64 BTI vs. x86 CET: Fundamental Differences
>>
>> x86 CET (Control-flow Enforcement Technology):
>> Requires endbr32/endbr64 at function entries. Overwriting these instructions breaks CET protection .
>> Kernel logic (e.g., bpf_trace.c) adjusts symbol addresses by -4 to skip the endbr prefix .
>
>This interpretation is not correct. The adjustment by -4 is not to skip the endbr prefix,
>but to get the actual symbol address. For example,
>
>ffffffff83809cb0 <bpf_fentry_test3>:
>ffffffff83809cb0: f3 0f 1e fa endbr64
>ffffffff83809cb4: 0f 1f 44 00 00 nopl (%rax,%rax)
>ffffffff83809cb9: 8d 04 37 leal (%rdi,%rsi), %eax
>ffffffff83809cbc: 01 d0 addl %edx, %eax
>ffffffff83809cbe: 2e e9 6c d3 c8 00 jmp 0xffffffff84497030 <__x86_return_thunk>
>ffffffff83809cc4: 66 66 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:(%rax,%rax)
>
>The fentry_ip argument in func get_entry_ip() is 0xffffffff83809cb4. Adding -4
>will get the value 0xffffffff83809cb0 which is the actual start of the function.
>
>> ARM64 BTI (Branch Target Identification):
>> Uses BTI instructions as "landing pads" for indirect jumps. Kprobes can safely overwrite BTI instructions without triggering faults because:
>> Executing BTI, SG, or PACBTI clears EPSR.B (the enforcement flag), allowing subsequent non-BTI instructions .
>> Non-landing-pad instructions (e.g., probes) only fault if executed before EPSR.B is cleared – which doesn’t occur when probes replace BTI .
>
>I am not super familiar with arm64 bti. But from an arm64 kernel, with my config file (based on bpf CI),
>I didn't find bti insns for tracable functions. So I double arm64 kernel will need address adjustment.
>Otherwise, get_entry_ip() should do adjustment there.
>
>It would be great if you can have an example to show arm64 also needs addr adjustment in bpftool
>as in this patch.
>
>>
>> https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/armv8-1-m-pointer-authentication-and-branch-target-identification-extension
>>
>>
>>
>>
>>
>>
>>
>>
>>
>> At 2025-07-01 10:31:41, "Yonghong Song" <yonghong.song@linux.dev> wrote:
>>>
>>> On 6/26/25 12:49 AM, Yuan Chen wrote:
>>>> From: Yuan Chen <chenyuan@kylinos.cn>
>>>>
>>>> Adjust symbol matching logic to account for Control-flow Enforcement
>>>> Technology (CET) on x86_64 systems. CET prefixes functions with a 4-byte
>>>> 'endbr' instruction, shifting the actual entry point to symbol + 4.
>>>>
>>>> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
>>>> ---
>>>> tools/bpf/bpftool/link.c | 30 ++++++++++++++++++++++++++++--
>>>> 1 file changed, 28 insertions(+), 2 deletions(-)
>>>>
>>>> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
>>>> index 03513ffffb79..dfd192b4c5ad 100644
>>>> --- a/tools/bpf/bpftool/link.c
>>>> +++ b/tools/bpf/bpftool/link.c
>>>> @@ -307,8 +307,21 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
>>>> goto error;
>>>>
>>>> for (i = 0; i < dd.sym_count; i++) {
>>>> - if (dd.sym_mapping[i].address != data[j].addr)
>>>> + if (dd.sym_mapping[i].address != data[j].addr) {
>>>> +#if defined(__x86_64__) || defined(__amd64__)
>>>> + /*
>>>> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
>>>> + * function entry points have a 4-byte 'endbr' instruction prefix.
>>>> + * This causes the actual function address = symbol address + 4.
>>>> + * Here we check if this symbol matches the target address minus 4,
>>>> + * indicating we've found a CET-enabled function entry point.
>>>> + */
>>>> + if (dd.sym_mapping[i].address == data[j].addr - 4)
>>>> + goto found;
>>>> +#endif
>>> In kernel/trace/bpf_trace.c, I see
>>>
>>> static inline unsigned long get_entry_ip(unsigned long fentry_ip)
>>> {
>>> #ifdef CONFIG_X86_KERNEL_IBT
>>> if (is_endbr((void *)(fentry_ip - ENDBR_INSN_SIZE)))
>>> fentry_ip -= ENDBR_INSN_SIZE;
>>> #endif
>>> return fentry_ip;
>>> }
>>>
>>> Could you explain why arm64 also need to do checking
>>> if (dd.sym_mapping[i].address == data[j].addr - 4)
>>> like x86_64?
>>>
>>>> continue;
>>>> + }
>>>> +found:
>>>> jsonw_start_object(json_wtr);
>>>> jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
>>>> jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
>>>> @@ -744,8 +757,21 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
>>>>
>>>> printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
>>>> for (i = 0; i < dd.sym_count; i++) {
>>>> - if (dd.sym_mapping[i].address != data[j].addr)
>>>> + if (dd.sym_mapping[i].address != data[j].addr) {
>>>> +#if defined(__x86_64__) || defined(__amd64__)
>>>> + /*
>>>> + * On x86_64 architectures with CET (Control-flow Enforcement Technology),
>>>> + * function entry points have a 4-byte 'endbr' instruction prefix.
>>>> + * This causes the actual function address = symbol address + 4.
>>>> + * Here we check if this symbol matches the target address minus 4,
>>>> + * indicating we've found a CET-enabled function entry point.
>>>> + */
>>>> + if (dd.sym_mapping[i].address == data[j].addr - 4)
>>>> + goto found;
>>>> +#endif
>>>> continue;
>>>> + }
>>>> +found:
>>>> printf("\n\t%016lx %-16llx %s",
>>>> dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
>>>> if (dd.sym_mapping[i].module[0] != '\0')
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] bpftool: Add CET-aware symbol matching for x86_64 architectures
2025-07-21 12:51 ` chenyuan
@ 2025-07-21 14:53 ` Yonghong Song
2025-07-22 1:46 ` [PATCH v4] bpftool: Add CET-aware symbol matching for x86/x86_64 architectures chenyuan_fl
2025-07-22 2:00 ` chenyuan_fl
0 siblings, 2 replies; 14+ messages in thread
From: Yonghong Song @ 2025-07-21 14:53 UTC (permalink / raw)
To: chenyuan; +Cc: ast, qmo, bpf, linux-kernel, Yuan Chen
On 7/21/25 5:51 AM, chenyuan wrote:
> Apologies for any inaccuracies in my previous explanation. Below, I'll provide a brief clarification based
> on verification across both ARM64 and x86 platforms:
> arm64:
> Without kprobe/kprobe_multi Hook:
> (gdb) disassemble vfs_read
> Dump of assembler code for function vfs_read:
> 0xffffc000803ca308 <+0>: bti c // ARM64 BTI security instruction
> 0xffffc000803ca30c <+4>: nop
> 0xffffc000803ca310 <+8>: nop
> 0xffffc000803ca314 <+12>: paciasp
> 0xffffc000803ca318 <+16>: sub sp, sp, #0xa0
>
> With kprobe/kprobe_multi Hook:
> (gdb) disassemble vfs_read
> Dump of assembler code for function vfs_read:
> 0xffffc000803ca308 <+0>: brk #0x4 // BTI replaced by breakpoint
> 0xffffc000803ca30c <+4>: mov x9, x30
> 0xffffc000803ca310 <+8>: nop
> 0xffffc000803ca314 <+12>: paciasp
> 0xffffc000803ca318 <+16>: sub sp, sp, #0xa0
Thanks for checking. If this is the case, then I don't think we
need to checking
if (dd.sym_mapping[i].address == data[j].addr - 4) for arm64. In you v3
patch, the comment also only mentions x86_64.
>
> kprobe directly overwrites the first instruction (bti c → brk #0x4). Hook address (0xffffc000803ca308) matches
> the symbol address exactly.
>
> x86_64:
> Without kprobe/kprobe_multi Hook:
> (gdb) disassemble vfs_read
> Dump of assembler code for function vfs_read:
> 0xffffffff82112b40 <+0>: endbr64 // x86 CET security instruction
> 0xffffffff82112b44 <+4>: nopl 0x0(%rax,%rax,1)
> 0xffffffff82112b49 <+9>: push %r15
> 0xffffffff82112b4b <+11>: mov %rsi,%r15
> 0xffffffff82112b4e <+14>: push %r14
> 0xffffffff82112b50 <+16>: push %r13
>
> With kprobe/kprobe_multi Hook:
> (gdb) disassemble vfs_read
> Dump of assembler code for function vfs_read:
> 0xffffffff82112b40 <+0>: endbr64 // Preserved security instruction
> 0xffffffff82112b44 <+4>: call 0xffffffffa1830000 // Hook replaces nopl
> 0xffffffff82112b49 <+9>: push %r15
> 0xffffffff82112b4b <+11>: mov %rsi,%r15
> 0xffffffff82112b4e <+14>: push %r14
> 0xffffffff82112b50 <+16>: push %r13
>
> kprobe preserves endbr64 and overwrites the subsequent instruction (nopl → call). Hook address (0xffffffff82112b44)
> requires -4 offset (0xffffffff82112b40) to match the symbol address.
>
> ARM64 hooks replace the very first instruction (including security features like BTI), while x86_64 hooks target the instruction
> immediately after endbr64, creating a 4-byte offset that must be compensated for when resolving symbol addresses.
[...]
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v4] bpftool: Add CET-aware symbol matching for x86/x86_64 architectures
2025-07-21 14:53 ` Yonghong Song
@ 2025-07-22 1:46 ` chenyuan_fl
2025-07-22 2:00 ` chenyuan_fl
1 sibling, 0 replies; 14+ messages in thread
From: chenyuan_fl @ 2025-07-22 1:46 UTC (permalink / raw)
To: qmo, ast, daniel, andrii, yonghong.song; +Cc: bpf, linux-kernel, Yuan Chen
From: Yuan Chen <chenyuan@kylinos.com>
Adjust symbol matching logic to account for Control-flow Enforcement
Technology (CET) on x86/x86_64 systems. CET prefixes functions with
a 4-byte 'endbr' instruction, shifting the actual hook entry point to
symbol + 4.
Changed in PATCH v4:
* Refactor repeated code into a function.
* Add detection for the x86 architecture.
Signed-off-by: Yuan Chen <chenyuan@kylinos.com>
---
tools/bpf/bpftool/link.c | 27 +++++++++++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
index a773e05d5ade..9e5d85421919 100644
--- a/tools/bpf/bpftool/link.c
+++ b/tools/bpf/bpftool/link.c
@@ -282,6 +282,28 @@ get_addr_cookie_array(__u64 *addrs, __u64 *cookies, __u32 count)
return data;
}
+static bool
+symbol_matches_target(__u64 sym_addr, __u64 target_addr)
+{
+ if (sym_addr == target_addr)
+ return true;
+
+#if defined(__i386__) || defined(__x86_64__)
+ /*
+ * On x86 architectures with CET (Control-flow Enforcement Technology),
+ * function entry points have a 4-byte 'endbr' instruction prefix.
+ * This causes kprobe hooks to target the address *after* 'endbr'
+ * (symbol address + 4), preserving the CET instruction.
+ * Here we check if the symbol address matches the hook target address minus 4,
+ * indicating a CET-enabled function entry point.
+ */
+ if (sym_addr == target_addr - 4)
+ return true;
+#endif
+
+ return false;
+}
+
static void
show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
{
@@ -307,8 +329,9 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
goto error;
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (!symbol_matches_target(dd.sym_mapping[i].address, data[j].addr))
continue;
+
jsonw_start_object(json_wtr);
jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
@@ -744,7 +767,7 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (!symbol_matches_target(dd.sym_mapping[i].address, data[j].addr))
continue;
printf("\n\t%016lx %-16llx %s",
dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
--
2.25.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v4] bpftool: Add CET-aware symbol matching for x86/x86_64 architectures
2025-07-21 14:53 ` Yonghong Song
2025-07-22 1:46 ` [PATCH v4] bpftool: Add CET-aware symbol matching for x86/x86_64 architectures chenyuan_fl
@ 2025-07-22 2:00 ` chenyuan_fl
2025-07-22 14:23 ` Quentin Monnet
1 sibling, 1 reply; 14+ messages in thread
From: chenyuan_fl @ 2025-07-22 2:00 UTC (permalink / raw)
To: qmo, ast, daniel, andrii, yonghong.song; +Cc: bpf, linux-kernel, Yuan Chen
From: Yuan Chen <chenyuan@kylinos.cn>
Adjust symbol matching logic to account for Control-flow Enforcement
Technology (CET) on x86/x86_64 systems. CET prefixes functions with
a 4-byte 'endbr' instruction, shifting the actual hook entry point to
symbol + 4.
Changed in PATCH v4:
* Refactor repeated code into a function.
* Add detection for the x86 architecture.
Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
tools/bpf/bpftool/link.c | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
index a773e05d5ade..717ca8c5ff83 100644
--- a/tools/bpf/bpftool/link.c
+++ b/tools/bpf/bpftool/link.c
@@ -282,6 +282,28 @@ get_addr_cookie_array(__u64 *addrs, __u64 *cookies, __u32 count)
return data;
}
+static bool
+symbol_matches_target(__u64 sym_addr, __u64 target_addr)
+{
+ if (sym_addr == target_addr)
+ return true;
+
+#if defined(__i386__) || defined(__x86_64__)
+ /*
+ * On x86 architectures with CET (Control-flow Enforcement Technology),
+ * function entry points have a 4-byte 'endbr' instruction prefix.
+ * This causes kprobe hooks to target the address *after* 'endbr'
+ * (symbol address + 4), preserving the CET instruction.
+ * Here we check if the symbol address matches the hook target address minus 4,
+ * indicating a CET-enabled function entry point.
+ */
+ if (sym_addr == target_addr - 4)
+ return true;
+#endif
+
+ return false;
+}
+
static void
show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
{
@@ -307,7 +329,7 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
goto error;
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (!symbol_matches_target(dd.sym_mapping[i].address, data[j].addr))
continue;
jsonw_start_object(json_wtr);
jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
@@ -744,7 +766,7 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (!symbol_matches_target(dd.sym_mapping[i].address, data[j].addr))
continue;
printf("\n\t%016lx %-16llx %s",
dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
--
2.25.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v4] bpftool: Add CET-aware symbol matching for x86/x86_64 architectures
2025-07-22 2:00 ` chenyuan_fl
@ 2025-07-22 14:23 ` Quentin Monnet
2025-07-23 1:52 ` chenyuan
0 siblings, 1 reply; 14+ messages in thread
From: Quentin Monnet @ 2025-07-22 14:23 UTC (permalink / raw)
To: chenyuan_fl, ast, daniel, andrii, yonghong.song
Cc: bpf, linux-kernel, Yuan Chen
2025-07-22 10:00 UTC+0800 ~ chenyuan_fl@163.com
> From: Yuan Chen <chenyuan@kylinos.cn>
>
> Adjust symbol matching logic to account for Control-flow Enforcement
> Technology (CET) on x86/x86_64 systems. CET prefixes functions with
> a 4-byte 'endbr' instruction, shifting the actual hook entry point to
> symbol + 4.
>
> Changed in PATCH v4:
> * Refactor repeated code into a function.
> * Add detection for the x86 architecture.
>
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
> tools/bpf/bpftool/link.c | 26 ++++++++++++++++++++++++--
> 1 file changed, 24 insertions(+), 2 deletions(-)
>
> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
> index a773e05d5ade..717ca8c5ff83 100644
> --- a/tools/bpf/bpftool/link.c
> +++ b/tools/bpf/bpftool/link.c
> @@ -282,6 +282,28 @@ get_addr_cookie_array(__u64 *addrs, __u64 *cookies, __u32 count)
> return data;
> }
>
> +static bool
> +symbol_matches_target(__u64 sym_addr, __u64 target_addr)
> +{
> + if (sym_addr == target_addr)
> + return true;
> +
> +#if defined(__i386__) || defined(__x86_64__)
Do you really need it for __i386__ as well? My understanding was that
CET would apply only to 64-bit?
Thanks,
Quentin
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re:Re: [PATCH v4] bpftool: Add CET-aware symbol matching for x86/x86_64 architectures
2025-07-22 14:23 ` Quentin Monnet
@ 2025-07-23 1:52 ` chenyuan
0 siblings, 0 replies; 14+ messages in thread
From: chenyuan @ 2025-07-23 1:52 UTC (permalink / raw)
To: Quentin Monnet
Cc: ast, daniel, andrii, yonghong.song, bpf, linux-kernel, Yuan Chen
You are absolutely right. My initial assumption was incorrect - while endbr32 can technically be
compiled for i386, I've verified in the kernel configuration that X86_KERNEL_IBT explicitly
depends on X86_64:
.config - Linux/i386 6.16.0-rc3 Kernel Configuration
> Search (X86_KERNEL_IBT) > Processor type and features > Search (X86_KERNEL_IBT)
Symbol: X86_KERNEL_IBT [=n]
Type : bool
Defined at arch/x86/Kconfig:1771
Prompt: Indirect Branch Tracking
Depends on: X86_64 [=n] && CC_HAS_IBT [=y] && HAVE_OBJTOOL [=n] && (!LD_IS_LLD [=n] || LLD_VERSION [=0]>=140000)
This confirms CET is indeed 64-bit exclusive in the current implementation. I'll revise the patch
immediately to remove i386 support.
Thanks for catching this!
Best regards,
Yuan Chen
At 2025-07-22 22:23:23, "Quentin Monnet" <qmo@kernel.org> wrote:
>2025-07-22 10:00 UTC+0800 ~ chenyuan_fl@163.com
>> From: Yuan Chen <chenyuan@kylinos.cn>
>>
>> Adjust symbol matching logic to account for Control-flow Enforcement
>> Technology (CET) on x86/x86_64 systems. CET prefixes functions with
>> a 4-byte 'endbr' instruction, shifting the actual hook entry point to
>> symbol + 4.
>>
>> Changed in PATCH v4:
>> * Refactor repeated code into a function.
>> * Add detection for the x86 architecture.
>>
>> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
>> ---
>> tools/bpf/bpftool/link.c | 26 ++++++++++++++++++++++++--
>> 1 file changed, 24 insertions(+), 2 deletions(-)
>>
>> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
>> index a773e05d5ade..717ca8c5ff83 100644
>> --- a/tools/bpf/bpftool/link.c
>> +++ b/tools/bpf/bpftool/link.c
>> @@ -282,6 +282,28 @@ get_addr_cookie_array(__u64 *addrs, __u64 *cookies, __u32 count)
>> return data;
>> }
>>
>> +static bool
>> +symbol_matches_target(__u64 sym_addr, __u64 target_addr)
>> +{
>> + if (sym_addr == target_addr)
>> + return true;
>> +
>> +#if defined(__i386__) || defined(__x86_64__)
>
>
>Do you really need it for __i386__ as well? My understanding was that
>CET would apply only to 64-bit?
>
>Thanks,
>Quentin
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2025-07-23 1:53 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-26 6:11 [PATCH] bpftool: Add CET-aware symbol matching for x86_64 architectures Yuan Chen
2025-06-26 7:11 ` [PATCH v2] " Yuan Chen
2025-06-26 7:49 ` [PATCH v3] " Yuan Chen
2025-06-27 11:08 ` Quentin Monnet
2025-07-11 6:35 ` chenyuan
2025-07-01 2:31 ` Yonghong Song
2025-07-11 7:07 ` chenyuan
2025-07-12 0:47 ` Yonghong Song
2025-07-21 12:51 ` chenyuan
2025-07-21 14:53 ` Yonghong Song
2025-07-22 1:46 ` [PATCH v4] bpftool: Add CET-aware symbol matching for x86/x86_64 architectures chenyuan_fl
2025-07-22 2:00 ` chenyuan_fl
2025-07-22 14:23 ` Quentin Monnet
2025-07-23 1:52 ` chenyuan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).