diff mbox series

[v2,1/2] arm64/signal: Restore TPIDR2 register rather than memory state

Message ID 20230621-arm64-fix-tpidr2-signal-restore-v2-1-c8e8fcc10302@kernel.org (mailing list archive)
State Accepted
Commit 616cb2f4b141852cac3dfffe8354c8bf19e9999d
Headers show
Series arm64/signal: Fix handling of TPIDR2 | expand

Commit Message

Mark Brown June 22, 2023, 1:39 p.m. UTC
Currently when restoring the TPIDR2 signal context we set the new value
from the signal frame in the thread data structure but not the register,
following the pattern for the rest of the data we are restoring. This does
not work in the case of TPIDR2, the register always has the value for the
current task. This means that either we return to userspace and ignore the
new value or we context switch and save the register value on top of the
newly restored value.

Load the value from the signal context into the register instead.

Fixes: 39e54499280f ("arm64/signal: Include TPIDR2 in the signal context")
Signed-off-by: Mark Brown <broonie@kernel.org>
Cc: stable@vger.kernel.org
---
 arch/arm64/kernel/signal.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Catalin Marinas June 22, 2023, 4:42 p.m. UTC | #1
On Thu, Jun 22, 2023 at 02:39:45PM +0100, Mark Brown wrote:
> Currently when restoring the TPIDR2 signal context we set the new value
> from the signal frame in the thread data structure but not the register,
> following the pattern for the rest of the data we are restoring. This does
> not work in the case of TPIDR2, the register always has the value for the
> current task. This means that either we return to userspace and ignore the
> new value or we context switch and save the register value on top of the
> newly restored value.
> 
> Load the value from the signal context into the register instead.
> 
> Fixes: 39e54499280f ("arm64/signal: Include TPIDR2 in the signal context")
> Signed-off-by: Mark Brown <broonie@kernel.org>
> Cc: stable@vger.kernel.org
> ---
>  arch/arm64/kernel/signal.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
> index 2cfc810d0a5b..10b407672c42 100644
> --- a/arch/arm64/kernel/signal.c
> +++ b/arch/arm64/kernel/signal.c
> @@ -398,7 +398,7 @@ static int restore_tpidr2_context(struct user_ctxs *user)
>  
>  	__get_user_error(tpidr2_el0, &user->tpidr2->tpidr2, err);
>  	if (!err)
> -		current->thread.tpidr2_el0 = tpidr2_el0;
> +		write_sysreg_s(tpidr2_el0, SYS_TPIDR2_EL0);

I guess the other way around may also be true - the libc sets tpidr2_el0
to something else and doesn't want the kernel to restore its original
value from sigcontext.

For tpidr_el0 we don't bother with sigcontext, not sure what the use for
tpidr2_el0 in signals is. If we assume the context saved is only
informative (like esr), we can simply ignore restoring it from the
signal stack.

I guess we need to ask Szabolcs what his preference is. The current code
is wrong either way since current->thread.tpidr2_el0 would be overridden
at thread switch.
Mark Brown June 22, 2023, 5:11 p.m. UTC | #2
On Thu, Jun 22, 2023 at 05:42:54PM +0100, Catalin Marinas wrote:
> On Thu, Jun 22, 2023 at 02:39:45PM +0100, Mark Brown wrote:

> > -		current->thread.tpidr2_el0 = tpidr2_el0;
> > +		write_sysreg_s(tpidr2_el0, SYS_TPIDR2_EL0);

> I guess the other way around may also be true - the libc sets tpidr2_el0
> to something else and doesn't want the kernel to restore its original
> value from sigcontext.

> For tpidr_el0 we don't bother with sigcontext, not sure what the use for
> tpidr2_el0 in signals is. If we assume the context saved is only
> informative (like esr), we can simply ignore restoring it from the
> signal stack.

TPIDR2 is intended to go along with the thread stack, it's intended to
be used to allow lazy save of the (rather large) ZA register state when
a called function needs it rather than forcing it to be caller saved.
TPIDR2 is used to point to memory allocated for managing this process,
something that provides a new value should be making a deliberate
decision to do so and editing the stack frame.

> I guess we need to ask Szabolcs what his preference is. The current code
> is wrong either way since current->thread.tpidr2_el0 would be overridden
> at thread switch.

Right.
Catalin Marinas June 23, 2023, 5:28 p.m. UTC | #3
On Thu, Jun 22, 2023 at 06:11:20PM +0100, Mark Brown wrote:
> On Thu, Jun 22, 2023 at 05:42:54PM +0100, Catalin Marinas wrote:
> > On Thu, Jun 22, 2023 at 02:39:45PM +0100, Mark Brown wrote:
> 
> > > -		current->thread.tpidr2_el0 = tpidr2_el0;
> > > +		write_sysreg_s(tpidr2_el0, SYS_TPIDR2_EL0);
> 
> > I guess the other way around may also be true - the libc sets tpidr2_el0
> > to something else and doesn't want the kernel to restore its original
> > value from sigcontext.
> 
> > For tpidr_el0 we don't bother with sigcontext, not sure what the use for
> > tpidr2_el0 in signals is. If we assume the context saved is only
> > informative (like esr), we can simply ignore restoring it from the
> > signal stack.
> 
> TPIDR2 is intended to go along with the thread stack, it's intended to
> be used to allow lazy save of the (rather large) ZA register state when
> a called function needs it rather than forcing it to be caller saved.
> TPIDR2 is used to point to memory allocated for managing this process,
> something that provides a new value should be making a deliberate
> decision to do so and editing the stack frame.

OK, so if the signal handler invokes a function that touches the ZA
state, it may use TPIDR2 for lazy saving in any callee. In this case we
need to restore the original TPIDR2 of the interrupted context on
sigreturn.

So I convinced myself this is the only option that makes sense ;). I'll
queue the patches.
Mark Brown June 23, 2023, 6:22 p.m. UTC | #4
On Fri, Jun 23, 2023 at 06:28:12PM +0100, Catalin Marinas wrote:
> On Thu, Jun 22, 2023 at 06:11:20PM +0100, Mark Brown wrote:

> > TPIDR2 is intended to go along with the thread stack, it's intended to
> > be used to allow lazy save of the (rather large) ZA register state when
> > a called function needs it rather than forcing it to be caller saved.
> > TPIDR2 is used to point to memory allocated for managing this process,
> > something that provides a new value should be making a deliberate
> > decision to do so and editing the stack frame.

> OK, so if the signal handler invokes a function that touches the ZA
> state, it may use TPIDR2 for lazy saving in any callee. In this case we
> need to restore the original TPIDR2 of the interrupted context on
> sigreturn.

Yeah, or if something tries to sigreturn to a previously saved context
which had live TPIDR2 state things might end up unfortunate.

> So I convinced myself this is the only option that makes sense ;). I'll
> queue the patches.

Thanks.
diff mbox series

Patch

diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 2cfc810d0a5b..10b407672c42 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -398,7 +398,7 @@  static int restore_tpidr2_context(struct user_ctxs *user)
 
 	__get_user_error(tpidr2_el0, &user->tpidr2->tpidr2, err);
 	if (!err)
-		current->thread.tpidr2_el0 = tpidr2_el0;
+		write_sysreg_s(tpidr2_el0, SYS_TPIDR2_EL0);
 
 	return err;
 }