Skip to content

arch/arm/arm64: Introduce Pointer Authentication support#369

Closed
michpappas wants to merge 2 commits intounikraft:stagingfrom
michpappas:arm64_introduce_pointer_authentication
Closed

arch/arm/arm64: Introduce Pointer Authentication support#369
michpappas wants to merge 2 commits intounikraft:stagingfrom
michpappas:arm64_introduce_pointer_authentication

Conversation

@michpappas
Copy link
Copy Markdown
Member

@michpappas michpappas commented Dec 12, 2021

Prerequisite checklist

  • Read the contribution guidelines regarding submitting new changes to the project;
  • Tested your changes against relevant architectures and platforms;
  • Ran the checkpatch.pl on your commit series before opening this PR;
  • Updated relevant documentation.

Base target

  • Architecture(s): arm64
  • Platform(s): common
  • Application(s): N/A

Additional configuration

This PR introduces CONFIG_ARM64_FEAT_PAUTH to enable Pointer Authentication support.

Description of changes

Pointer Authentication (PAuth) allows signing and authenticating pointers to harden the system against classes of attacks that rely on the manipulation of pointers, such as ROP. Pointer Authentication Codes (PACs) are created by signing a pointer and a 64-bit modifier with an 128-bit key. The modifier is a value that is normally used to restrict the PAC to a specific context (eg the SP). Keys are stored in registerers.

The PAC is stored in the unused upper bits of the memory address, and can be later verified to ensure that the pointer has not been tampered.

The reference algorithm is the QARMA block cipher, but it is possible for architectures to use an IMPLEMENTATION DEFINED algorithm instead.

Basic Instructions:

  • PAC*: Sign address
  • AUT*: Authenticate address
  • XPA*: Strip PAC from address (useful for things like unwinding)

Combined Instructions:

  • RETA*: Auth and return
  • BRA*: Auth and branch
  • BLRA*: Auth and branch-link
  • ERETA*: Auth and return from exception
  • LDRA*: Auth and load

Keys:

  • APIA_Key: Key A for signing instructions
  • APIB_Key: Key B for signing instructions
  • APDA_Key: Key A for signing data
  • APDB_Key: Key B for signing data
  • APGA_Key: Key A for generic use

For more background see:

Architecture support

Armv8.3:

  • Introduce FEAT_PAuth as a mandatory feature.
  • Introduce FEAT_PAuth2 as an optional feature in Armv8.3 and mandatory in
    Armv8.6. FEAT_PAuth2 changes the way the PAC is applied to the address
    (XOR vs replace).
  • Introduce FEAT_FPAC, which allows to control the faulting behavior when authentication
    fails. FEAT_FPAC is an optional feature and depends on FEAT_PAuth2.

GCC support

GCC 7:

  • Introduce support for armv8.3-a
  • Introduce --msign-return-address=[none|non-leaf|all]

The parameters passed to --msign-return-address are interpreted as:

  • none: Do not sign return addresses
  • non-leaf: Sign/auth the return address of non-leaf functions.
  • all: Sign/auth the return address of non-leaf and leaf functions.

GCC implements the Basic Set of PAuth instructions using the HINT instruction, in the so-called NOP space. This allows executing protected binaries on older platforms that do not implement Armv8.3-a. In these platforms PAuth instructions
execute as NOP.

When sign-return-address is enabled without setting -march=armv8.3-a, GCC generates PACIASP / AUTIASP sequences that sign and authenticate the LR upon function entry and exit. In the following snippet notice the opcode of the
HINT instruction (0xd5032300) that GCC generates for PACIASP and AUTIASP.

0000000000400564 <main>:
  400564:       d503233f        paciasp
  400568:       a9bf7bfd        stp     x29, x30, [sp, #-16]!
  40056c:       910003fd        mov     x29, sp
  400570:       90000000        adrp    x0, 400000 <_init-0x3e8>
  400574:       9118c000        add     x0, x0, #0x630
  400578:       97ffffb6        bl      400450 <puts@plt>
  40057c:       52800000        mov     w0, #0x0
  400580:       a8c17bfd        ldp     x29, x30, [sp], #16
  400584:       d50323bf        autiasp
  400588:       d65f03c0        ret

When -msign-return-address is used along with -march=armv8.3-a, GCC
generates PACIASP / RETAA sequences instead. In the snippet below notice the
opcode of RETAA (0xd65f0bff) which is in the fused space (instructions in the
Combined Set do not have HINT implementations). Clearly, this code cannot be
executed in architectures earlier than Armv8.3-a.

0000000000400564 <main>:
  400564:       d503233f        paciasp
  400568:       a9bf7bfd        stp     x29, x30, [sp, #-16]!
  40056c:       910003fd        mov     x29, sp
  400570:       90000000        adrp    x0, 400000 <_init-0x3e8>
  400574:       9118a000        add     x0, x0, #0x628
  400578:       97ffffb6        bl      400450 <puts@plt>
  40057c:       52800000        mov     w0, #0x0
  400580:       a8c17bfd        ldp     x29, x30, [sp], #16
  400584:       d65f0bff        retaa

GCC 9:

  • Introduce support for armv8.5-a
  • Deprecate -msign-return-address
  • Introduce -mbranch-protection=[none|pac-ret{+leaf}|bti|standard]
  • Introduce --enable-standard-branch-protection as a short to mbranch-protection=standard

The parameters passed to -mbranch-protection are interpreted as:

  • none: Disables all protections
  • pac-ret: Enables PAuth for function returns on non-leaf functions. The
    +leaf modifier enables protection for leaf functions.
  • bti: Enables Branch Target Identification
  • standard: Enables all protections

GCC 10:

  • Introduce the b-key addendum to pac-ret in --mbranch-protection.
    This allows using the APIB_Key instead of the APIA_Key when signing
    return pointers.

Changes introduced in this series

This PR provides platforms with an API to initialize PAuth keys, and enable PAuth.

A platform can initialize PAuth as:

#ifdef CONFIG_ARM64_FEAT_PAUTH
	if (ukplat_pauth_enable())
		UK_CRASH("Pointer Authentication is not available");
#endif

To generate PAyth keys, it is required that platforms provide an implementation of the key generation function that uses an adequate source of randomness.

void ukplat_pauth_gen_key(__u64 *key_hi, __u64 *key_lo);

Platforms that support Armv8.5 can use the random number generation instructions introduced into the architecture (RNDR/RNDRSS). Others can initialize drivers of an HWRNG/TRNG, or request randomness from the TEE.

Since the above functions are used for the initialization of PAuth, it is required that GCC excludes them when generating PACIASP/RETAA sequences. Otherwise, once PAuth is enabled, authentication will fail upon return. A macro is provided to help overriding the global gcc settings per function:

#define __no_pauth __attribute__((target("branch-protection=none")))

This macro is used by pauth_enable(), and should be used by all functions generated by GCC in the boot chain, up to - and including - the caller of pauth_init().

Additionally to the above, the following choices are made:

Do not enable backwards compatibility mode.
If -march=armv8.3-a is not set, it is not possible to initialize the keys.
GCC exits with the following error:

bash$ ~/toolchains/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin/aarch64-none-elf-gcc -mbranch-protection=standard /tmp/test.c
/tmp/ccx7K1nD.s: Assembler messages:
/tmp/ccx7K1nD.s:17: Error: selected processor does not support system register name 'apiakeyhi_el1'

I believe that Arm's intention behind compatibility mode was mostly to provide backwards compatibility on userspace libraries. Clearly on Unikraft this feature cannot be utilized, except from the case of linking with prebuilt libraries.
Because of that this PR sets -march=armv8.3-a when enabling CONFIG_ARM64_FEAT_PAUTH.

Enable leaf protection by default

Although it's not strictly required as leaf functions do not save the LR, it doesn't hurt to be there as an additional protection.

Initialize only the PACIA_Key

The motivation for that is minimizing the impact on boot time. Additional keys can be enabled later, if needed.

Do not introduce additional APIs for sign / auth / strip

The intention of this PR is to provide initial PAuth support. As such, its scope is limited to the signing and verification of the function return address. Nevertheless, Pointer Authentication provides the possibility to harden the system further by signing more pointers. Additional functionality can be introduced into the Unkraft API later, on demand.

@michpappas michpappas requested a review from a team December 12, 2021 15:16
@michpappas
Copy link
Copy Markdown
Member Author

This PR depends on #368

@unikraft-bot unikraft-bot added arch/arm arch/arm64 area/arch Unikraft Architecture area/plat Unikraft Patform lang/c Issues or PRs to do with C/C++ plat/common Common to all platforms labels Dec 13, 2021
@unikraft-bot unikraft-bot requested a review from jongwu December 13, 2021 22:49
@razvand razvand added this to the v0.8 - Enceladus milestone Jan 5, 2022
@razvand razvand requested a review from craciunoiuc January 15, 2022 10:09
@craciunoiuc
Copy link
Copy Markdown
Member

Hey @michpappas,

I did an initial pass of everything and the PR seems good!

I will come back to it to test it once the other PR is merged.

Cezar

@razvand razvand removed the request for review from a team February 5, 2022 05:35
@razvand razvand self-assigned this Feb 5, 2022
@michpappas michpappas force-pushed the arm64_introduce_pointer_authentication branch from 118cef0 to b0412ce Compare February 19, 2022 16:37
@michpappas
Copy link
Copy Markdown
Member Author

Rebased and fixed a bug in the __no_branch_prediction macro in plat/common/include/arm/arm64/pauth.h.

Copy link
Copy Markdown
Member

@craciunoiuc craciunoiuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @michpappas,

This PR looks good. I have just a small comment. After that I can approve it 🙂

Comment thread plat/common/include/arm/arm64/pauth.h Outdated
Comment on lines +31 to +39
/* Macro to disable the global branch-protection settings for a specific
* function. This needs to be declared on any functions in the boot-chain
* up to the caller of ukplat_pauth_init() to avoid PAuth errors upon return.
*/
#if CONFIG_ARM64_FEAT_PAUTH
#define __no_branch_protection __attribute__((target("branch-protection=none")))
#else
#define __no_branch_protection
#endif
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really get the #if brace here.

  1. There might be other cases where branch protection needs to be disabled apart from the PAUTH case

  2. This macro is in the pauth.h file. It should be used only in the context of pauth anyway.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @craciunoiuc,

  1. There might be other cases where branch protection needs to be disabled apart from the PAUTH case

Yes in general the branch-protection option can also be used for BTI, so my on-off logic was in the current scope of BTI not supported yet. But that should change soon, as I have a PR on BTI almost ready.
With BTI enabled the macro will have to include additional logic to take into account whether CONFIG_ARM64_FEAT_BTI is enabled or not, and change the value of branch-protection accordingly. Saying that, a more suitable name for that macro would be something like _no_pauth_prot or something similar.

  1. This macro is in the pauth.h file. It should be used only in the context of pauth anyway.

Well, it depends, one could include the header unconditionally, but overall I agree. Especially as GCC uses the same parameter for PAuth and BTI, this should probably be moved to a generic header.

I'll think about it a bit more as I work on the BTI PR. Will push an update soon.

Introduce a header for architecture-specific compiler definitions.
Architectures add compiler definitions in uk/asm/compiler.h, which
are made available through uk/essentials.h

This commit adds empty headers for arm, arm64, and x86_64.

Signed-off-by: Michalis Pappas <mpappas@fastmail.fm>
@michpappas michpappas force-pushed the arm64_introduce_pointer_authentication branch from b0412ce to 4d7db7d Compare March 6, 2022 10:38
@michpappas
Copy link
Copy Markdown
Member Author

Changes in this update

Config.uk:

  • Switch to a flat structure
  • Update help message to a more generic description

Makefile.uk:

compiler.h:

  • Introduce header for architecture-specific compiler definitions.
  • Move the __no_branch_protection macro to compiler.h.
  • Rename the __no_branch_protection macro to __no_pauth.

pauth.c:

  • Reduce API to a single function, ukpauth_init()

pauth.h:

  • Add include guards
  • Change the return type of ukplat_gen_key() to void
  • Add note about using __no_pauth when declaring ukplat_gen_key()

lcpu.h:

  • Use the _AC macro where needed

@michpappas michpappas changed the title arch/arm/arm64: Introduce pointer authentication support arch/arm/arm64: Introduce Pointer Authentication support Mar 6, 2022
@michpappas
Copy link
Copy Markdown
Member Author

michpappas commented Mar 6, 2022

PR description and commit message updated based on latest changes.

@razvand razvand removed the request for review from jongwu March 9, 2022 11:29
Copy link
Copy Markdown
Member

@craciunoiuc craciunoiuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello again @michpappas,

The PR looks good now, but before adding my tag, here are some typos from the second commit message 😅:

"stored in registerers. The PAC" -> "stored in registers. The PAC"
"been tampered." -> "been tampered with."
"Pointer Authenticaiton" ->"Pointer Authentication"
"equired to" -> "required to"

After this, we can finally close this PR 😆.

Comment thread arch/arm/arm64/Config.uk Outdated
help
Enable signing and authentication of pointers. This
provides protections against classes of attacks that
rely on memory corruption, such as smash stacking and
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rely on memory corruption, such as smash stacking and
rely on memory corruption, such as stack smashing and

Comment thread arch/arm/arm64/Config.uk
Pointer Authentication (FEAT_PAuth) allows signing and authenticating
pointers to harden systems against classes of attacks that rely on
memory corruption, such as stack smashing and ROP.

Pointer Authentication Codes (PACs) are created by signing a pointer and
a 64-bit modifier with an 128-bit key. The modifier is a value that is
used to restrict the PAC to a specific context (eg the SP). Keys are
stored in registers. The PAC is stored in the unused upper bits of the
memory address, and can be later verified to ensure that the pointer has
not been tampered with.

This commit adds basic Pointer Authentication support for platforms that
support arm64. Specifically, it introduces:

- The CONFIG_ARM64_FEAT_PAUTH option. When enabled, GCC is instructed
  to generate PACIASP/RETAA sequences to protect the return address of
  functions using the PACIA_Key.

- An API to set up and enable Pointer Authentication. Platforms are
  required to implement a key generation function that uses a secure
  source of randomness.

- A macro to disable Pointer Authentication in certain functions. This
  is required on any returning function that is called prior to the
  initialization of Pointer Authentication.

Unikraft images generated with FEAT_PAuth are compatible with
architectures that implement Armv8.3 and above. That is due to the fact
that, although the basic set of Pointer Authentication instructions is
implemented in the NOP space, combined instructions and registers
holding the keys are not.

Signed-off-by: Michalis Pappas <mpappas@fastmail.fm>
@michpappas michpappas force-pushed the arm64_introduce_pointer_authentication branch from 4d7db7d to 140af00 Compare March 12, 2022 09:30
@michpappas
Copy link
Copy Markdown
Member Author

@craciunoiuc, typos are fixed now

Copy link
Copy Markdown
Member

@craciunoiuc craciunoiuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good!

@razvand your turn now 😉

Reviewed-by: Cezar Craciunoiu cezar.craciunoiu@gmail.com

@unikraft-bot
Copy link
Copy Markdown
Member

Checkpatch passed

Beep boop! I ran Unikraft's checkpatch.pl support script on your pull request and it all looks good!

SHA commit checkpatch
8682589 arch/*: Introduce uk/asm/compiler.h
140af00 arch/arm/arm64: plat/common/arm: Add Pointer Authentication support

Copy link
Copy Markdown
Contributor

@razvand razvand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michpappas , this looks good. Before approving, please check the copyright information.

Comment thread arch/arm/arm/include/uk/asm/compiler.h
Comment thread arch/x86/x86_64/include/uk/asm/compiler.h
Copy link
Copy Markdown
Contributor

@razvand razvand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved-by: Razvan Deaconescu razvan.deaconescu@cs.pub.ro

unikraft-bot pushed a commit that referenced this pull request Mar 29, 2022
Pointer Authentication (FEAT_PAuth) allows signing and authenticating
pointers to harden systems against classes of attacks that rely on
memory corruption, such as stack smashing and ROP.

Pointer Authentication Codes (PACs) are created by signing a pointer and
a 64-bit modifier with an 128-bit key. The modifier is a value that is
used to restrict the PAC to a specific context (eg the SP). Keys are
stored in registers. The PAC is stored in the unused upper bits of the
memory address, and can be later verified to ensure that the pointer has
not been tampered with.

This commit adds basic Pointer Authentication support for platforms that
support arm64. Specifically, it introduces:

- The CONFIG_ARM64_FEAT_PAUTH option. When enabled, GCC is instructed
  to generate PACIASP/RETAA sequences to protect the return address of
  functions using the PACIA_Key.

- An API to set up and enable Pointer Authentication. Platforms are
  required to implement a key generation function that uses a secure
  source of randomness.

- A macro to disable Pointer Authentication in certain functions. This
  is required on any returning function that is called prior to the
  initialization of Pointer Authentication.

Unikraft images generated with FEAT_PAuth are compatible with
architectures that implement Armv8.3 and above. That is due to the fact
that, although the basic set of Pointer Authentication instructions is
implemented in the NOP space, combined instructions and registers
holding the keys are not.

Signed-off-by: Michalis Pappas <mpappas@fastmail.fm>
Reviewed-by: Cezar Craciunoiu <cezar.craciunoiu@gmail.com>
Approved-by: Razvan Deaconescu <razvan.deaconescu@cs.pub.ro>
Tested-by: Unikraft CI <monkey@unikraft.io>
GitHub-Closes: #369
@unikraft-bot unikraft-bot added the ci/merged Merged by CI label Mar 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch/arm arch/arm64 arch/x86_64 area/arch Unikraft Architecture area/include Part of include/uk area/plat Unikraft Patform ci/merged Merged by CI lang/c Issues or PRs to do with C/C++ plat/common Common to all platforms

Projects

Status: ✅ Done!
Status: review:invalid

Development

Successfully merging this pull request may close these issues.

5 participants