[v4,05/22] aarch64: Add GCS support to longjmp

Message ID 20241129163721.2385847-6-yury.khrustalev@arm.com
State Superseded
Delegated to: Carlos O'Donell
Headers
Series aarch64: Add support for Guarded Control Stack extension |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed

Commit Message

Yury Khrustalev Nov. 29, 2024, 4:37 p.m. UTC
  From: Szabolcs Nagy <szabolcs.nagy@arm.com>

This implementations ensures that longjmp across different stacks
works: it scans for GCS cap token and switches GCS if necessary
then the target GCSPR is restored with a GCSPOPM loop once the
current GCSPR is on the same GCS.

This makes longjmp linear time in the number of jumped over stack
frames when GCS is enabled.
---
 sysdeps/aarch64/__longjmp.S | 30 ++++++++++++++++++++++++++++++
 sysdeps/aarch64/setjmp.S    | 10 ++++++++++
 2 files changed, 40 insertions(+)
  

Comments

Carlos O'Donell Dec. 2, 2024, 9:41 p.m. UTC | #1
On 11/29/24 11:37 AM, Yury Khrustalev wrote:
> From: Szabolcs Nagy <szabolcs.nagy@arm.com>
> 
> This implementations ensures that longjmp across different stacks
> works: it scans for GCS cap token and switches GCS if necessary
> then the target GCSPR is restored with a GCSPOPM loop once the
> current GCSPR is on the same GCS.
> 
> This makes longjmp linear time in the number of jumped over stack
> frames when GCS is enabled.

Doesn't expose any specific ABI, and can be committed immediately.

LGTM.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> ---
>  sysdeps/aarch64/__longjmp.S | 30 ++++++++++++++++++++++++++++++
>  sysdeps/aarch64/setjmp.S    | 10 ++++++++++
>  2 files changed, 40 insertions(+)
> 
> diff --git a/sysdeps/aarch64/__longjmp.S b/sysdeps/aarch64/__longjmp.S
> index 7b6add751e..d8670d8995 100644
> --- a/sysdeps/aarch64/__longjmp.S
> +++ b/sysdeps/aarch64/__longjmp.S
> @@ -91,6 +91,36 @@ ENTRY (__longjmp)
>  	ldp	d12, d13, [x0, #JB_D12<<3]
>  	ldp	d14, d15, [x0, #JB_D14<<3]
>  
> +	/* GCS support.  */
> +	mov	x16, 1
> +	CHKFEAT_X16
> +	tbnz	x16, 0, L(gcs_done)
> +	MRS_GCSPR (x2)
> +	ldr	x3, [x0, #JB_GCSPR]
> +	mov	x4, x3
> +	/* x2: GCSPR now.  x3, x4: target GCSPR.  x5, x6: tmp regs.  */
> +L(gcs_scan):
> +	cmp	x2, x4
> +	b.eq	L(gcs_pop)
> +	sub	x4, x4, 8
> +	/* Check for a cap token.  */
> +	ldr	x5, [x4]
> +	and	x6, x4, 0xfffffffffffff000
> +	orr	x6, x6, 1
> +	cmp	x5, x6
> +	b.ne	L(gcs_scan)
> +L(gcs_switch):
> +	add	x2, x4, 8
> +	GCSSS1 (x4)
> +	GCSSS2 (xzr)
> +L(gcs_pop):
> +	cmp	x2, x3
> +	b.eq	L(gcs_done)
> +	GCSPOPM (xzr)
> +	add	x2, x2, 8
> +	b	L(gcs_pop)
> +L(gcs_done):

OK.

> +
>          /* Originally this was implemented with a series of
>  	   .cfi_restore() directives.
>  
> diff --git a/sysdeps/aarch64/setjmp.S b/sysdeps/aarch64/setjmp.S
> index 43fdb1b2fb..f7ffccfaba 100644
> --- a/sysdeps/aarch64/setjmp.S
> +++ b/sysdeps/aarch64/setjmp.S
> @@ -57,6 +57,16 @@ ENTRY (__sigsetjmp)
>  	stp	d10, d11, [x0, #JB_D10<<3]
>  	stp	d12, d13, [x0, #JB_D12<<3]
>  	stp	d14, d15, [x0, #JB_D14<<3]
> +
> +	/* GCS support.  */
> +	mov	x16, 1
> +	CHKFEAT_X16
> +	tbnz	x16, 0, L(gcs_done)
> +	MRS_GCSPR (x2)
> +	add	x2, x2, 8 /* GCS state right after setjmp returns.  */
> +	str	x2, [x0, #JB_GCSPR]
> +L(gcs_done):

OK.

> +
>  #ifdef PTR_MANGLE
>  	mov	x4, sp
>  	PTR_MANGLE (5, 4, 3, 2)
  

Patch

diff --git a/sysdeps/aarch64/__longjmp.S b/sysdeps/aarch64/__longjmp.S
index 7b6add751e..d8670d8995 100644
--- a/sysdeps/aarch64/__longjmp.S
+++ b/sysdeps/aarch64/__longjmp.S
@@ -91,6 +91,36 @@  ENTRY (__longjmp)
 	ldp	d12, d13, [x0, #JB_D12<<3]
 	ldp	d14, d15, [x0, #JB_D14<<3]
 
+	/* GCS support.  */
+	mov	x16, 1
+	CHKFEAT_X16
+	tbnz	x16, 0, L(gcs_done)
+	MRS_GCSPR (x2)
+	ldr	x3, [x0, #JB_GCSPR]
+	mov	x4, x3
+	/* x2: GCSPR now.  x3, x4: target GCSPR.  x5, x6: tmp regs.  */
+L(gcs_scan):
+	cmp	x2, x4
+	b.eq	L(gcs_pop)
+	sub	x4, x4, 8
+	/* Check for a cap token.  */
+	ldr	x5, [x4]
+	and	x6, x4, 0xfffffffffffff000
+	orr	x6, x6, 1
+	cmp	x5, x6
+	b.ne	L(gcs_scan)
+L(gcs_switch):
+	add	x2, x4, 8
+	GCSSS1 (x4)
+	GCSSS2 (xzr)
+L(gcs_pop):
+	cmp	x2, x3
+	b.eq	L(gcs_done)
+	GCSPOPM (xzr)
+	add	x2, x2, 8
+	b	L(gcs_pop)
+L(gcs_done):
+
         /* Originally this was implemented with a series of
 	   .cfi_restore() directives.
 
diff --git a/sysdeps/aarch64/setjmp.S b/sysdeps/aarch64/setjmp.S
index 43fdb1b2fb..f7ffccfaba 100644
--- a/sysdeps/aarch64/setjmp.S
+++ b/sysdeps/aarch64/setjmp.S
@@ -57,6 +57,16 @@  ENTRY (__sigsetjmp)
 	stp	d10, d11, [x0, #JB_D10<<3]
 	stp	d12, d13, [x0, #JB_D12<<3]
 	stp	d14, d15, [x0, #JB_D14<<3]
+
+	/* GCS support.  */
+	mov	x16, 1
+	CHKFEAT_X16
+	tbnz	x16, 0, L(gcs_done)
+	MRS_GCSPR (x2)
+	add	x2, x2, 8 /* GCS state right after setjmp returns.  */
+	str	x2, [x0, #JB_GCSPR]
+L(gcs_done):
+
 #ifdef PTR_MANGLE
 	mov	x4, sp
 	PTR_MANGLE (5, 4, 3, 2)