[PATCH v2 3/5] arm64: signal: Improve POR_EL0 handling to avoid uaccess failures
Kevin Brodsky
kevin.brodsky at arm.com
Thu Oct 24 07:55:48 PDT 2024
On 24/10/2024 12:59, Catalin Marinas wrote:
> On Wed, Oct 23, 2024 at 04:05:09PM +0100, Kevin Brodsky wrote:
>> +/*
>> + * Save the unpriv access state into ua_state and reset it to disable any
>> + * restrictions.
>> + */
>> +static void save_reset_user_access_state(struct user_access_state *ua_state)
>> +{
>> + if (system_supports_poe()) {
>> + /*
>> + * Enable all permissions in all 8 keys
>> + * (inspired by REPEAT_BYTE())
>> + */
>> + u64 por_enable_all = (~0u / POE_MASK) * POE_RXW;
> I think this should be ~0ul.
It is ~0u on purpose, because unlike in REPEAT_BYTE(), I only wanted the
lower 32 bits to be filled with POE_RXW (we only have 8 keys, the top 32
bits are RES0). That said, given that D128 has 4-bit pkeys, we could
anticipate and fill the top 32 bits too (should make no difference on D64).
>> @@ -907,6 +964,7 @@ SYSCALL_DEFINE0(rt_sigreturn)
>> {
>> struct pt_regs *regs = current_pt_regs();
>> struct rt_sigframe __user *frame;
>> + struct user_access_state ua_state;
>>
>> /* Always make any pending restarted system calls return -EINTR */
>> current->restart_block.fn = do_no_restart_syscall;
>> @@ -923,12 +981,14 @@ SYSCALL_DEFINE0(rt_sigreturn)
>> if (!access_ok(frame, sizeof (*frame)))
>> goto badframe;
>>
>> - if (restore_sigframe(regs, frame))
>> + if (restore_sigframe(regs, frame, &ua_state))
>> goto badframe;
>>
>> if (restore_altstack(&frame->uc.uc_stack))
>> goto badframe;
>>
>> + restore_user_access_state(&ua_state);
>> +
>> return regs->regs[0];
>>
>> badframe:
> The saving part I'm fine with. For restoring, I was wondering whether we
> can get a more privileged POR_EL0 if reading the frame somehow failed.
> This is largely theoretical, there are other ways to attack like
> writing POR_EL0 directly than unmapping/remapping the signal stack.
>
> What I'd change here is always restore_user_access_state() to
> POR_EL0_INIT. Maybe just initialise ua_state above and add the function
> call after the badframe label.
I'm not sure I understand. When we enter this function, POR_EL0 is set
to whatever the signal handler set it to (POR_EL0_INIT by default).
There are then two cases:
1) Everything succeeds, including reading the saved POR_EL0 from the
frame. We then call restore_user_access_state(), setting POR_EL0 to the
value we've read, and return to userspace.
2) Any uaccess fails (for instance reading POR_EL0). In that case we
leave POR_EL0 unchanged and deliver SIGSEGV.
In case 2 POR_EL0 is most likely already set to POR_EL0_INIT, or
whatever the signal handler set it to. It's not clear to me that forcing
it to POR_EL0_INIT helps much. Either way it's doubtful that the SIGSEGV
handler will be able to recover, since the new signal frame we will
create for it may be a mix of interrupted state and signal handler state
(depending on exactly where we fail).
Kevin
More information about the linux-arm-kernel
mailing list