From: "Edgecombe, Rick P" <rick.p.edgecombe@intel.com>
To: "ripatel@wii.dev" <ripatel@wii.dev>,
"joao@overdrivepizza.com" <joao@overdrivepizza.com>
Cc: "hjl.tools@gmail.com" <hjl.tools@gmail.com>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"dave.hansen@linux.intel.com" <dave.hansen@linux.intel.com>,
"x86@kernel.org" <x86@kernel.org>,
"peterz@infradead.org" <peterz@infradead.org>,
"hpa@zytor.com" <hpa@zytor.com>,
"mingo@redhat.com" <mingo@redhat.com>,
"david.laight.linux@gmail.com" <david.laight.linux@gmail.com>,
"bp@alien8.de" <bp@alien8.de>,
"fweimer@redhat.com" <fweimer@redhat.com>,
"tglx@kernel.org" <tglx@kernel.org>,
"libc-alpha@sourceware.org" <libc-alpha@sourceware.org>,
"david@vortan.dev" <david@vortan.dev>,
"xin@zytor.com" <xin@zytor.com>
Subject: Re: [RFC] x86: usermode IBT and signal handling
Date: Fri, 9 Oct 2026 23:13:41 +0000 [thread overview]
Message-ID: <e46563c5d54f1190ac5a385a5a75dafa4b0e3151.camel@intel.com> (raw)
In-Reply-To: <asgS64GnvOH781hw@wii.dev>
On Thu, 2026-10-08 at 22:02 +0000, Richard Patel wrote:
> On Wed, Oct 07, 2026 at 03:28:02PM +0000, Edgecombe, Rick P wrote:
> > On Wed, 2026-10-07 at 13:32 +0000, Richard Patel wrote:
> > > 2. Where to preserve WAIT_FOR_ENDBR across signal? Options:
> > > - shstk signal frame
> > > - signal frame fpstate (carve out a bit in _fpx_sw_bytes)
> > > - uc_flags
> >
> > The shadow stack signal frame is extensible to fit things like this and gives a
> > nice security protection for the bit. But I'd consider pursuing the simplest
> > option first. This option requires making IBT require shadow stack though. It
> > probably is the common case.
>
> The problem is changing the shadow stack signal frame layout breaks a
> few userland apps that were already written to hardcode it:
> - libgcc's C++ exception unwinder runs into a parse failure if the frame
> is extended (_URC_FATAL_PHASE2_ERROR)
> https://github.com/gcc-mirror/gcc/blob/master/libgcc/config/i386/shadow-stack-unwind.h
> - CRIU (process migration) hand-builds such a shstk signal frame
> https://github.com/checkpoint-restore/criu/blob/criu-dev/compel/arch/x86/src/lib/infect.c
>
> It might help with logistics to allow apps to evolve shstk and IBT
> independently also.
>
> > The original patches found a bit in the normal signal frame, but I don't think
> > it ever got fully settled? In any case it would need to be revisited at this
> > point.
> >
> > But the best option is probably the one with the least resistance. It might be
> > shadow stack, since that is off to the side. We can always harden things with
> > further changes (add to shadow stack), or extend to IBT-only later.
>
> The original patches used uc_flags but that didn't work with 32-bit
> sigreturn, but that's easy enough to just ban.
>
> I just didn't like the cosmetics of uc_flags anyways, there's a more
> elegant way to use a reserved fpstate bit.
We'd have to think if it is worth taking from a limited resource of bits for
this if we have another place to put it. Especially since the other place is
more secure. Apps could clear the tracker bit during a signal.
But it depends on the ABI backwards compatibility problems you mentioned.
>
> > > 3. OK to break legacy 32-bit sigreturn if IBT enabled?
> >
> > IBT needs userspace enabling to work, and legacy 32 bit userspace is basically,
> > well, legacy. So unless there is any serious user that pops up, we should just
> > not supporting user IBT for 32 bit. This simplifies things. BTW we don't support
> > 32 bit shadow stack.
>
> Wouldn't you need extra code to prevent IBT from working in 32-bit mode?
> Since the user can just far call into 32-bit without asking the kernel
> AFAIK.
Hmm, yea. We could either ignore it (let the signal handlers, etc just not be
functional). Or block 32 bit syscalls when IBT is on, like discussed on the
shadow stack thread.
>
> With IBT state in fpstate, IBT would work fine for any variant of
> sigreturn (32-bit and 64-bit) all in the common signal frame restore
> code, no need to modify any syscall handlers.
>
> So, I'd argue it's simpler to leave it enabled in 32-bit compatibility
> mode even if no one ever used 32-bit IBT. We should certainly prevent
> IBT from being compiled in for true 32-bit kernels though.
>
> > What happened to the discussion of a PROT_IBT (like PROT_BTI)? I had POCed two
> > ways to do it and one was not that bad. The legacy bitmap thing is not easy to
> > use here, and I don't recommend it. But handling IBT #CPs by looking up the VMA
> > of RIP was pretty compact. If the VMA has !PROT_IBT, then clear the TRACKER bit
>
> Sorry, I just forgot about them! I like the idea.
>
> Do you think we should do PROT_IBT first or prctl or both?
> I'm happy to help upstream the POC, please let me know.
It would need a bit of cleanup to share. It is mixed in with the bitmap version,
which was quite a mess. But the code to do the #CP violation based PROT_IBT is
very small. Here is the jist of it to give some idea. It was based on Yu-cheng's
original user IBT implementation that you linked:
int handle_user_ibt_violation_cp(struct vm_area_struct *vma, unsigned long addr)
{
if (vma->vm_flags & VM_IBT)
return 0;
ibt_get_clear_wait_endbr();
return 1;
}
int handle_user_ibt_violation(struct vm_area_struct *vma, unsigned long addr)
{
if (features_enabled(ARCH_IBT_PROT_IBT_BITMAP))
return handle_user_ibt_violation_bitmap(vma, addr);
else if(features_enabled(ARCH_IBT_PROT_IBT_CP))
return handle_user_ibt_violation_cp(vma, addr);
return 0;
}
diff --git a/arch/x86/kernel/cet.c b/arch/x86/kernel/cet.c
index d2c732a34e5d9..7152f31095912 100644
--- a/arch/x86/kernel/cet.c
+++ b/arch/x86/kernel/cet.c
@@ -44,6 +44,26 @@ static void do_unexpected_cp(struct pt_regs *regs, unsigned
long error_code)
static DEFINE_RATELIMIT_STATE(cpf_rate, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
+static int do_user_ibt_violation(struct pt_regs *regs, unsigned long
error_code)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ int ret = 0;
+
+ vma = lock_mm_and_find_vma(mm, regs->ip, regs);
+ /* TODO: Hmm, what to do if the VMA is gone? */
+ if (!vma) {
+ printk("vma missing\n");
+ return 0;
+ }
+
+ if (handle_user_ibt_violation(vma, regs->ip))
+ ret = 1;
+
+ mmap_read_unlock(mm);
+ return ret;
+}
+
static void do_user_cp_fault(struct pt_regs *regs, unsigned long error_code)
{
struct task_struct *tsk;
@@ -63,6 +83,9 @@ static void do_user_cp_fault(struct pt_regs *regs, unsigned
long error_code)
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = X86_TRAP_CP;
+ if (((error_code & CP_EC) == CP_ENDBR) && do_user_ibt_violation(regs,
error_code))
+ goto out;
+
/* Ratelimit to prevent log spamming. */
if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
__ratelimit(&cpf_rate)) {
@@ -76,6 +99,8 @@ static void do_user_cp_fault(struct pt_regs *regs, unsigned
long error_code)
}
force_sig_fault(SIGSEGV, SEGV_CPERR, (void __user *)0);
+
+out:
cond_local_irq_disable(regs);
}
>
> > and proceed. This punishes mixed mode apps indirect calls (not *that* horrible
> > IIRC, I've lost my test results), but doesn't hurt fully enabled apps.
>
> Could you share your POC?
> I'm curious about the overhead on top of a regular GOT-PLT inter-DSO
> call. There are probably apps that spam those, so if ld.so started
> enabling PROT_IBT automatically, those apps would probably be confused
> by any performance regressions.
>
> > Now, the security question of whether a mixed mode makes sense, is valid I
> > think. But the issue we struggled with a lot for shadow stack was compatibility
> > of apps made of multiple packages (which is a lot of them), or which have JITs.
> > So I think a mixed mode would be valuable if it can be a stepping stone to a
> > fully locked down mode eventually. For example, start with a more permissive
> > prctl that turns on mixed (PROT_IBT) mode. Distros and other wider enablers can
> > use this without fear of crashing apps with JITs, etc. Emit a pr_info() or
> > something that incentives people to fix their libs. Then later distros can
> > switch to a fully enforced on thing.
>
> I think pr_info() instead of crashing is a really cool idea! I will port
> this to the prctl() app-wide API. We'd disable IBT and log to dmesg on
> the first violation in log mode. (hopefully loudly enough so that the
> distros' error-reporting telemetry uploads them)
>
> This allows glibc/distros to roll out IBT immediately.
>
> > But would be good to hear updated opinions from the distros on this. (maybe I
> > missed it?)
>
> Wasn't sure who to CC, Florian what do you think?
Are you working on getting IBT ready for a distro? Or just other interest?
>
> > > - IBT enablement sets both ENDBR_EN and NO_TRACK_EN
> > > (enforce endbr64 on indirect jumps, allow 'notrack' prefix to opt-out)
> > > - vDSO polishing needed (add missing endbr64 markers, GNU property note)
> > > - signal handler entrypoint does not need 'endbr64' (the signal handler
> > > entrypoint cannot easily be changed)
> >
> > I think the signal delivery should manually check for endbr in SW. Using
> > something like the speculate loop in shstk_pop_sigframe(). What is the problem?
> >
> > At the same time, something small that actually upstream is better than nothing.
>
> It's trivial to enforce it. But generally the less endbr markers the
> better, because it reduces valid sites for `call rax`. Hijacking a
> sigaction call is much harder than smashing a function pointer.
I wouldn't try to stop an IBT implementation that was missing this to start. But
I think setting a signal handler to random code and triggering a signal would be
pretty trivial forward edge by pass?
>
> Userland should eventually do a variant of FineIBT where the callee
> checks a cookie that the caller sets, e.g. `mov r13, MAGIC; call rax`.
> If the kernel required endbr for signal delivery, that blows a gap into
> the cookie mechanism. The signal handler would always be a valid target
> because such a cookie can hardly ever be part of uapi (kernel signal
> delivery wouldn't know what cookie the userland handler expects).
Ah! This also brings up the wonderful problem of mixed mode FineIBT, +Joao who
has had a lot of thoughts on this subject. And the signal delivery part too
IIRC.
>
> Thanks,
> -- Richard
prev parent reply other threads:[~2026-10-09 23:13 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-07 13:32 Richard Patel
2026-10-07 15:28 ` Edgecombe, Rick P
2026-10-08 22:02 ` Richard Patel
2026-10-09 23:13 ` Edgecombe, Rick P [this message]
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=e46563c5d54f1190ac5a385a5a75dafa4b0e3151.camel@intel.com \
--to=rick.p.edgecombe@intel.com \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=david.laight.linux@gmail.com \
--cc=david@vortan.dev \
--cc=fweimer@redhat.com \
--cc=hjl.tools@gmail.com \
--cc=hpa@zytor.com \
--cc=joao@overdrivepizza.com \
--cc=libc-alpha@sourceware.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=ripatel@wii.dev \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
--cc=xin@zytor.com \
/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®