From: Peter Zijlstra <peterz@infradead.org>
To: x86@kernel.org
Cc: linux-kernel@vger.kernel.org, peterz@infradead.org,
jpoimboe@kernel.org, leitao@debian.org, keescook@chromium.org,
hpa@zytor.com, kernel test robot <lkp@intel.com>
Subject: [PATCH 5/6] objtool: Allow EXPORT_SYMBOL() of __noendbr functions
Date: Mon, 02 Nov 2026 12:25:54 +0100 [thread overview]
Message-ID: <20261102114000.887706274@infradead.org> (raw)
In-Reply-To: <20261102112549.714541309@infradead.org>
Objtool IBT validation does two things:
- it checks that all text references have an ENDBR instruction (WARN);
- it finds all ENDBR instructions that have no references (SEAL).
The first ensures that all function pointers are of functions that have ENDBR,
since doing indirect calls requires this. The flip side is that you can have
text references that are not function pointers in the sense of later calling
them. Eg. ip_within_syscall_gap() will take the address of symbols, but will
never call them. Any such location that is referenced but does not have ENDBR
requires an ANNOTATE_NOENDBR.
The second is used to create a list of ENDBR instructions to SEAL. That is, at
boot time the kernel will replace all these ENDBR instructions with a NOP
instruction, limiting indirect targets.
Now, EXPORT_SYMBOL*(sym) is counted as a reference. This is important because
even if the kernel itself does not reference the symbol, a module still might.
The flip side is that exporting a __noendbr function needs an ANNOTATE_NOENDBR.
Relax this by supressing the objtool-WARN for .export_symbol references. This
allows exporting of __noendbr without further ANNOTATE_NOENDBR. Remove any such
now redudant annotation.
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
arch/x86/entry/calling.h | 1 -
arch/x86/entry/entry_64.S | 2 +-
arch/x86/entry/entry_64_compat.S | 6 +++---
arch/x86/lib/clear_page_64.S | 1 -
arch/x86/lib/copy_user_64.S | 1 -
arch/x86/lib/copy_user_uncached_64.S | 1 -
arch/x86/lib/getuser.S | 4 ----
arch/x86/lib/hweight.S | 3 ---
arch/x86/lib/putuser.S | 4 ----
tools/objtool/check.c | 15 ++++++++++++---
10 files changed, 16 insertions(+), 22 deletions(-)
--- a/arch/x86/entry/calling.h
+++ b/arch/x86/entry/calling.h
@@ -432,7 +432,6 @@ For 32-bit we have the following convent
/* rdi: arg1 ... normal C conventions. rax is saved/restored. */
.macro THUNK name, func
SYM_FUNC_START(\name)
- ANNOTATE_NOENDBR
pushq %rbp
movq %rsp, %rbp
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -95,7 +95,7 @@ SYM_CODE_START(entry_SYSCALL_64)
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
SYM_INNER_LABEL(entry_SYSCALL_64_safe_stack, SYM_L_GLOBAL)
- ANNOTATE_NOENDBR
+ ANNOTATE_NOENDBR // ip_within_syscall_gap
/* Construct struct pt_regs on stack */
pushq $__USER_DS /* pt_regs->ss */
--- a/arch/x86/entry/entry_64_compat.S
+++ b/arch/x86/entry/entry_64_compat.S
@@ -196,7 +196,7 @@ SYM_CODE_START(entry_SYSCALL_compat)
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
SYM_INNER_LABEL(entry_SYSCALL_compat_safe_stack, SYM_L_GLOBAL)
- ANNOTATE_NOENDBR
+ ANNOTATE_NOENDBR // ip_within_syscall_gap
/* Construct struct pt_regs on stack */
pushq $__USER_DS /* pt_regs->ss */
@@ -260,7 +260,7 @@ SYM_INNER_LABEL(entry_SYSCALL_compat_aft
*/
movq RSP-ORIG_RAX(%rsp), %rsp
SYM_INNER_LABEL(entry_SYSRETL_compat_unsafe_stack, SYM_L_GLOBAL)
- ANNOTATE_NOENDBR
+ ANNOTATE_NOENDBR // ip_within_syscall_gap
/*
* The original userspace %rsp (RSP-ORIG_RAX(%rsp)) is stored
@@ -280,7 +280,7 @@ SYM_INNER_LABEL(entry_SYSRETL_compat_uns
CLEAR_CPU_BUFFERS
sysretl
SYM_INNER_LABEL(entry_SYSRETL_compat_end, SYM_L_GLOBAL)
- ANNOTATE_NOENDBR
+ ANNOTATE_NOENDBR // ip_within_syscall_gap
int3
SYM_CODE_END(entry_SYSCALL_compat)
--- a/arch/x86/lib/clear_page_64.S
+++ b/arch/x86/lib/clear_page_64.S
@@ -42,7 +42,6 @@ EXPORT_SYMBOL_GPL(__clear_pages_unrolled
* rcx: uncleared bytes or 0 if successful.
*/
SYM_FUNC_START(rep_stos_alternative)
- ANNOTATE_NOENDBR
cmpq $64,%rcx
jae .Lunrolled
--- a/arch/x86/lib/copy_user_64.S
+++ b/arch/x86/lib/copy_user_64.S
@@ -32,7 +32,6 @@
* it simpler for us, we can clobber rsi/rdi and rax freely.
*/
SYM_FUNC_START(rep_movs_alternative)
- ANNOTATE_NOENDBR
cmpq $64,%rcx
jae .Llarge
--- a/arch/x86/lib/copy_user_uncached_64.S
+++ b/arch/x86/lib/copy_user_uncached_64.S
@@ -28,7 +28,6 @@
* rax uncopied bytes or 0 if successful.
*/
SYM_FUNC_START(copy_to_nontemporal)
- ANNOTATE_NOENDBR
/* If destination is not 7-byte aligned, we'll have to align it */
testb $7,%dil
jne .Lalign
--- a/arch/x86/lib/getuser.S
+++ b/arch/x86/lib/getuser.S
@@ -58,7 +58,6 @@
.text
SYM_FUNC_START(__get_user_1)
- ANNOTATE_NOENDBR
check_range size=1
ASM_STAC
UACCESS movzbl (%_ASM_AX),%edx
@@ -69,7 +68,6 @@ SYM_FUNC_END(__get_user_1)
EXPORT_SYMBOL(__get_user_1)
SYM_FUNC_START(__get_user_2)
- ANNOTATE_NOENDBR
check_range size=2
ASM_STAC
UACCESS movzwl (%_ASM_AX),%edx
@@ -80,7 +78,6 @@ SYM_FUNC_END(__get_user_2)
EXPORT_SYMBOL(__get_user_2)
SYM_FUNC_START(__get_user_4)
- ANNOTATE_NOENDBR
check_range size=4
ASM_STAC
UACCESS movl (%_ASM_AX),%edx
@@ -91,7 +88,6 @@ SYM_FUNC_END(__get_user_4)
EXPORT_SYMBOL(__get_user_4)
SYM_FUNC_START(__get_user_8)
- ANNOTATE_NOENDBR
#ifndef CONFIG_X86_64
xor %ecx,%ecx
#endif
--- a/arch/x86/lib/hweight.S
+++ b/arch/x86/lib/hweight.S
@@ -10,8 +10,6 @@
* %rdi: w
*/
SYM_FUNC_START(__sw_hweight32)
- ANNOTATE_NOENDBR
-
#ifdef CONFIG_X86_64
movl %edi, %eax # w
#endif
@@ -44,7 +42,6 @@ EXPORT_SYMBOL(__sw_hweight32)
*/
#ifdef CONFIG_X86_64
SYM_FUNC_START(__sw_hweight64)
- ANNOTATE_NOENDBR
pushq %rdi
pushq %rdx
--- a/arch/x86/lib/putuser.S
+++ b/arch/x86/lib/putuser.S
@@ -46,7 +46,6 @@
.text
SYM_FUNC_START(__put_user_1)
- ANNOTATE_NOENDBR
check_range size=1
ASM_STAC
1: movb %al,(%_ASM_CX)
@@ -57,7 +56,6 @@ SYM_FUNC_END(__put_user_1)
EXPORT_SYMBOL(__put_user_1)
SYM_FUNC_START(__put_user_2)
- ANNOTATE_NOENDBR
check_range size=2
ASM_STAC
2: movw %ax,(%_ASM_CX)
@@ -68,7 +66,6 @@ SYM_FUNC_END(__put_user_2)
EXPORT_SYMBOL(__put_user_2)
SYM_FUNC_START(__put_user_4)
- ANNOTATE_NOENDBR
check_range size=4
ASM_STAC
3: movl %eax,(%_ASM_CX)
@@ -79,7 +76,6 @@ SYM_FUNC_END(__put_user_4)
EXPORT_SYMBOL(__put_user_4)
SYM_FUNC_START(__put_user_8)
- ANNOTATE_NOENDBR
check_range size=8
ASM_STAC
4: mov %_ASM_AX,(%_ASM_CX)
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4597,7 +4597,7 @@ static int validate_ibt_insn(struct objt
}
static int validate_ibt_data_reloc(struct objtool_file *file,
- struct reloc *reloc)
+ struct reloc *reloc, bool warn)
{
struct instruction *dest;
@@ -4611,7 +4611,7 @@ static int validate_ibt_data_reloc(struc
return 0;
}
- if (dest->noendbr)
+ if (dest->noendbr || !warn)
return 0;
WARN_FUNC(reloc->sec->base, reloc_offset(reloc),
@@ -4636,6 +4636,7 @@ static int validate_ibt(struct objtool_f
warnings += validate_ibt_insn(file, insn);
for_each_sec(file->elf, sec) {
+ bool warn = true;
/* Already done by validate_ibt_insn() */
if (is_text_sec(sec))
@@ -4672,6 +4673,14 @@ static int validate_ibt(struct objtool_f
!strcmp(sec->name, "__patchable_function_entries"))
continue;
+ /*
+ * EXPORT_SYMBOL() must be counted towards an ENDBR usage,
+ * such symbols must not be implicitly sealed. But it is fine
+ * to EXPORT an explicitly (__noendbr) sealed function.
+ */
+ if (!strcmp(sec->name, ".export_symbol"))
+ warn = false;
+
for_each_reloc(sec->rsec, reloc) {
struct symbol *sym = find_symbol_containing(reloc->sec->base,
reloc_offset(reloc));
@@ -4679,7 +4688,7 @@ static int validate_ibt(struct objtool_f
if (sym && !strcmp(sym->name, "sys_call_table"))
continue;
- warnings += validate_ibt_data_reloc(file, reloc);
+ warnings += validate_ibt_data_reloc(file, reloc, warn);
}
}
next prev parent reply other threads:[~2026-11-02 11:40 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 11:25 [PATCH 0/6] x86: Harden syscall dispatch Peter Zijlstra
2026-11-02 11:25 ` [PATCH 1/6] x86_64/entry: Unconditionally use -fno-jump-table Peter Zijlstra
2026-11-02 14:28 ` Breno Leitao
2026-11-02 11:25 ` [PATCH 2/6] objtool: Add instrumentation_weak() Peter Zijlstra
2026-11-02 11:25 ` [PATCH 3/6] x86_64/entry: Make syscall dispatch noinstr Peter Zijlstra
2026-11-02 11:25 ` [PATCH 4/6] x86_64/entry: Mark all syscalls __noendbr Peter Zijlstra
2026-11-02 11:25 ` Peter Zijlstra [this message]
2026-11-02 11:25 ` [PATCH 6/6] x86_64/entry: Mark C noinstr __noendbr Peter Zijlstra
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261102114000.887706274@infradead.org \
--to=peterz@infradead.org \
--cc=hpa@zytor.com \
--cc=jpoimboe@kernel.org \
--cc=keescook@chromium.org \
--cc=leitao@debian.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lkp@intel.com \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®