diff --git a/libc/Android.mk b/libc/Android.mk index a8c66fa99..175fbb4c0 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -250,10 +250,11 @@ libc_bionic_src_files += bionic/fork.cpp # dereferences. libc_bionic_src_files += bionic/getauxval.cpp -# These three require getauxval, which isn't available on older platforms. +# These four require getauxval, which isn't available on older platforms. libc_bionic_src_files += bionic/getentropy_linux.c libc_bionic_src_files += bionic/sysconf.cpp libc_bionic_src_files += bionic/vdso.cpp +libc_bionic_src_files += bionic/setjmp_cookie.cpp libc_cxa_src_files := \ bionic/__cxa_guard.cpp \ @@ -347,7 +348,7 @@ libc_upstream_openbsd_gdtoa_src_files_64 := \ $(libc_upstream_openbsd_gdtoa_src_files) \ upstream-openbsd/lib/libc/gdtoa/strtorQ.c \ -# These two depend on getentropy_linux.cpp, which isn't in libc_ndk.a. +# These two depend on getentropy_linux.c, which isn't in libc_ndk.a. libc_upstream_openbsd_src_files := \ upstream-openbsd/lib/libc/crypt/arc4random.c \ upstream-openbsd/lib/libc/crypt/arc4random_uniform.c \ diff --git a/libc/arch-arm/bionic/setjmp.S b/libc/arch-arm/bionic/setjmp.S index 8220c0825..a119529ee 100644 --- a/libc/arch-arm/bionic/setjmp.S +++ b/libc/arch-arm/bionic/setjmp.S @@ -51,13 +51,13 @@ // The internal structure of a jmp_buf is totally private. // Current layout (may change in the future): // -// word name description -// 0 magic magic number -// 1 sigmask signal mask (not used with _setjmp / _longjmp) -// 2 float_base base of float registers (d8 to d15) -// 18 float_state floating-point status and control register -// 19 core_base base of core registers (r4 to r14) -// 30 reserved reserved entries (room to grow) +// word name description +// 0 sigflag/cookie setjmp cookie in top 31 bits, signal mask flag in low bit +// 1 sigmask signal mask (not used with _setjmp / _longjmp) +// 2 float_base base of float registers (d8 to d15) +// 18 float_state floating-point status and control register +// 19 core_base base of core registers (r4 to r14) +// 30 reserved reserved entries (room to grow) // 64 // // NOTE: float_base must be at an even word index, since the @@ -80,33 +80,79 @@ ENTRY(_setjmp) b sigsetjmp END(_setjmp) +#define MANGLE_REGISTERS 1 +.macro m_mangle_registers reg +#if MANGLE_REGISTERS + eor r4, r4, \reg + eor r5, r5, \reg + eor r6, r6, \reg + eor r7, r7, \reg + eor r8, r8, \reg + eor r9, r9, \reg + eor r10, r10, \reg + eor r11, r11, \reg + eor r12, r12, \reg + eor r13, r13, \reg + eor r14, r14, \reg +#endif +.endm + +.macro m_unmangle_registers reg + m_mangle_registers \reg +.endm + // int sigsetjmp(sigjmp_buf env, int save_signal_mask); ENTRY(sigsetjmp) - // Record whether or not we're saving the signal mask. + stmfd sp!, {r0, lr} + .cfi_def_cfa_offset 8 + .cfi_rel_offset r0, 0 + .cfi_rel_offset lr, 4 + + mov r0, r1 + bl __bionic_setjmp_cookie_get + mov r1, r0 + + ldmfd sp, {r0} + + // Save the setjmp cookie for later. + bic r2, r1, #1 + stmfd sp!, {r2} + .cfi_adjust_cfa_offset 4 + + // Record the setjmp cookie and whether or not we're saving the signal mask. str r1, [r0, #(_JB_SIGFLAG * 4)] // Do we need to save the signal mask? - teq r1, #0 + tst r1, #1 beq 1f - // Get current signal mask. - stmfd sp!, {r0, r14} - .cfi_def_cfa_offset 8 - .cfi_rel_offset r0, 0 - .cfi_rel_offset r14, 4 - mov r0, #0 - bl sigblock - mov r1, r0 - ldmfd sp!, {r0, r14} - .cfi_def_cfa_offset 0 + // Align the stack. + sub sp, #4 + .cfi_adjust_cfa_offset 4 - // Save the signal mask. - str r1, [r0, #(_JB_SIGMASK * 4)] + // Save the current signal mask. + add r2, r0, #(_JB_SIGMASK * 4) + mov r0, #2 // SIG_SETMASK + mov r1, #0 + bl sigprocmask + + // Unalign the stack. + add sp, #4 + .cfi_adjust_cfa_offset -4 1: + ldmfd sp!, {r2} + .cfi_adjust_cfa_offset -4 + ldmfd sp!, {r0, lr} + .cfi_adjust_cfa_offset -8 + .cfi_restore r0 + .cfi_restore lr + // Save core registers. add r1, r0, #(_JB_CORE_BASE * 4) + m_mangle_registers r2 stmia r1, {r4-r14} + m_unmangle_registers r2 // Save floating-point registers. add r1, r0, #(_JB_FLOAT_BASE * 4) @@ -122,29 +168,30 @@ END(sigsetjmp) // void siglongjmp(sigjmp_buf env, int value); ENTRY(siglongjmp) - // Do we need to restore the signal mask? - ldr r2, [r0, #(_JB_SIGFLAG * 4)] - teq r2, #0 - beq 1f - - // Restore the signal mask. - stmfd sp!, {r0, r1, r14} + stmfd sp!, {r0, r1, lr} .cfi_def_cfa_offset 12 .cfi_rel_offset r0, 0 .cfi_rel_offset r1, 4 - .cfi_rel_offset r14, 8 - sub sp, sp, #4 // Align the stack. - .cfi_adjust_cfa_offset 4 + .cfi_rel_offset lr, 8 + // Fetch the signal flag. + ldr r1, [r0, #(_JB_SIGFLAG * 4)] + + // Do we need to restore the signal mask? + ands r1, r1, #1 + beq 1f + + // Restore the signal mask. ldr r0, [r0, #(_JB_SIGMASK * 4)] bl sigsetmask - add sp, sp, #4 // Unalign the stack. - .cfi_adjust_cfa_offset -4 - ldmfd sp!, {r0, r1, r14} - .cfi_def_cfa_offset 0 - 1: + ldmfd sp!, {r0, r1, lr} + .cfi_adjust_cfa_offset -12 + .cfi_restore r0 + .cfi_restore r1 + .cfi_restore lr + // Restore floating-point registers. add r2, r0, #(_JB_FLOAT_BASE * 4) vldmia r2, {d8-d15} @@ -154,16 +201,24 @@ ENTRY(siglongjmp) fmxr fpscr, r2 // Restore core registers. + ldr r3, [r0, #(_JB_SIGFLAG * 4)] + bic r3, r3, #1 add r2, r0, #(_JB_CORE_BASE * 4) ldmia r2, {r4-r14} + m_unmangle_registers r3 - // Validate sp and r14. - teq sp, #0 - teqne r14, #0 - bleq longjmperror + // Save the return value/address and check the setjmp cookie. + stmfd sp!, {r1, lr} + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset lr, 4 + mov r0, r3 + bl __bionic_setjmp_cookie_check + + // Restore return value/address. + ldmfd sp!, {r0, lr} + .cfi_adjust_cfa_offset -8 + .cfi_restore lr - // Set return value. - mov r0, r1 teq r0, #0 moveq r0, #1 bx lr diff --git a/libc/arch-arm64/bionic/setjmp.S b/libc/arch-arm64/bionic/setjmp.S index ba0a226e0..c06a67157 100644 --- a/libc/arch-arm64/bionic/setjmp.S +++ b/libc/arch-arm64/bionic/setjmp.S @@ -52,6 +52,29 @@ #define _JB_D10_D11 (_JB_D12_D13 + 2) #define _JB_D8_D9 (_JB_D10_D11 + 2) +#define MANGLE_REGISTERS 1 +.macro m_mangle_registers reg, sp_reg +#if MANGLE_REGISTERS + eor x19, x19, \reg + eor x20, x20, \reg + eor x21, x21, \reg + eor x22, x22, \reg + eor x23, x23, \reg + eor x24, x24, \reg + eor x25, x25, \reg + eor x26, x26, \reg + eor x27, x27, \reg + eor x28, x28, \reg + eor x29, x29, \reg + eor x30, x30, \reg + eor \sp_reg, \sp_reg, \reg +#endif +.endm + +.macro m_unmangle_registers reg, sp_reg + m_mangle_registers \reg, sp_reg=\sp_reg +.endm + ENTRY(setjmp) mov w1, #1 b sigsetjmp @@ -64,23 +87,47 @@ END(_setjmp) // int sigsetjmp(sigjmp_buf env, int save_signal_mask); ENTRY(sigsetjmp) - // Record whether or not we're saving the signal mask. - str w1, [x0, #(_JB_SIGFLAG * 8)] + stp x0, x30, [sp, #-16]! + .cfi_def_cfa_offset 16 + .cfi_rel_offset x0, 0 + .cfi_rel_offset x30, 8 + + // Get the cookie and store it along with the signal flag. + mov x0, x1 + bl __bionic_setjmp_cookie_get + mov x1, x0 + ldr x0, [sp, #0] + str x1, [x0, #(_JB_SIGFLAG * 8)] // Do we need to save the signal mask? - cbz w1, 1f + tbz w1, #0, 1f + + // Save the cookie for later. + stp x1, xzr, [sp, #-16]! + .cfi_adjust_cfa_offset 16 // Save current signal mask. - stp x0, x30, [sp, #-16]! // The 'how' argument is ignored if new_mask is NULL. mov x1, #0 // NULL. add x2, x0, #(_JB_SIGMASK * 8) // old_mask. bl sigprocmask - ldp x0, x30, [sp], #16 + + ldp x1, xzr, [sp], #16 + .cfi_adjust_cfa_offset -16 1: + // Restore original x0 and lr. + ldp x0, x30, [sp], #16 + .cfi_adjust_cfa_offset -16 + .cfi_restore x0 + .cfi_restore x30 + + // Mask off the signal flag bit. + bic x1, x1, #1 + // Save core registers. mov x10, sp + m_mangle_registers x1, sp_reg=x10 stp x30, x10, [x0, #(_JB_X30_SP * 8)] stp x28, x29, [x0, #(_JB_X28_X29 * 8)] stp x26, x27, [x0, #(_JB_X26_X27 * 8)] @@ -88,6 +135,7 @@ ENTRY(sigsetjmp) stp x22, x23, [x0, #(_JB_X22_X23 * 8)] stp x20, x21, [x0, #(_JB_X20_X21 * 8)] str x19, [x0, #(_JB_X19 * 8)] + m_unmangle_registers x1, sp_reg=x10 // Save floating point registers. stp d14, d15, [x0, #(_JB_D14_D15 * 8)] @@ -102,30 +150,60 @@ END(sigsetjmp) // void siglongjmp(sigjmp_buf env, int value); ENTRY(siglongjmp) // Do we need to restore the signal mask? - ldr w9, [x0, #(_JB_SIGFLAG * 8)] - cbz w9, 1f + ldr x2, [x0, #(_JB_SIGFLAG * 8)] + tbz w2, #0, 1f + + stp x0, x30, [sp, #-16]! + .cfi_adjust_cfa_offset 16 + .cfi_rel_offset x0, 0 + .cfi_rel_offset x30, 8 // Restore signal mask. - stp x0, x30, [sp, #-16]! mov x19, x1 // Save 'value'. + mov x2, x0 mov x0, #2 // SIG_SETMASK add x1, x2, #(_JB_SIGMASK * 8) // new_mask. mov x2, #0 // NULL. bl sigprocmask mov x1, x19 // Restore 'value'. - ldp x0, x30, [sp], #16 + // Restore original x0 and lr. + ldp x0, x30, [sp], #16 + .cfi_adjust_cfa_offset -16 + .cfi_restore x0 + .cfi_restore x30 + + ldr x2, [x0, #(_JB_SIGFLAG * 8)] 1: // Restore core registers. + bic x2, x2, #1 ldp x30, x10, [x0, #(_JB_X30_SP * 8)] - mov sp, x10 ldp x28, x29, [x0, #(_JB_X28_X29 * 8)] ldp x26, x27, [x0, #(_JB_X26_X27 * 8)] ldp x24, x25, [x0, #(_JB_X24_X25 * 8)] ldp x22, x23, [x0, #(_JB_X22_X23 * 8)] ldp x20, x21, [x0, #(_JB_X20_X21 * 8)] ldr x19, [x0, #(_JB_X19 * 8)] + m_unmangle_registers x2, sp_reg=x10 + mov sp, x10 + + stp x0, x1, [sp, #-16]! + .cfi_adjust_cfa_offset 16 + .cfi_rel_offset x0, 0 + .cfi_rel_offset x1, 8 + stp x30, xzr, [sp, #-16]! + .cfi_adjust_cfa_offset 16 + .cfi_rel_offset x30, 0 + ldr x0, [x0, #(_JB_SIGFLAG * 8)] + bl __bionic_setjmp_cookie_check + ldp x30, xzr, [sp], #16 + .cfi_adjust_cfa_offset -16 + .cfi_restore x30 + ldp x0, x1, [sp], #16 + .cfi_adjust_cfa_offset -16 + .cfi_restore x0 + .cfi_restore x1 // Restore floating point registers. ldp d14, d15, [x0, #(_JB_D14_D15 * 8)] @@ -133,13 +211,6 @@ ENTRY(siglongjmp) ldp d10, d11, [x0, #(_JB_D10_D11 * 8)] ldp d8, d9, [x0, #(_JB_D8_D9 * 8)] - // Validate sp (sp mod 16 = 0) and lr (lr mod 4 = 0). - tst x30, #3 - b.ne longjmperror - mov x10, sp - tst x10, #15 - b.ne longjmperror - // Set return value. cmp w1, wzr csinc w0, w1, wzr, ne diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp index 3ca6c0d5e..f59fa67ea 100644 --- a/libc/bionic/libc_init_common.cpp +++ b/libc/bionic/libc_init_common.cpp @@ -49,6 +49,7 @@ #include "pthread_internal.h" extern "C" abort_msg_t** __abort_message_ptr; +extern "C" void __bionic_setjmp_cookie_init(void); extern "C" int __system_properties_init(void); extern "C" int __set_tls(void* ptr); extern "C" int __set_tid_address(int* tid_address); @@ -121,6 +122,7 @@ void __libc_init_common(KernelArgumentBlock& args) { __system_properties_init(); // Requires 'environ'. + __bionic_setjmp_cookie_init(); __libc_init_vdso(); } diff --git a/libc/bionic/setjmp_cookie.cpp b/libc/bionic/setjmp_cookie.cpp new file mode 100644 index 000000000..cf79e8361 --- /dev/null +++ b/libc/bionic/setjmp_cookie.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE + * COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "private/libc_logging.h" + +extern "C" __LIBC_HIDDEN__ int getentropy(void*, size_t); +static long __bionic_setjmp_cookie; + +extern "C" void __bionic_setjmp_cookie_init() { + char* random_data = reinterpret_cast(getauxval(AT_RANDOM)); + long value = *reinterpret_cast(random_data + 8); + + // Mask off the last bit to store the signal flag. + __bionic_setjmp_cookie = value & ~1; +} + +extern "C" long __bionic_setjmp_cookie_get(long sigflag) { + if (sigflag & ~1) { + __libc_fatal("unexpected sigflag value: %ld", sigflag); + } + + return __bionic_setjmp_cookie | sigflag; +} + +// Aborts if cookie doesn't match, returns the signal flag otherwise. +extern "C" long __bionic_setjmp_cookie_check(long cookie) { + if (__bionic_setjmp_cookie != (cookie & ~1)) { + __libc_fatal("setjmp cookie mismatch"); + } + + return cookie & 1; +} diff --git a/tests/setjmp_test.cpp b/tests/setjmp_test.cpp index a3b5885c3..944dac8fb 100644 --- a/tests/setjmp_test.cpp +++ b/tests/setjmp_test.cpp @@ -212,3 +212,30 @@ TEST(setjmp, setjmp_fp_registers) { CHECK_FREGS; } } + +#if defined(__arm__) +#define __JB_SIGFLAG 0 +#elif defined(__aarch64__) +#define __JB_SIGFLAG 0 +#elif defined(__i386__) +#define __JB_SIGFLAG 7 +#elif defined(__x86_64) +#define __JB_SIGFLAG 8 +#endif + +TEST(setjmp, setjmp_cookie) { +#if !defined(__mips__) + jmp_buf jb; + int value = setjmp(jb); + ASSERT_EQ(0, value); + + long* sigflag = reinterpret_cast(jb) + __JB_SIGFLAG; + + // Make sure there's actually a cookie. + EXPECT_NE(0, *sigflag & ~1); + + // Wipe it out + *sigflag &= 1; + EXPECT_DEATH(longjmp(jb, 0), ""); +#endif +}