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

8277204: Implement PAC-RET branch protection on Linux/AArch64 #6334

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9b05854
Build with branch protection
a74nh Oct 12, 2020
66b05a6
Add AArch64 ROP protection runtime flag
a74nh Oct 14, 2020
2c7e273
Add PAC assembly instructions
a74nh Nov 25, 2020
e0e3f66
8264130: PAC-RET protection for Linux/AArch64
a74nh Oct 19, 2020
29471d3
Simplify branch protection configure check
a74nh Nov 10, 2021
cfad2fe
Update UseROPProtection description
a74nh Nov 12, 2021
25e6249
Document pauth functions && remove OS split
a74nh Nov 11, 2021
2c27eb5
Merge master
a74nh Nov 15, 2021
a810ea7
Don't keep LR live across restore_live_registers
a74nh Nov 15, 2021
6f501e6
Fix windows aarch64 by restoring pauth file split
a74nh Nov 15, 2021
dbd6bda
Rename pauth_authenticate_or_strip_return_address
a74nh Nov 15, 2021
deb17a5
Merge master
a74nh Nov 16, 2021
280abc4
Merge master
a74nh Nov 22, 2021
995d8aa
Fix up UseROPProtection flag
a74nh Dec 1, 2021
38c08ef
Default to building without branch-protection
a74nh Dec 10, 2021
63f7515
Remove BSD/Apple specific code
a74nh Dec 10, 2021
9c4f349
Change UseROPProtection to UseBranchProtection
a74nh Dec 13, 2021
3cc2c5e
Fix assembler for post-merge
a74nh Jan 19, 2022
f6f8041
Merge master
a74nh Jan 20, 2022
83d2167
Fix GC issues
a74nh Jan 6, 2022
1479942
Fix jvmci tests
a74nh Jan 18, 2022
0b47654
Fix popframe failures
a74nh Jan 20, 2022
b792561
Fix up nits
a74nh Feb 1, 2022
78da1bd
Change pac-ret defaults on non PAC machines
a74nh Feb 2, 2022
6255d4c
Update copyrights to 2022
a74nh Feb 2, 2022
d97883b
Documentation updates
a74nh Feb 3, 2022
614a326
Review fixups
a74nh Feb 7, 2022
f779513
Merge enter_subframe into enter
a74nh Feb 8, 2022
001a8f1
Set PreserveFramePointer if use_rop_protection is set
a74nh Feb 7, 2022
2062cce
Add comments to enter calls
a74nh Feb 10, 2022
7f80f28
Error on -XX:-PreserveFramePointer -XX:UseBranchProtection=pac-ret
a74nh Feb 11, 2022
f9882ff
Merge master
a74nh Feb 21, 2022
97ae934
Merge master
a74nh Feb 22, 2022
c4e0ee3
Merge master
a74nh Feb 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -805,17 +805,19 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP],
fi
AC_SUBST(FILE_MACRO_CFLAGS)

FLAGS_SETUP_BRANCH_PROTECTION

# EXPORT to API
CFLAGS_JVM_COMMON="$ALWAYS_CFLAGS_JVM $ALWAYS_DEFINES_JVM \
$TOOLCHAIN_CFLAGS_JVM ${$1_TOOLCHAIN_CFLAGS_JVM} \
$OS_CFLAGS $OS_CFLAGS_JVM $CFLAGS_OS_DEF_JVM $DEBUG_CFLAGS_JVM \
$WARNING_CFLAGS $WARNING_CFLAGS_JVM $JVM_PICFLAG $FILE_MACRO_CFLAGS \
$REPRODUCIBLE_CFLAGS"
$REPRODUCIBLE_CFLAGS $BRANCH_PROTECTION_CFLAGS"

CFLAGS_JDK_COMMON="$ALWAYS_CFLAGS_JDK $ALWAYS_DEFINES_JDK $TOOLCHAIN_CFLAGS_JDK \
$OS_CFLAGS $CFLAGS_OS_DEF_JDK $DEBUG_CFLAGS_JDK $DEBUG_OPTIONS_FLAGS_JDK \
$WARNING_CFLAGS $WARNING_CFLAGS_JDK $DEBUG_SYMBOLS_CFLAGS_JDK \
$FILE_MACRO_CFLAGS $REPRODUCIBLE_CFLAGS"
$FILE_MACRO_CFLAGS $REPRODUCIBLE_CFLAGS $BRANCH_PROTECTION_CFLAGS"

# Use ${$2EXTRA_CFLAGS} to block EXTRA_CFLAGS to be added to build flags.
# (Currently we don't have any OPENJDK_BUILD_EXTRA_CFLAGS, but that might
@@ -881,3 +883,24 @@ AC_DEFUN([FLAGS_SETUP_GCC6_COMPILER_FLAGS],
PREFIX: $2, IF_FALSE: [NO_LIFETIME_DSE_CFLAG=""])
$1_GCC6_CFLAGS="${NO_DELETE_NULL_POINTER_CHECKS_CFLAG} ${NO_LIFETIME_DSE_CFLAG}"
])

AC_DEFUN_ONCE([FLAGS_SETUP_BRANCH_PROTECTION],
[
# Is branch protection available?
BRANCH_PROTECTION_AVAILABLE=false
BRANCH_PROTECTION_FLAG="-mbranch-protection=standard"

if test "x$OPENJDK_TARGET_CPU" = xaarch64; then
if test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang; then
FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${BRANCH_PROTECTION_FLAG}],
IF_TRUE: [BRANCH_PROTECTION_AVAILABLE=true])
fi
fi

BRANCH_PROTECTION_CFLAGS=""
UTIL_ARG_ENABLE(NAME: branch-protection, DEFAULT: false,
RESULT: USE_BRANCH_PROTECTION, AVAILABLE: $BRANCH_PROTECTION_AVAILABLE,
a74nh marked this conversation as resolved.
Show resolved Hide resolved
DESC: [enable branch protection when compiling C/C++],
IF_ENABLED: [ BRANCH_PROTECTION_CFLAGS=${BRANCH_PROTECTION_FLAG}])
AC_SUBST(BRANCH_PROTECTION_CFLAGS)
])
@@ -406,6 +406,7 @@ LIBFFI_CFLAGS:=@LIBFFI_CFLAGS@
ENABLE_LIBFFI_BUNDLING:=@ENABLE_LIBFFI_BUNDLING@
LIBFFI_LIB_FILE:=@LIBFFI_LIB_FILE@
FILE_MACRO_CFLAGS := @FILE_MACRO_CFLAGS@
BRANCH_PROTECTION_CFLAGS := @BRANCH_PROTECTION_CFLAGS@

STATIC_LIBS_CFLAGS := @STATIC_LIBS_CFLAGS@

@@ -1369,8 +1369,8 @@ source %{
_NO_SPECIAL_PTR_REG_mask.SUBTRACT(_HEAPBASE_REG_mask);
}

// r29 is not allocatable when PreserveFramePointer is on
if (PreserveFramePointer) {
// r29 is not allocatable when PreserveFramePointer or ROP protection is on
if (PreserveFramePointer || VM_Version::use_rop_protection()) {
_NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(r29->as_VMReg()));
_NO_SPECIAL_REG_mask.SUBTRACT(_FP_REG_mask);
_NO_SPECIAL_PTR_REG_mask.SUBTRACT(_FP_REG_mask);
@@ -987,33 +987,36 @@ class Assembler : public AbstractAssembler {
rf(rt, 0);
}

void hint(int imm) {
system(0b00, 0b011, 0b0010, 0b0000, imm);
}

void nop() {
hint(0);
}

void yield() {
hint(1);
}
// Hint instructions

void wfe() {
hint(2);
#define INSN(NAME, crm, op2) \
void NAME() { \
system(0b00, 0b011, 0b0010, crm, op2); \
}

void wfi() {
hint(3);
}
INSN(nop, 0b000, 0b0000);
INSN(yield, 0b000, 0b0001);
INSN(wfe, 0b000, 0b0010);
INSN(wfi, 0b000, 0b0011);
INSN(sev, 0b000, 0b0100);
INSN(sevl, 0b000, 0b0101);

void sev() {
hint(4);
}
// PAC instructions
INSN(autia1716, 0b0001, 0b100);
INSN(autiasp, 0b0011, 0b101);
INSN(autiaz, 0b0011, 0b100);
INSN(autib1716, 0b0001, 0b110);
INSN(autibsp, 0b0011, 0b111);
INSN(autibz, 0b0011, 0b110);
INSN(pacia1716, 0b0001, 0b000);
INSN(paciasp, 0b0011, 0b001);
INSN(paciaz, 0b0011, 0b000);
INSN(pacib1716, 0b0001, 0b010);
INSN(pacibsp, 0b0011, 0b011);
INSN(pacibz, 0b0011, 0b010);
INSN(xpaclri, 0b0000, 0b111);

void sevl() {
hint(5);
}
#undef INSN

// we only provide mrs and msr for the special purpose system
// registers where op1 (instr[20:19]) == 11 and, (currently) only
@@ -1099,18 +1102,20 @@ class Assembler : public AbstractAssembler {
}

// Unconditional branch (register)
void branch_reg(Register R, int opc) {
void branch_reg(int OP, int A, int M, Register RN, Register RM) {
starti;
f(0b1101011, 31, 25);
f(opc, 24, 21);
f(0b11111000000, 20, 10);
rf(R, 5);
f(0b00000, 4, 0);
f(OP, 24, 21);
f(0b111110000, 20, 12);
f(A, 11, 11);
f(M, 10, 10);
rf(RN, 5);
rf(RM, 0);
}

#define INSN(NAME, opc) \
void NAME(Register R) { \
branch_reg(R, opc); \
#define INSN(NAME, opc) \
void NAME(Register RN) { \
branch_reg(opc, 0, 0, RN, r0); \
}

INSN(br, 0b0000);
@@ -1121,16 +1126,54 @@ class Assembler : public AbstractAssembler {

#undef INSN

#define INSN(NAME, opc) \
void NAME() { \
branch_reg(dummy_reg, opc); \
#define INSN(NAME, opc) \
void NAME() { \
branch_reg(opc, 0, 0, dummy_reg, r0); \
}

INSN(eret, 0b0100);
INSN(drps, 0b0101);

#undef INSN

// PAC return instructions
#define INSN(NAME, M) \
void NAME() { \
branch_reg(0b0010, 1, M, dummy_reg, dummy_reg); \
}

INSN(retaa, 0);
INSN(retab, 1);

#undef INSN

// PAC branch instructions (with zero modifier)
#define INSN(NAME, OP, M) \
void NAME(Register rn) { \
branch_reg(OP, 1, M, rn, dummy_reg); \
}

INSN(braaz, 0b0000, 0);
INSN(brabz, 0b0000, 1);
INSN(blraaz, 0b0001, 0);
INSN(blrabz, 0b0001, 1);

#undef INSN

// PAC branch instructions (with register modifier)
a74nh marked this conversation as resolved.
Show resolved Hide resolved
#define INSN(NAME, OP, M) \
void NAME(Register rn, Register rm) { \
branch_reg(OP, 1, M, rn, rm); \
}

INSN(braa, 0b1000, 0);
INSN(brab, 0b1000, 1);
INSN(blraa, 0b1001, 0);
INSN(blrab, 0b1001, 1);

#undef INSN


// Load/store exclusive
enum operand_size { byte, halfword, word, xword };

@@ -1792,6 +1835,37 @@ void mvnw(Register Rd, Register Rm,
INSN(clz, 0b110, 0b00000, 0b00100);
INSN(cls, 0b110, 0b00000, 0b00101);

// PAC instructions
INSN(pacia, 0b110, 0b00001, 0b00000);
INSN(pacib, 0b110, 0b00001, 0b00001);
INSN(pacda, 0b110, 0b00001, 0b00010);
INSN(pacdb, 0b110, 0b00001, 0b00011);
INSN(autia, 0b110, 0b00001, 0b00100);
INSN(autib, 0b110, 0b00001, 0b00101);
INSN(autda, 0b110, 0b00001, 0b00110);
INSN(autdb, 0b110, 0b00001, 0b00111);

#undef INSN

#define INSN(NAME, op29, opcode2, opcode) \
void NAME(Register Rd) { \
starti; \
f(opcode2, 20, 16); \
data_processing(current_insn, op29, opcode, Rd, dummy_reg); \
}

// PAC instructions (with zero modifier)
INSN(paciza, 0b110, 0b00001, 0b01000);
INSN(pacizb, 0b110, 0b00001, 0b01001);
INSN(pacdza, 0b110, 0b00001, 0b01010);
INSN(pacdzb, 0b110, 0b00001, 0b01011);
INSN(autiza, 0b110, 0b00001, 0b01100);
INSN(autizb, 0b110, 0b00001, 0b01101);
INSN(autdza, 0b110, 0b00001, 0b01110);
INSN(autdzb, 0b110, 0b00001, 0b01111);
INSN(xpaci, 0b110, 0b00001, 0b10000);
INSN(xpacd, 0b110, 0b00001, 0b10001);

#undef INSN

// (2 sources)
@@ -385,6 +385,7 @@ OopMapSet* Runtime1::generate_handle_exception(StubID id, StubAssembler *sasm) {

// load issuing PC (the return address for this stub) into r3
__ ldr(exception_pc, Address(rfp, 1*BytesPerWord));
__ authenticate_return_address(exception_pc, rscratch1);

// make sure that the vm_results are cleared (may be unnecessary)
__ str(zr, Address(rthread, JavaThread::vm_result_offset()));
@@ -433,6 +434,7 @@ OopMapSet* Runtime1::generate_handle_exception(StubID id, StubAssembler *sasm) {
__ str(exception_pc, Address(rthread, JavaThread::exception_pc_offset()));

// patch throwing pc into return address (has bci & oop map)
__ protect_return_address(exception_pc, rscratch1);
__ str(exception_pc, Address(rfp, 1*BytesPerWord));

// compute the exception handler.
@@ -448,6 +450,7 @@ OopMapSet* Runtime1::generate_handle_exception(StubID id, StubAssembler *sasm) {
__ invalidate_registers(false, true, true, true, true, true);
a74nh marked this conversation as resolved.
Show resolved Hide resolved

// patch the return address, this stub will directly return to the exception handler
__ protect_return_address(r0, rscratch1);
__ str(r0, Address(rfp, 1*BytesPerWord));

a74nh marked this conversation as resolved.
Show resolved Hide resolved
switch (id) {
@@ -496,10 +499,12 @@ void Runtime1::generate_unwind_exception(StubAssembler *sasm) {
// Save our return address because
// exception_handler_for_return_address will destroy it. We also
// save exception_oop
__ mov(r3, lr);
__ protect_return_address();
__ stp(lr, exception_oop, Address(__ pre(sp, -2 * wordSize)));

// search the exception handler address of the caller (using the return address)
__ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), rthread, lr);
__ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), rthread, r3);
// r0: exception handler address of the caller

// Only R0 is valid at this time; all other registers have been
@@ -512,6 +517,7 @@ void Runtime1::generate_unwind_exception(StubAssembler *sasm) {
// get throwing pc (= return address).
// lr has been destroyed by the call
__ ldp(lr, exception_oop, Address(__ post(sp, 2 * wordSize)));
__ authenticate_return_address();
__ mov(r3, lr);

__ verify_not_null_oop(exception_oop);
@@ -128,13 +128,13 @@ bool frame::safe_for_sender(JavaThread *thread) {
return false;
}

sender_pc = (address) this->fp()[return_addr_offset];
// for interpreted frames, the value below is the sender "raw" sp,
// which can be different from the sender unextended sp (the sp seen
// by the sender) because of current frame local variables
sender_sp = (intptr_t*) addr_at(sender_sp_offset);
sender_unextended_sp = (intptr_t*) this->fp()[interpreter_frame_sender_sp_offset];
saved_fp = (intptr_t*) this->fp()[link_offset];
sender_pc = pauth_strip_verifiable((address) this->fp()[return_addr_offset], (address)saved_fp);

} else {
// must be some sort of compiled/runtime frame
@@ -151,9 +151,9 @@ bool frame::safe_for_sender(JavaThread *thread) {
return false;
}
sender_unextended_sp = sender_sp;
sender_pc = (address) *(sender_sp-1);
// Note: frame::sender_sp_offset is only valid for compiled frame
saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset);
sender_pc = pauth_strip_verifiable((address) *(sender_sp-1), (address)saved_fp);
}


@@ -268,17 +268,18 @@ bool frame::safe_for_sender(JavaThread *thread) {
void frame::patch_pc(Thread* thread, address pc) {
assert(_cb == CodeCache::find_blob(pc), "unexpected pc");
address* pc_addr = &(((address*) sp())[-1]);
address signing_sp = (((address*) sp())[-2]);
address signed_pc = pauth_sign_return_address(pc, (address)signing_sp);
if (TracePcPatching) {
tty->print_cr("patch_pc at address " INTPTR_FORMAT " [" INTPTR_FORMAT " -> " INTPTR_FORMAT "]",
p2i(pc_addr), p2i(*pc_addr), p2i(pc));
p2i(pc_addr), p2i(*pc_addr), p2i(signed_pc));
a74nh marked this conversation as resolved.
Show resolved Hide resolved
}

// Only generated code frames should be patched, therefore the return address will not be signed.
assert(pauth_ptr_is_raw(*pc_addr), "cannot be signed");
// Either the return address is the original one or we are going to
// patch in the same address that's already there.
assert(_pc == *pc_addr || pc == *pc_addr, "must be");
*pc_addr = pc;
address pc_old = pauth_strip_verifiable(*pc_addr, (address)signing_sp);
assert(_pc == pc_old || pc == pc_old, "must be");
*pc_addr = signed_pc;
address original_pc = CompiledMethod::get_deopt_original_pc(this);
if (original_pc != NULL) {
assert(original_pc == _pc, "expected original PC to be stored before patching");
@@ -454,12 +455,12 @@ frame frame::sender_for_interpreter_frame(RegisterMap* map) const {
}
#endif // COMPILER2_OR_JVMCI

// Use the raw version of pc - the interpreter should not have signed it.
// For ROP protection, Interpreter will have signed the sender_pc, but there is no requirement to authenticate it here.
address sender_pc = pauth_strip_verifiable(sender_pc_maybe_signed(), (address)link());

return frame(sender_sp, unextended_sp, link(), sender_pc_maybe_signed());
return frame(sender_sp, unextended_sp, link(), sender_pc);
}


//------------------------------------------------------------------------------
// frame::sender_for_compiled_frame
frame frame::sender_for_compiled_frame(RegisterMap* map) const {
@@ -472,7 +473,9 @@ frame frame::sender_for_compiled_frame(RegisterMap* map) const {
intptr_t* unextended_sp = l_sender_sp;

// the return_address is always the word on the stack
address sender_pc = (address) *(l_sender_sp-1);

// For ROP protection, C1/C2 will have signed the sender_pc, but there is no requirement to authenticate it here.
address sender_pc = pauth_strip_verifiable((address) *(l_sender_sp-1), (address) *(l_sender_sp-2));

intptr_t** saved_fp_addr = (intptr_t**) (l_sender_sp - frame::sender_sp_offset);

@@ -520,6 +523,9 @@ frame frame::sender_raw(RegisterMap* map) const {
// Must be native-compiled frame, i.e. the marshaling code for native
// methods that exists in the core system.

// Native code may or may not have signed the return address, we have no way to be sure or what
// signing methods they used. Instead, just ensure the stripped value is used.

return frame(sender_sp(), link(), sender_pc());
}

@@ -118,7 +118,9 @@ define_pd_global(intx, InlineSmallCode, 1000);
product(uint, OnSpinWaitInstCount, 1, DIAGNOSTIC, \
"The number of OnSpinWaitInst instructions to generate." \
"It cannot be used with OnSpinWaitInst=none.") \
range(1, 99)
range(1, 99) \
product(ccstr, UseBranchProtection, "none", \
"Branch Protection to use: none,standard,pac-ret") \
a74nh marked this conversation as resolved.
Show resolved Hide resolved

// end of ARCH_FLAGS