[PATCH v1 1/1] aarch64: make RA signing method extendable for upcoming PAuth_LR
Indu Bhagat
indu.bhagat@oracle.com
Tue Nov 12 22:42:47 GMT 2024
On 11/8/24 7:25 AM, Matthieu Longo wrote:
> The Pointer Authentication (PAuth) architecture provides instructions
> that enable software to sign an address using SP as a diversifier.
> This form of PAC instruction is typically used for signing return
> addresses that are stored on the stack.
>
> The new Armv9.5-A architecture introduces an enhancement of the
> previous PAuth feature, called PAuth_LR. It aims at hardening the PAC
> in a signed return address. When signing the return address in LR, the
> PC is used as a diversifier, in addition to the SP to generate PAC code.
>
> PAuth introduced the DWARF register RA_SIGN_STATE which stores the
> return address signed state (currently no signing, or signed with SP).
> PAuth_LR adds a new value to this state: signed with SP and PC.
>
> This DWARF register can now be set via 2 directives:
> - DW_CFA_AARCH64_negate_ra_state: toggle the RA state in a PAuth
> context.
> - DW_CFA_AARCH64_negate_ra_state_with_pc: toggle the RA state in a
> PAuth_LR context.
>
> The existing implementation relies on a binary approach that cannot
> accomodate with the new state value.
> This patch is a refactoring to make the RA signing method extendable for
> the upcoming PAuth_LR patch series.
Hi Matthieu,
Thanks for the patch. And apologies for the massive delay. I somehow
missed this patch in the original series.
It looks like, in theory, an application is allowed to have mixed usage
of DW_CFA_AARCH64_negate_ra_state_with_pc and
DW_CFA_AARCH64_negate_ra_state across functions. Can you confirm my
understanding ?
If yes, then technically the signing method information is a
per-SFrame-FDE information. I will add supporting Pauth_LR in the SFrame
V3 ToDo list. For V3 then, for an SFrame FDE, we would add additional
bits to keep this information. The SFrame FREs will continue to carry
the boolean information of ra_mangled_p.
I am a bit curious now to know how will the subsequent patches use the
refactoring. :)
> ---
> gas/gen-sframe.c | 22 ++++++++++++++--------
> gas/gen-sframe.h | 17 ++++++++++++++---
> include/sframe.h | 4 ++--
> 3 files changed, 30 insertions(+), 13 deletions(-)
>
> diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
> index 626dc33b71d..565148d2842 100644
> --- a/gas/gen-sframe.c
> +++ b/gas/gen-sframe.c
> @@ -254,11 +254,13 @@ static struct sframe_version_ops sframe_ver_ops;
>
> static unsigned char
> sframe_v1_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
> - unsigned int offset_size, bool mangled_ra_p)
> + unsigned int offset_size,
> + ra_signing_method_t ra_signing_info)
> {
> unsigned char fre_info;
> fre_info = SFRAME_V1_FRE_INFO (base_reg, num_offsets, offset_size);
> - fre_info = SFRAME_V1_FRE_INFO_UPDATE_MANGLED_RA_P (mangled_ra_p, fre_info);
> + fre_info = SFRAME_V1_FRE_INFO_UPDATE_RA_SIGNING_INFO (ra_signing_info,
> + fre_info);
> return fre_info;
> }
>
> @@ -292,10 +294,11 @@ sframe_set_version (uint32_t sframe_version ATTRIBUTE_UNUSED)
>
> static unsigned char
> sframe_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
> - unsigned int offset_size, bool mangled_ra_p)
> + unsigned int offset_size,
> + ra_signing_method_t ra_signing_info)
> {
> return sframe_ver_ops.set_fre_info (base_reg, num_offsets,
> - offset_size, mangled_ra_p);
> + offset_size, ra_signing_info);
> }
>
> /* SFrame set func info. */
> @@ -526,7 +529,7 @@ output_sframe_row_entry (symbolS *fde_start_addr,
> fre_num_offsets = get_fre_num_offsets (sframe_fre);
> fre_offset_size = sframe_get_fre_offset_size (sframe_fre);
> fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets,
> - fre_offset_size, sframe_fre->mangled_ra_p);
> + fre_offset_size, sframe_fre->ra_signing_info);
> out_one (fre_info);
>
> idx = sframe_fre_offset_func_map_index (fre_offset_size);
> @@ -879,7 +882,7 @@ sframe_row_entry_new (void)
> fre->merge_candidate = true;
> /* Reset the mangled RA status bit to zero by default. We will initialize it in
> sframe_row_entry_initialize () with the sticky bit if set. */
> - fre->mangled_ra_p = false;
> + fre->ra_signing_info = ra_no_signing;
>
> return fre;
> }
> @@ -927,7 +930,7 @@ sframe_row_entry_initialize (struct sframe_row_entry *cur_fre,
> cur_fre->ra_offset = prev_fre->ra_offset;
> /* Treat RA mangling as a sticky bit. It retains its value until another
> .cfi_negate_ra_state is seen. */
> - cur_fre->mangled_ra_p = prev_fre->mangled_ra_p;
> + cur_fre->ra_signing_info = prev_fre->ra_signing_info;
> }
>
> /* Return SFrame register name for SP, FP, and RA, or NULL if other. */
> @@ -1276,7 +1279,10 @@ sframe_xlate_do_aarch64_negate_ra_state (struct sframe_xlate_ctx *xlate_ctx,
>
> gas_assert (cur_fre);
> /* Toggle the mangled RA status bit. */
> - cur_fre->mangled_ra_p = !cur_fre->mangled_ra_p;
> + cur_fre->ra_signing_info
> + = ((cur_fre->ra_signing_info == ra_no_signing)
> + ? ra_signing_sp
> + : ra_no_signing);
> cur_fre->merge_candidate = false;
>
> return SFRAME_XLATE_OK;
> diff --git a/gas/gen-sframe.h b/gas/gen-sframe.h
> index 8ed46dbb087..35cc01ff0be 100644
> --- a/gas/gen-sframe.h
> +++ b/gas/gen-sframe.h
> @@ -21,11 +21,22 @@
> #ifndef GENSFRAME_H
> #define GENSFRAME_H
>
> +#include "ansidecl.h"
> +
> #define SFRAME_FRE_ELEM_LOC_REG 0
> #define SFRAME_FRE_ELEM_LOC_STACK 1
>
> #define SFRAME_FRE_BASE_REG_INVAL ((unsigned int)-1)
>
> +/* Signing method used for return address authentication.
> + (AArch64 extension) */
> +
> +typedef enum ATTRIBUTE_PACKED
> +{
> + ra_no_signing = 0x0,
> + ra_signing_sp = 0x1,
> +} ra_signing_method_t;
> +
> /* SFrame Frame Row Entry (FRE).
>
> A frame row entry is a slice of the frame and can be valid for a set of
> @@ -52,8 +63,8 @@ struct sframe_row_entry
> on it. */
> bool merge_candidate;
>
> - /* Whether the return address is mangled with pauth code. */
> - bool mangled_ra_p;
> + /* Track the PAuth mangling information for RA. */
> + ra_signing_method_t ra_signing_info;
>
I think at the FRE level, we will continue to track whether RA is signed
or not.
But later on, when we add necessary bits in SFrame V3 to convey the
signing method, we can keep the 'ra_signing_method_t ra_signing_info' in
the sframe_xlate_ctx object and pass it to SFrame FDE at relevant points.
> /* Track CFA base (architectural) register ID. */
> unsigned int cfa_base_reg;
> @@ -146,7 +157,7 @@ struct sframe_version_ops
> unsigned char format_version; /* SFrame format version. */
> /* set SFrame FRE info. */
> unsigned char (*set_fre_info) (unsigned int, unsigned int, unsigned int,
> - bool);
> + ra_signing_method_t);
> /* set SFrame Func info. */
> unsigned char (*set_func_info) (unsigned int, unsigned int, unsigned int);
> };
> diff --git a/include/sframe.h b/include/sframe.h
> index 347dbc73787..4e7092d0bbe 100644
> --- a/include/sframe.h
> +++ b/include/sframe.h
> @@ -264,8 +264,8 @@ typedef struct sframe_fre_info
> (((offset_num) & 0xf) << 1) | ((base_reg_id) & 0x1))
>
> /* Set the mangled_ra_p bit as indicated. */
> -#define SFRAME_V1_FRE_INFO_UPDATE_MANGLED_RA_P(mangled_ra_p, fre_info) \
> - ((((mangled_ra_p) & 0x1) << 7) | ((fre_info) & 0x7f))
> +#define SFRAME_V1_FRE_INFO_UPDATE_RA_SIGNING_INFO(ra_signing_info, fre_info) \
> + ((((ra_signing_info) & 0x1) << 7) | ((fre_info) & 0x7f))
>
We should not remove the SFRAME_V1_FRE_INFO_UPDATE_MANGLED_RA_P definition.
The way I see it is that the signing method has two flavors now:
- pauth (SP)
- pauth_lr (SP + PC)
Whether or not RA is mangled is still boolean. Also the signing method
is chosen per function (correct ?), so we simply need to keep this
information per SFrame FDE.
Additionally, removing a macro may cause a consumer program of libsframe
to run into build time failures.
> #define SFRAME_V1_FRE_CFA_BASE_REG_ID(data) ((data) & 0x1)
> #define SFRAME_V1_FRE_OFFSET_COUNT(data) (((data) >> 1) & 0xf)
More information about the Binutils
mailing list