[3/3] x86/retpoline: Ensure default return thunk isn't used at runtime

Message ID 20231010171020.462211-4-david.kaplan@amd.com
State New
Headers
Series Ensure default return thunk isn't used at runtime |

Commit Message

Kaplan, David Oct. 10, 2023, 5:10 p.m. UTC
  All CPU bugs that require a return thunk define a special return thunk
to use (e.g., srso_return_thunk).  The default thunk,
__x86_return_thunk, should never be used after apply_returns() completes.
Otherwise this could lead to potential speculation holes.

Enforce this by replacing this thunk with a ud2 when alternatives are
applied.  Alternative instructions are applied after apply_returns().

The default thunk is only used during kernel boot, it is not used during
module init since that occurs after apply_returns().

Signed-off-by: David Kaplan <david.kaplan@amd.com>
---
 arch/x86/lib/retpoline.S | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
  

Comments

Josh Poimboeuf Oct. 10, 2023, 7:36 p.m. UTC | #1
On Tue, Oct 10, 2023 at 12:10:20PM -0500, David Kaplan wrote:
> All CPU bugs that require a return thunk define a special return thunk
> to use (e.g., srso_return_thunk).  The default thunk,
> __x86_return_thunk, should never be used after apply_returns() completes.
> Otherwise this could lead to potential speculation holes.
> 
> Enforce this by replacing this thunk with a ud2 when alternatives are
> applied.  Alternative instructions are applied after apply_returns().
> 
> The default thunk is only used during kernel boot, it is not used during
> module init since that occurs after apply_returns().
> 
> Signed-off-by: David Kaplan <david.kaplan@amd.com>
> ---
>  arch/x86/lib/retpoline.S | 8 +++++---
>  1 file changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S
> index 3da768a71cf9..10212cf4a9af 100644
> --- a/arch/x86/lib/retpoline.S
> +++ b/arch/x86/lib/retpoline.S
> @@ -358,15 +358,17 @@ SYM_FUNC_END(call_depth_return_thunk)
>   * This function name is magical and is used by -mfunction-return=thunk-extern
>   * for the compiler to generate JMPs to it.
>   *
> - * This code is only used during kernel boot or module init.  All
> + * This code is only used during kernel boot.  All
>   * 'JMP __x86_return_thunk' sites are changed to something else by
>   * apply_returns().
> + *
> + * This thunk is turned into a ud2 to ensure it is never used at runtime.
> + * Alternative instructions are applied after apply_returns().
>   */
>  SYM_CODE_START(__x86_return_thunk)
>  	UNWIND_HINT_FUNC
>  	ANNOTATE_NOENDBR
> -	ANNOTATE_UNRET_SAFE
> -	ret
> +	ALTERNATIVE __stringify(ANNOTATE_UNRET_SAFE;ret),"ud2", X86_FEATURE_RETHUNK

If it's truly never used after boot (even for non-rethunk cases) then
can we use X86_FEATURE_ALWAYS?
  
Kaplan, David Oct. 10, 2023, 8:14 p.m. UTC | #2
[AMD Official Use Only - General]

> -----Original Message-----
> From: Josh Poimboeuf <jpoimboe@kernel.org>
> Sent: Tuesday, October 10, 2023 2:37 PM
> To: Kaplan, David <David.Kaplan@amd.com>
> Cc: x86@kernel.org; luto@kernel.org; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH 3/3] x86/retpoline: Ensure default return thunk isn't used
> at runtime
>
> >   *
> > - * This code is only used during kernel boot or module init.  All
> > + * This code is only used during kernel boot.  All
> >   * 'JMP __x86_return_thunk' sites are changed to something else by
> >   * apply_returns().
> > + *
> > + * This thunk is turned into a ud2 to ensure it is never used at runtime.
> > + * Alternative instructions are applied after apply_returns().
> >   */
> >  SYM_CODE_START(__x86_return_thunk)
> >       UNWIND_HINT_FUNC
> >       ANNOTATE_NOENDBR
> > -     ANNOTATE_UNRET_SAFE
> > -     ret
> > +     ALTERNATIVE __stringify(ANNOTATE_UNRET_SAFE;ret),"ud2",
> > + X86_FEATURE_RETHUNK
>
> If it's truly never used after boot (even for non-rethunk cases) then can we use
> X86_FEATURE_ALWAYS?
>

I think that could work.  There is one subtlety though I'll point out:

The use of __x86_return_thunk when X86_FEATURE_RETHUNK is set is a potential security issue, as it means the required return thunk is not being used.  The use of __x86_return_thunk when X86_FEATURE_RETHUNK is not set is only a performance issue, as it means there is a return that was not rewritten to be an inline 'ret' by apply_returns().

The ud2 was primarily intended to capture cases where there is a potential security hole, while it is a bit overkill just to point out a return that was not optimized.

--David Kaplan
  
Josh Poimboeuf Oct. 10, 2023, 8:41 p.m. UTC | #3
On Tue, Oct 10, 2023 at 08:14:33PM +0000, Kaplan, David wrote:
> [AMD Official Use Only - General]
> 
> > -----Original Message-----
> > From: Josh Poimboeuf <jpoimboe@kernel.org>
> > Sent: Tuesday, October 10, 2023 2:37 PM
> > To: Kaplan, David <David.Kaplan@amd.com>
> > Cc: x86@kernel.org; luto@kernel.org; linux-kernel@vger.kernel.org
> > Subject: Re: [PATCH 3/3] x86/retpoline: Ensure default return thunk isn't used
> > at runtime
> >
> > >   *
> > > - * This code is only used during kernel boot or module init.  All
> > > + * This code is only used during kernel boot.  All
> > >   * 'JMP __x86_return_thunk' sites are changed to something else by
> > >   * apply_returns().
> > > + *
> > > + * This thunk is turned into a ud2 to ensure it is never used at runtime.
> > > + * Alternative instructions are applied after apply_returns().
> > >   */
> > >  SYM_CODE_START(__x86_return_thunk)
> > >       UNWIND_HINT_FUNC
> > >       ANNOTATE_NOENDBR
> > > -     ANNOTATE_UNRET_SAFE
> > > -     ret
> > > +     ALTERNATIVE __stringify(ANNOTATE_UNRET_SAFE;ret),"ud2",
> > > + X86_FEATURE_RETHUNK
> >
> > If it's truly never used after boot (even for non-rethunk cases) then can we use
> > X86_FEATURE_ALWAYS?
> >
> 
> I think that could work.  There is one subtlety though I'll point out:
> 
> The use of __x86_return_thunk when X86_FEATURE_RETHUNK is set is a
> potential security issue, as it means the required return thunk is not
> being used.  The use of __x86_return_thunk when X86_FEATURE_RETHUNK is
> not set is only a performance issue, as it means there is a return
> that was not rewritten to be an inline 'ret' by apply_returns().
> 
> The ud2 was primarily intended to capture cases where there is a
> potential security hole, while it is a bit overkill just to point out
> a return that was not optimized.

Even if it's not a security hole, I'd still view it as a major BUG() as
it would directly contradict our understanding (and the comments above)
and could cause performance or other correctness issues that would
otherwise go unnoticed.

So I think an unconditional UD2 is warranted.
  

Patch

diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S
index 3da768a71cf9..10212cf4a9af 100644
--- a/arch/x86/lib/retpoline.S
+++ b/arch/x86/lib/retpoline.S
@@ -358,15 +358,17 @@  SYM_FUNC_END(call_depth_return_thunk)
  * This function name is magical and is used by -mfunction-return=thunk-extern
  * for the compiler to generate JMPs to it.
  *
- * This code is only used during kernel boot or module init.  All
+ * This code is only used during kernel boot.  All
  * 'JMP __x86_return_thunk' sites are changed to something else by
  * apply_returns().
+ *
+ * This thunk is turned into a ud2 to ensure it is never used at runtime.
+ * Alternative instructions are applied after apply_returns().
  */
 SYM_CODE_START(__x86_return_thunk)
 	UNWIND_HINT_FUNC
 	ANNOTATE_NOENDBR
-	ANNOTATE_UNRET_SAFE
-	ret
+	ALTERNATIVE __stringify(ANNOTATE_UNRET_SAFE;ret),"ud2", X86_FEATURE_RETHUNK
 	int3
 SYM_CODE_END(__x86_return_thunk)
 EXPORT_SYMBOL(__x86_return_thunk)