Ignore:
Timestamp:
Jul 25, 2017, 1:56:04 PM (8 years ago)
Author:
[email protected]
Message:

Fix bugs in probe code to change sp on x86, x86_64 and 32-bit ARM.
https://bugs.webkit.org/show_bug.cgi?id=174809
<rdar://problem/33504759>

Reviewed by Filip Pizlo.

  1. When the probe handler function changes the sp register to point to the region of stack in the middle of the ProbeContext on the stack, there is a bug where the ProbeContext's register values to be restored can be over-written before they can be restored. This is now fixed.
  1. Added more robust probe tests for changing the sp register.
  1. Made existing probe tests to ensure that probe handlers were actually called.
  1. Added some verification to testProbePreservesGPRS().
  1. Change all the probe tests to fail early on discovering an error instead of batching till the end of the test. This helps point a finger to the failing issue earlier.

This patch was tested on x86, x86_64, and ARMv7. ARM64 probe code will be fixed
next in https://bugs.webkit.org/show_bug.cgi?id=174697.

  • assembler/MacroAssemblerARM.cpp:
  • assembler/MacroAssemblerARMv7.cpp:
  • assembler/MacroAssemblerX86Common.cpp:
  • assembler/testmasm.cpp:

(JSC::testProbeReadsArgumentRegisters):
(JSC::testProbeWritesArgumentRegisters):
(JSC::testProbePreservesGPRS):
(JSC::testProbeModifiesStackPointer):
(JSC::testProbeModifiesStackPointerToInsideProbeContextOnStack):
(JSC::testProbeModifiesStackPointerToNBytesBelowSP):
(JSC::testProbeModifiesProgramCounter):
(JSC::run):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.cpp

    r219740 r219885  
    187187    // MacroAssemblerARMv7::probe() has already generated code to store some values.
    188188    // The top of stack now looks like this:
    189     //     esp[0 * ptrSize]: probeFunction
    190     //     esp[1 * ptrSize]: arg
     189    //     esp[0 * ptrSize]: probe handler function
     190    //     esp[1 * ptrSize]: probe arg
    191191    //     esp[2 * ptrSize]: saved r0
    192192    //     esp[3 * ptrSize]: saved ip
     
    255255    // 1. Normal ARM calling convention relies on moving lr to pc to return to
    256256    //    the caller. In our case, the address to return to is specified by
    257     //    ProbeContext.cpu.pc. And at that moment, we won't have any available
     257    //    ProbeContext.cpu.gprs[pc]. And at that moment, we won't have any available
    258258    //    scratch registers to hold the return address (lr needs to hold
    259     //    ProbeContext.cpu.lr, not the return address).
     259    //    ProbeContext.cpu.gprs[lr], not the return address).
    260260    //
    261261    //    The solution is to store the return address on the stack and load the
     
    263263    //
    264264    // 2. Issue 1 means we will need to write to the stack location at
    265     //    ProbeContext.cpu.sp - 4. But if the user probe function had modified
    266     //    the value of ProbeContext.cpu.sp to point in the range between
    267     //    &ProbeContext.cpu.ip thru &ProbeContext.cpu.aspr, then the action for
    268     //    Issue 1 may trash the values to be restored before we can restore
    269     //    them.
     265    //    ProbeContext.cpu.gprs[sp] - PTR_SIZE. But if the user probe function had
     266    //    modified the value of ProbeContext.cpu.gprs[sp] to point in the range between
     267    //    &ProbeContext.cpu.gprs[ip] thru &ProbeContext.cpu.sprs[aspr], then the action
     268    //    for Issue 1 may trash the values to be restored before we can restore them.
    270269    //
    271     //    The solution is to check if ProbeContext.cpu.sp contains a value in
     270    //    The solution is to check if ProbeContext.cpu.gprs[sp] contains a value in
    272271    //    the undesirable range. If so, we copy the remaining ProbeContext
    273     //    register data to a safe range (at memory lower than where
    274     //    ProbeContext.cpu.sp points) first, and restore the remaining register
    275     //    from this new range.
    276 
    277     "add       ip, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "\n"
     272    //    register data to a safe area first, and restore the remaining register
     273    //    from this new safe area.
     274
     275    // The restore area for the pc will be located at 1 word below the resultant sp.
     276    // All restore values are located at offset <= PROBE_CPU_APSR_OFFSET. Hence,
     277    // we need to make sure that resultant sp > offset of apsr + 1.
     278    "add       ip, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET + PTR_SIZE) "\n"
    278279    "ldr       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
    279280    "cmp       lr, ip" "\n"
     
    281282    "bgt     " SYMBOL_STRING(ctiMasmProbeTrampolineEnd) "\n"
    282283
    283     // We get here because the new expected stack pointer location is lower
    284     // than where it's supposed to be. This means the safe range of stack
    285     // memory where we'll be copying the remaining register restore values to
    286     // might be in a region of memory below the sp i.e. unallocated stack
    287     // memory. This, in turn, makes it vulnerable to interrupts potentially
    288     // trashing the copied values. To prevent that, we must first allocate the
    289     // needed stack memory by adjusting the sp before the copying.
    290 
    291     "sub       lr, lr, #(6 * " STRINGIZE_VALUE_OF(PTR_SIZE)
    292     " + " STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) ")" "\n"
    293 
    294     "mov       ip, sp" "\n"
    295     "mov       sp, lr" "\n"
    296     "mov       lr, ip" "\n"
    297 
     284    // Getting here means that the restore area will overlap the ProbeContext data
     285    // that we will need to get the restoration values from. So, let's move that
     286    // data to a safe place before we start writing into the restore area.
     287    // Let's locate the "safe area" at 2x sizeof(ProbeContext) below where the
     288    // restore area. This ensures that:
     289    // 1. The safe area does not overlap the restore area.
     290    // 2. The safe area does not overlap the ProbeContext.
     291    //    This makes it so that we can use memcpy (does not require memmove) semantics
     292    //    to copy the restore values to the safe area.
     293
     294    // lr already contains [sp, #STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET)].
     295    "sub       lr, lr, #(2 * " STRINGIZE_VALUE_OF(PROBE_ALIGNED_SIZE) ")" "\n"
     296   
     297    "mov       ip, sp" "\n" // Save the original ProbeContext*.
     298   
     299    // Make sure the stack pointer points to the safe area. This ensures that the
     300    // safe area is protected from interrupt handlers overwriting it.
     301    "mov       sp, lr" "\n" // sp now points to the new ProbeContext in the safe area.
     302   
     303    "mov       lr, ip" "\n" // Use lr as the old ProbeContext*.
     304   
     305    // Copy the restore data to the new ProbeContext*.
    298306    "ldr       ip, [lr, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n"
    299307    "str       ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n"
     
    307315    "str       ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "]" "\n"
    308316
     317    // ctiMasmProbeTrampolineEnd expects lr to contain the sp value to be restored.
     318    // Since we used it as scratch above, let's restore it.
     319    "ldr       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
     320
    309321    ".thumb_func " THUMB_FUNC_PARAM(ctiMasmProbeTrampolineEnd) "\n"
    310322    SYMBOL_STRING(ctiMasmProbeTrampolineEnd) ":" "\n"
     323
     324    // Set up the restore area for sp and pc.
     325    // lr already contains [sp, #STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET)].
     326
     327    // Push the pc on to the restore area.
    311328    "ldr       ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n"
    312     "ldr       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
    313329    "sub       lr, lr, #" STRINGIZE_VALUE_OF(PTR_SIZE) "\n"
    314330    "str       ip, [lr]" "\n"
     331    // Point sp to the restore area.
    315332    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
    316333
     334    // All done with math i.e. won't trash the status register (apsr) and don't need
     335    // scratch registers (lr and ip) anymore. Restore apsr, lr, and ip.
    317336    "ldr       ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "]" "\n"
    318337    "msr       APSR, ip" "\n"
    319     "ldr       ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n"
    320     "mov       lr, ip" "\n"
     338    "ldr       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n"
    321339    "ldr       ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n"
     340
     341    // Restore the sp and pc.
    322342    "ldr       sp, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
    323 
    324343    "pop       { pc }" "\n"
    325344);
Note: See TracChangeset for help on using the changeset viewer.