[PATCH v2 1/8] newlib: libc: define M-profile PACBTI-enablement macros
Victor L. Do Nascimento
victor.donascimento@arm.com
Tue Aug 16 16:11:12 GMT 2022
Richard Earnshaw <Richard.Earnshaw@foss.arm.com> writes:
> On 03/08/2022 16:35, Victor Do Nascimento wrote:
>> Create an assembly header file that conditionally defines fuction
>> prologues/epilogues depending on the compile-time mbranch-protection
>> argument values.
>> * newlib/libc/machine/arm/pacbti.h: New.
>> ---
>> newlib/libc/machine/arm/pacbti.h | 64 ++++++++++++++++++++++++++++++++
>> 1 file changed, 64 insertions(+)
>> create mode 100644 newlib/libc/machine/arm/pacbti.h
>> diff --git a/newlib/libc/machine/arm/pacbti.h
>> b/newlib/libc/machine/arm/pacbti.h
>> new file mode 100644
>> index 000000000..4c31d00bc
>> --- /dev/null
>> +++ b/newlib/libc/machine/arm/pacbti.h
>> @@ -0,0 +1,64 @@
>> +/*
>> + * Copyright (c) 2022 Arm Ltd
>> + * All rights reserved.
>> + *
>> + * Redistribution and use in source and binary forms, with or without
>> + * modification, are permitted provided that the following conditions
>> + * are met:
>> + * 1. Redistributions of source code must retain the above copyright
>> + * notice, this list of conditions and the following disclaimer.
>> + * 2. Redistributions in binary form must reproduce the above copyright
>> + * notice, this list of conditions and the following disclaimer in the
>> + * documentation and/or other materials provided with the distribution.
>> + * 3. The name of the company may not be used to endorse or promote
>> + * products derived from this software without specific prior written
>> + * permission.
>> + *
>> + * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
>> + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
>> + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
>> + * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>> + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
>> + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
>> + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
>> + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
>> + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
>> + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>> + */
>> +
>> +/* Checki whether leaf function PAC signing has been requested
>> + in the -mbranch-protect compile-time option. */
>> +#define LEAF_PROTECT_BIT 2
>> +#define __HAVE_PAC_LEAF \
>> + __ARM_FEATURE_PAC_DEFAULT & (1 << LEAF_PROTECT_BIT)
>
> Either this header needs to avoid polluting the user namespace, or it
> doesn't. If it does, then LEAF_PROTECT_BIT fails to do that. If it
> doesn't then __HAVE_PAC_LEAF should really be HAVE_PAC_LEAF (to avoid
> polluting the reserved namespace. I suspect this header is private to
> newlib (won't be exported to users), so should not be prefixing names
> with __.
>
>> +
>> +/* Macro to handle function entry depending on branch-protection
>> + schemes. */
>> + .macro pacbti_prologue
>> +#if __HAVE_PAC_LEAF
>> +#if __ARM_FEATURE_BTI_DEFAULT
>> + pacbti ip, lr, sp
>> +#else
>> + pac ip, lr, sp
>> +#endif /* __ARM_FEATURE_BTI_DEFAULT */
>> + .cfi_register 143, 12
>> + str ip, [sp, #-4]!
>
> This causes the stack to lose 8-byte alignment. Whilst for leaf
> functions that's probably not a problem, the macro should have an
> option where the user can ask for alignment to be preserved. But
> there should also be an option to not save IP on the stack at all (for
> when the user does not modify IP in the function body).
Work is ongoing on implementing the macro with all necessary features.
A better macro implementation for the prologue is given as follows (but
still lacks any alignment preservation logic):
/* Emit .cfi_offset directives for a consecutive sequence of registers.
PAC_CFI_ADJ will be 4 bytes if IP reg is pushed. */
.macro cfisavelist first, last, index=1
.cfi_offset \last, -4*(\index) - PAC_CFI_ADJ
.if \last-\first
cfisavelist \first, \last-1, \index+1
.endif
.endm
/* Create a prologue entry sequence handling PAC/BTI, if required and emitting
CFI directives for generated PAC code and any pushed registers. */
#define NREG ((\last-\first)+1)
.macro prologue first=-1, last=-1, savepac=PAC_LEAF_PUSH_IP
#if HAVE_PAC_LEAF
#if __ARM_FEATURE_BTI_DEFAULT
pacbti ip, lr, sp
#else
pac ip, lr, sp
#endif /* __ARM_FEATURE_BTI_DEFAULT */
.cfi_register 143, 12
#else
#if __ARM_FEATURE_BTI_DEFAULT
bti
#endif /* __ARM_FEATURE_BTI_DEFAULT */
#endif /* HAVE_PAC_LEAF */
.if \first != -1
.if \last != -1
.if \savepac
push {r\first-r\last, ip}
.cfi_adjust_cfa_offset NREG*4 + PAC_CFI_ADJ
.cfi_offset 143, -PAC_CFI_ADJ
cfisavelist \first, \last
.else
push {r\first-r\last}
.cfi_adjust_cfa_offset NREG*4
cfisavelist \first, \last
.endif
.else
.if \savepac
push {r\first, ip}
.cfi_adjust_cfa_offset 4 + PAC_CFI_ADJ
.cfi_offset 143, -PAC_CFI_ADJ
cfisavelist \first, \first
.else // !\savepac
push {r\first}
cfisavelist \first, \first
.endif
.endif
.else // \first == -1
.if \savepac
push {ip}
.cfi_adjust_cfa_offset PAC_CFI_ADJ
.cfi_offset 143, -PAC_CFI_ADJ
.endif
.endif
.endm
The question remains of how I can ammend the register list in the push
directive when we wish to push an extra dummy register to the stack for
alignment purposes.
A hypothetical solution (albeit a broken one, as we can't redefine macro
arguments in the body of the macro) for when pushing an even no. of
registers + the pac code is:
.if \first != -1
.if \last != -1
.if \savepac
+ .if \align_requested && ( ( (NREG+1) % 2 ) != 0 && (\first != 0) )
+ \first = \first - 1
+ .endif
push {r\first-r\last, ip}
.cfi_adjust_cfa_offset NREG*4 + PAC_CFI_ADJ
+ .if \align_requested && ( ((NREG+1) % 2 ) != 0 && (\first != 0) )
+ /* Having pushed the dummy, restore original register range. */
+ \first = \first + 1
+ .endif
.cfi_offset 143, -PAC_CFI_ADJ
cfisavelist \first, \last
.else
...
The logic above seems correct to me, but I need to figure out a way of
adjusting the value of \first in the least invasive way.
If we emit the push instruction via a separate macro, we could then pass
the \first argument as \first-1 (e.g. pushmacro \first=\first-1,
last=last, savepac=1). Even so, creating a macro just to emit the push
instruction seems overkill. We may end up having way too many macros.
Do you have any insights on how I may be able to fix the above code to
allow for alignment preservation elegantly within the limitations of
what assembler macros can do?
Cheers,
V.
>> + .save {ra_auth_code}
>> + .cfi_def_cfa_offset 4
>> + .cfi_offset 143, -4
>> +#elif __ARM_FEATURE_BTI_DEFAULT
>> + bti
>> +#endif /* __HAVE_PAC_LEAF */
>> + .endm
>> +
>> +/* Macro to handle different branch exchange cases depending on
>> + branch-protection schemes. */
>> + .macro pacbti_epilogue
>> +#if __HAVE_PAC_LEAF
>> + ldr ip, [sp], #4
>> + .cfi_restore 143
>> + .cfi_def_cfa_offset 0
>> + aut ip, lr, sp
>> +#endif /* __HAVE_PAC_LEAF */
>> + bx lr
>> + .endm
>
> I think these macros are really misnamed, firstly, they're only for
> leaf functions and secondly, they (particularly the epilogue) does
> something even if PAC is not needed. So I think they should be
> renamed as 'leaf_prologue' and 'leaf_epilogue' respectively.
>
> In consequence, I think this file should really be merged into the
> existing arm_asm.h, then we don't need yet another header file.
>
> Finally, the header needs to define a (C) macro that defines how much
> to adjust the CFI offset by for various scenarios:
> - When PAC is not used
> - When PAC is used with no alignment
> - When PAC is used and asked for 8-byte alignment to be preserved.
>
> R.
More information about the Newlib
mailing list