On Tue, Oct 15, 2024 at 12:41:16PM +0100, Will Deacon wrote:
On Tue, Oct 15, 2024 at 10:59:11AM +0100, Joey Gouly wrote:
On Mon, Oct 14, 2024 at 06:10:23PM +0100, Will Deacon wrote:
Kevin, Joey,
On Wed, Oct 09, 2024 at 03:43:01PM +0100, Will Deacon wrote:
On Tue, Sep 24, 2024 at 01:27:58PM +0200, Kevin Brodsky wrote:
On 22/08/2024 17:11, Joey Gouly wrote:
@@ -1178,6 +1237,9 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, sme_smstop(); }
- if (system_supports_poe())
write_sysreg_s(POR_EL0_INIT, SYS_POR_EL0);
At the point where setup_return() is called, the signal frame has already been written to the user stack. In other words, we write to the user stack first, and then reset POR_EL0. This may be problematic, especially if we are using the alternate signal stack, which the interrupted POR_EL0 may not grant access to. In that situation uaccess will fail and we'll end up with a SIGSEGV.
This issue has already been discussed on the x86 side, and as it happens patches to reset PKRU early [1] have just landed. I don't think this is a blocker for getting this series landed, but we should try and align with x86. If there's no objection, I'm planning to work on a counterpart to the x86 series (resetting POR_EL0 early during signal delivery).
Did you get a chance to work on that? It would be great to land the fixes for 6.12, if possible, so that the first kernel release with POE support doesn't land with known issues.
Looking a little more at this, I think we have quite a weird behaviour on arm64 as it stands. It looks like we rely on the signal frame to hold the original POR_EL0 so, if for some reason we fail to allocate space for the POR context, I think we'll return back from the signal with POR_EL0_INIT. That seems bad?
If we don't allocate space for POR_EL0, I think the program recieves SIGSGEV?
setup_sigframe_layout() if (system_supports_poe()) { err = sigframe_alloc(user, &user->poe_offset, sizeof(struct poe_context)); if (err) return err; }
Through get_sigframe() and setup_rt_frame(), that eventually hets here:
handle_signal() ret = setup_rt_frame(usig, ksig, oldset, regs);
[..]
signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP));
void signal_setup_done(int failed, struct ksignal *ksig, int stepping) { if (failed) force_sigsegv(ksig->sig); else signal_delivered(ksig, stepping); }
So I think it's "fine"?
Ah, yes, sorry about that. I got confused by the conditional push in setup_sigframe():
if (system_supports_poe() && err == 0 && user->poe_offset) { ...
which gives the wrong impression that the POR is somehow optional, even if the CPU supports POE. So we should drop that check of 'user->poe_offset' as it cannot be NULL here.
From memory and a quick glance at the code:
For other "conditionally unconditional" things, we don't have a corresponding check on user->foo.
For conditional stuff, non-NULLness of user->foo is used to track whether we decided to dump the corresponding record; for consistency here, if we have system_supports_poe() && err == 0, then that's sufficient (though in prior versions of this code, POR_EL0 dumping was conditional and so the extra check did do something...)
In any case, if some allocation fails then we splat out with a SIGSEGV before modifying the user task state to deliver the signal (in setup_return() etc.)
If The user's POR_EL0 value is being clobbered before we get here, we would save the wrong value -- so the code would be broken anyway.
So, as Joey says, this is probably fine, but the user->poe_offset check looks superfluous. The kernel will splat on us here and kill the thread if it's NULL anyway.
[...]
Cheers ---Dave