Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AArch64][PAC][lldb] Support PAuth features in user expressions #68

Open
wants to merge 9 commits into
base: elf-pauth
Choose a base branch
from

Conversation

kovdan01
Copy link
Contributor

@kovdan01 kovdan01 commented Jan 25, 2024

This PR contains changes related to PAuth support in lldb (particularly, in lldb user expressions).

For implicitly signed stuff, we rely on correct LangOptions. They are set depending on the GNU_PROPERTY_AARCH64_FEATURE_PAUTH value, so the PR includes its support implementation at all levels. See:

For explicitly signed stuff, we still need to set LangOptions properly, but we also need to parse the explicit signing schema from Dwarf. See:

  • __ptrauth-qualified pointers: 8e62fa8
  • [[clang::ptrauth_vtable_pointer(...)]] attribute: 8ed84bc
  • [[clang::ptrauth_struct(...)]] attribute: 3a04a18

Any prefix of the patch series can be merged independently of the remaining suffix - when reset to any of the commits, all tests still pass and the functionality implemented in the commits from the prefix of the series works.


The PR contains tests for all the changes, but manual testing is also used since lldb unit tests might not be enough (at least unless we have lldb shell lit tests). See C/C++ code under spoilers and lldb session output while debugging corresponding executables below.

Free function pointer
source

int n = 0;

typedef void fptr();

void a() {
  n = 1;
}

fptr *b = &a;

int main() {
  b();
  return n;
}

lldb session

$ lldb ./freefun
(lldb) target create "./freefun"
Current executable set to '/path/to/freefun' (aarch64).
(lldb) b main
Breakpoint 1: where = freefun`main + 20 at freefun.c:12:3, address = 0x0000000000010770
(lldb) r
Process 3155439 launched: '/path/to/freefun' (aarch64)
Process 3155439 stopped
* thread #1, name = 'freefun', stop reason = breakpoint 1.1
    frame #0: 0x0000aaaaaaab0770 freefun`main at freefun.c:12:3
(lldb) p b
(fptr *) 0x0036aaaaaaab074c (actual=0x0000aaaaaaab074c freefun`a at freefun.c:6:5)
(lldb) p n
(int) 0
(lldb) expr b()
(lldb) p n
(int) 1

Member function pointer
source

int n = 0;

struct A {
  void foo() { n = 1; }
};

A a;

void (A::*fptr)() = &A::foo;

int main() {
  (a.*fptr)();
  return n;
}

lldb session

$ lldb ./member
(lldb) target create "./member"
Current executable set to '/path/to/member' (aarch64).
(lldb) b main
Breakpoint 1: where = member`main + 32 at member.cpp:12:7, address = 0x0000000000010774
(lldb) r
Process 3155481 launched: '/path/to/member' (aarch64)
Process 3155481 stopped
* thread #1, name = 'member', stop reason = breakpoint 1.1
    frame #0: 0x0000aaaaaaab0774 member`main at member.cpp:12:7
(lldb) p fptr
(void (A::*)()) 0x0000000000000000001aaaaaaaab0804 (actual=0x0000aaaaaaab0804 member`A::foo() at member-implicit.cpp:4)
(lldb) p n
(int) 0
(lldb) (a.*fptr)()
error: '(a.*fptr)()' is not a valid command.
(lldb) expr (a.*fptr)()
(lldb) p n
(int) 1

Virtual table pointer and virtual function pointer
source

int n = 0;

struct A {
  virtual void foo() { n = 1; }
};

A a;

int main() {
  a.foo();
  return n;
}

lldb session

$ lldb ./implicit/virt
(lldb) target create "./virt"
Current executable set to '/path/to/virt' (aarch64).
(lldb) b main
Breakpoint 1: where = virt`main + 20 at virt.cpp:10:5, address = 0x0000000000010818
(lldb) r
Process 3155530 launched: '/path/to/virt' (aarch64)
Process 3155530 stopped
* thread #1, name = 'virt', stop reason = breakpoint 1.1
    frame #0: 0x0000aaaaaaab0818 virt`main at virt.cpp:10:5
(lldb) p *(void**)(&a)
(void *) 0x0039aaaaaaac0900
(lldb) p *(void**)(0xaaaaaaac0900)
(void *) 0x0010aaaaaaab0838
(lldb) p n
(int) 0
(lldb) expr a.foo()
(lldb) p n
(int) 1

TODO:

  • Add more manual tests in this description
  • Distinguish naming for ELF and MachO PAuth ABI versioning

@kovdan01 kovdan01 added the pauth label Jan 25, 2024
@kovdan01 kovdan01 force-pushed the dkovalev/pauth-lldb branch 2 times, most recently from f85a1a3 to f39ea06 Compare February 7, 2024 04:29
@kovdan01 kovdan01 force-pushed the dkovalev/pauth-lldb branch 11 times, most recently from fd2517e to 2abc4d2 Compare February 12, 2024 10:58
…riminated`

The Dwarf attribute `DW_AT_LLVM_ptrauth_address_discriminated` was
emitted for all ptrauth-qualified types, even if the address
discrimination was disabled. This was related to incorrect usage of
`std::optional<bool>` holding the value of the attribute.
@kovdan01 kovdan01 force-pushed the dkovalev/pauth-lldb branch 2 times, most recently from 08567db to a567cae Compare February 12, 2024 17:06
@kovdan01 kovdan01 changed the title [WIP][PAC][lldb] Add basic ptrauth handling in lldb user expressions [AArch64][PAC][lldb] Support PAuth features in user expressions Feb 12, 2024
@kovdan01 kovdan01 marked this pull request as ready for review February 12, 2024 17:14
@kovdan01 kovdan01 requested review from asl and atrosinenko February 12, 2024 17:14
…RE_PAUTH`

This adds support for `GNU_PROPERTY_AARCH64_FEATURE_PAUTH` feature
handling in llvm-readobj and llvm-readelf. The following constants for
supported platforms are also introduced:

- `GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_INVALID = 0`
- `GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_BAREMETAL = 1`
- `GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX = 2`

For the linux platform, output of the tools contains descriptions of
PAuth features which are enabled/disabled depending on the version
value.
Emit PAuth ABI compatibility tag values as llvm module flags:
- `aarch64-elf-pauthabi-platform`
- `aarch64-elf-pauthabi-version`
Emit `GNU_PROPERTY_AARCH64_FEATURE_PAUTH` property in
`.note.gnu.property` section depending on
`aarch64-elf-pauthabi-platform` and `aarch64-elf-pauthabi-version` llvm
module flags.
- In new PAuth ABI version, support for extra data (except platform and
  version) is dropped. Change container for holding the tag from
  `SmallVector<uint8_t>` to `std::optional<std::array<uint8_t, 16>>`.

- In new PAuth ABI version, the GNU property section becomes the main
  way of ELF marking. Support for alternative way is temporarily
  disabled in this commit unless the corresponding mainline PR and ARM
  PAuth ABI PR are merged.
…E_PAUTH`

Apply signing schema for user expressions when debugging AArch64 ELFs
depending on the `GNU_PROPERTY_AARCH64_FEATURE_PAUTH` property in
`.note.gnu.property` section in the executable object file.

To avoid linking against ObjectFileELF plugin everywhere, define
`ParseGNUPropertyAArch64PAuthABI` as a virtual function in the base
`ObjectFile` class.
This adds support for `DW_TAG_LLVM_ptrauth_type` entries corresponding
to explicitly signed free function pointers in lldb user expressions.

In this patch, as a temporary solution pointer auth schema corresponding
to `-mbranch-protection=pauthabi` is enabled unconditionally. This also
brings support for all kinds of implicitly signed pointers, including
member function pointers, virtual function pointers, etc.
…...)]]` attribute

- Encode the attribute in Dwarf as a `DW_TAG_LLVM_ptrauth_type` entry
  corresponding to vtable pointer. The attribute supports "default"
  values for address and extra discrimination, but in Dwarf we emit
  actual values due to `DW_AT_LLVM_ptrauth_*` attributes limitations. See
  lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp for details.

- When parsing dynamic class and structure types from Dwarf, check the
  vtable pointer type against `DW_TAG_LLVM_ptrauth_type`. If true, apply
  the `clang::ptrauth_vtable_pointer` attribute with corresponding
  parameters to the class record decl. "Default" address and extra
  discrimination values are not restored, the actual ones are, but the
  result is effectively the same since the signing schema must be
  preserved unchanged.
…attribute

Use IR metadata annotations for storing `ptrauth_struct_key` and
`ptrauth_struct_disc` values. Corresponding `DW_TAG_LLVM_annotation`
tags are used in Dwarf.
@kovdan01 kovdan01 force-pushed the dkovalev/pauth-lldb branch from ee59834 to 3a04a18 Compare February 16, 2024 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
1 participant