Applying these techniques to real code

In Return-oriented programming (ROP) and Jump-oriented programming (JOP), we explored features that Arm introduced to the Arm architecture to mitigate against JOP-style and ROP-style attacks. Now we will look at the compiler support for these features, and how enabling these protections affects the number of that are gadgets available to attackers.

In this section, we refer to these versions of Arm Compiler 6 and Gnu C Compiler (GCC):

  • Arm Compiler 6.11
  • GCC 9.1

Compiler support for these features continues to evolve. Precise figures will vary based on the versions that you use.

Build an image with pointer authentication and branch target identification

For Arm Compiler 6, GCC and LLVM generation of pointer authentication and BTI-enabled code is controlled by:

  • mbranch-protection=<protection>

Where <protection> can be any combination of:

  • pac-ret{+leaf+b-key}
    • pac-ret enables return address signing for non-leaf functions using the A-key.
    • +leaf increases the scope of return address signing to include leaf functions.
    • +b-key uses B-key instructions to sign addresses instead of A-key instructions.
  • bti protects code using Branch Target Identification.
  • standard turns on all types of branch protection.
    • Currently standard implies pac-ret+bti.
  • none turns off all types of branch protection.
    • This is the default if the -mbranch-protection flag is not provided.

Whether the combined or NOP-compatible instructions are generated depends on the architecture version that the code is built for. When building for Armv8.3-A, or later, the compiler will use the combined operations. When building for Armv8.2-A, or earlier, it will use the NOP compatible instructions. For example:

-march=armv8.2-a 
-mbranch-protection=standard 
-march=armv8.3-a
-mbranch-protection=standard
enableInt
 0x00000000: d503233f PACIASP
 ...
 ...
 0x00000350: d50323bf AUTIASP
 0x00000354: 65f03c0  RET
enableInt
  0x00000000: d503233f PACIASP
  ...
  ...
  0x00000350: d65f0bff RETAA

Note: The function used in this example was taken from the example that accompanies our guide Arm CoreLink Generic Interrupt Controller v3 and v4 Overview and built with Arm Compiler 6.

The compiler generates the instructions that are required to perform signing and authentication. Generating and configuring keys is the responsibility of supervising software, typically an operating system.

Reduction in available gadgets

GLIBC is a large library that is used in C or C++ applications. This means that it is a good target for attackers, and a good place for us to see the effect of applying the measures to mitigate attacks. Arm used this tool to measure the number of available gadgets and modified the tool to fit our requirements.

The following graph shows the number of gadgets before and after the compiler options were enabled:

By enabling both pointer authentication and branch target identification, the number of gadgets that are available reduces by 97.65%.

Effect on code size

The protection described in the preceding section is helpful but comes at a cost. One obvious cost is the increase in code size. Here is an analysis of this cost:

The graph shows that the code size effect on GLIBC is minimal. Even though turning on both the mitigations leads to a 2.9% code size increase, this increase is smaller when compiling with -march=armv8.3-a. Compiling for Armv8.3-A allows the compiler to use fused authenticate and return instructions. This means that, for Armv8.3-A, the code size increase is only 1.6%.

Previous Next