tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

jitsrc.py (4809B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 # You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 # This is a gdb extension to automate the process of tracing backwards in rr
      6 # from a jit instruction to the code that generated that instruction.
      7 #
      8 # Usage:
      9 #  (rr) x/i $pc
     10 #  => 0x240e954ac13a:	pushq  (%rbx)
     11 #  (rr) jitsrc 0x240e954ac13a
     12 
     13 import re
     14 
     15 import gdb
     16 
     17 # (base_name, hops, func_name, source_var, dest_var) tuples, such that :
     18 #   - `base_name`: a regex matching the name of the function that implements
     19 #       the actual write
     20 #   - `hops`: the number of stack frames between `base_name` and `func_name`
     21 #   - `func_name`: a regex matching the name of the function that calls memcpy
     22 #   - `source_var`: an expression that can be evaluated in the frame
     23 #       corresponding to `func_name` to get the source of the memcpy
     24 #   - `dest_var`: an expression that can be evaluated in the frame
     25 #       corresponding to `func_name` to get the destination of the memcpy
     26 #
     27 # If an invocation of `jitsrc` stops in the middle of a memcpy, the solution
     28 # is normally to add a new pattern here.
     29 patterns = [
     30    (
     31        "__memmove",
     32        1,
     33        "js::jit::X86Encoding::BaseAssembler::executableCopy",
     34        "src",
     35        "dst",
     36    ),
     37    (
     38        "__memcpy",
     39        1,
     40        "js::jit::X86Encoding::BaseAssembler::executableCopy",
     41        "src",
     42        "dst",
     43    ),
     44    (
     45        "__memmove",
     46        1,
     47        "arena_t::RallocSmallOrLarge",
     48        "aPtr",
     49        "ret",
     50    ),
     51    ("__memcpy", 1, "arena_t::RallocSmallOrLarge", "aPtr", "ret"),
     52    (
     53        "mozilla::detail::VectorImpl<.*>::new_<.*>",
     54        3,
     55        "mozilla::Vector<.*>::convertToHeapStorage",
     56        "beginNoCheck()",
     57        "newBuf",
     58    ),
     59    (
     60        "__memmove",
     61        1,
     62        "js::jit::AssemblerBufferWithConstantPools",
     63        "&cur->instructions[0]",
     64        "dest",
     65    ),
     66    (
     67        "__memcpy",
     68        1,
     69        "js::jit::AssemblerBufferWithConstantPools",
     70        "&cur->instructions[0]",
     71        "dest",
     72    ),
     73    (
     74        "__memcpy",
     75        2,
     76        "js::jit::AssemblerX86Shared::executableCopy",
     77        "masm.m_formatter.m_buffer.m_buffer.mBegin",
     78        "buffer",
     79    ),
     80    ("__memcpy", 1, "arena_t::RallocSmallOrLarge", "aPtr", "ret"),
     81    ("js::jit::X86Encoding::SetInt32", 0, "js::jit::X86Encoding::SetInt32", "0", "0"),
     82    (
     83        "js::jit::X86Encoding::SetPointer",
     84        0,
     85        "js::jit::X86Encoding::SetPointer",
     86        "0",
     87        "0",
     88    ),
     89    (
     90        "<unnamed>",
     91        1,
     92        "js::jit::AssemblerBufferWithConstantPools<.*>::executableCopy",
     93        "&cur->instructions[0]",
     94        "dest",
     95    ),
     96    ("std::__copy_move", 4, "CopySpan", "source.data()", "target.data()"),
     97    (
     98        "__memmove_(avx|evex)_unaligned_erms",
     99        1,
    100        "mozilla::detail::EndianUtils::copyAndSwapTo<.*0,.*0",
    101        "aSrc",
    102        "(size_t) aDest",
    103    ),
    104 ]
    105 
    106 
    107 class JitSource(gdb.Command):
    108    def __init__(self):
    109        super().__init__("jitsrc", gdb.COMMAND_RUNNING)
    110        self.dont_repeat()
    111 
    112    def disable_breakpoints(self):
    113        self.disabled_breakpoints = [b for b in gdb.breakpoints() if b.enabled]
    114        for b in self.disabled_breakpoints:
    115            b.enabled = False
    116 
    117    def enable_breakpoints(self):
    118        for b in self.disabled_breakpoints:
    119            b.enabled = True
    120 
    121    def search_stack(self, base_name, hops, name, src, dst, address):
    122        current_frame_name = gdb.newest_frame().name() or "<unnamed>"
    123        if not re.match(base_name, current_frame_name):
    124            return None
    125        f = gdb.newest_frame()
    126        for _ in range(hops):
    127            f = f.older()
    128        if not re.match(name, f.name() or "<unnamed>"):
    129            return None
    130        f.select()
    131        src_val = gdb.parse_and_eval(src)
    132        dst_val = gdb.parse_and_eval(dst)
    133        return hex(src_val + int(address, 16) - dst_val)
    134 
    135    def next_address(self, old):
    136        for pattern in patterns:
    137            found = self.search_stack(*pattern, old)
    138            if found:
    139                return found
    140        return None
    141 
    142    def runback(self, address):
    143        b = gdb.Breakpoint(
    144            "*" + address, type=gdb.BP_WATCHPOINT, wp_class=gdb.WP_WRITE, internal=True
    145        )
    146        try:
    147            while b.hit_count == 0:
    148                gdb.execute("rc", to_string=True)
    149        finally:
    150            b.delete()
    151 
    152    def invoke(self, arg, from_tty):
    153        args = gdb.string_to_argv(arg)
    154        address = args[0]
    155        self.disable_breakpoints()
    156        while address:
    157            self.runback(address)
    158            address = self.next_address(address)
    159        self.enable_breakpoints()
    160 
    161 
    162 # Register to the gdb runtime
    163 JitSource()