tools/bpf/bpftool/link.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-)
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
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
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
© 2016 - 2025 Red Hat, Inc.