]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/dev/arm64/fbt_arm.c
xnu-4903.221.2.tar.gz
[apple/xnu.git] / bsd / dev / arm64 / fbt_arm.c
index c2f348f9ac33ef2e8d15e0fcbfb7511ecef92f3e..3364a066e478fde9de9e80fa93a0c6ab5dda93fd 100644 (file)
 
 #include <sys/dtrace_glue.h>
 
+#if __has_include(<ptrauth.h>)
+#include <ptrauth.h>
+#endif
+
 #define DTRACE_INVOP_PUSH_FRAME 11
 
 #define DTRACE_INVOP_NOP_SKIP          4
@@ -90,7 +94,7 @@
        (((x) & 0xffc07fff) == 0xa9407bfd || ((x) & 0xffc07fff) == 0xa8c07bfd)
 
 #define FBT_IS_ARM64_ADD_FP_SP(x)      (((x) & 0xffc003ff) == 0x910003fd)      /* add fp, sp, #val  (add fp, sp, #0 == mov fp, sp) */
-#define FBT_IS_ARM64_RET(x)            ((x) == 0xd65f03c0)                     /* ret */
+#define FBT_IS_ARM64_RET(x)            (((x) == 0xd65f03c0) || ((x) == 0xd65f0fff))                    /* ret, retab */
 
 
 #define FBT_B_MASK                     0xff000000
@@ -128,19 +132,19 @@ fbt_invop(uintptr_t addr, uintptr_t * stack, uintptr_t rval)
                                if (fbt->fbtp_roffset == 0) {
                                        /*
                                         * Stack looks like this:
-                                        *      
+                                        *
                                         *      [Higher addresses]
-                                        *      
+                                        *
                                         *      Frame of caller
                                         *      Extra args for callee
-                                        *      ------------------------ 
+                                        *      ------------------------
                                         *      Frame from traced function: <previous sp (e.g. 0x1000), return address>
                                         *      ------------------------
                                         *      arm_context_t
                                         *      ------------------------
                                         *      Frame from trap handler:  <previous sp (e.g. 0x1000) , traced PC >
                                         *                              The traced function never got to mov fp, sp,
-                                        *                              so there is no frame in the backtrace pointing 
+                                        *                              so there is no frame in the backtrace pointing
                                         *                              to the frame on the stack containing the LR in the
                                         *                              caller.
                                         *      ------------------------
@@ -155,29 +159,29 @@ fbt_invop(uintptr_t addr, uintptr_t * stack, uintptr_t rval)
 
                                        arm_saved_state_t *regs = (arm_saved_state_t *)(&((arm_context_t *)stack)->ss);
 
-                                       /* 
-                                        * cpu_dtrace_caller compensates for fact that the traced function never got to update its fp. 
-                                        * When walking the stack, when we reach the frame where we extract a PC in the patched 
+                                       /*
+                                        * cpu_dtrace_caller compensates for fact that the traced function never got to update its fp.
+                                        * When walking the stack, when we reach the frame where we extract a PC in the patched
                                         * function, we put the cpu_dtrace_caller in the backtrace instead.  The next frame we extract
-                                        * will be in the caller's caller, so we output a backtrace starting at the caller and going 
+                                        * will be in the caller's caller, so we output a backtrace starting at the caller and going
                                         * sequentially up the stack.
                                         */
-                                       CPU->cpu_dtrace_caller = get_saved_state_lr(regs); 
+                                       CPU->cpu_dtrace_caller = get_saved_state_lr(regs);
                                        dtrace_probe(fbt->fbtp_id, get_saved_state_reg(regs, 0), get_saved_state_reg(regs, 1),
                                            get_saved_state_reg(regs, 2), get_saved_state_reg(regs, 3),get_saved_state_reg(regs, 4));
                                        CPU->cpu_dtrace_caller = 0;
                                } else {
                                        /*
                                         * When fbtp_roffset is non-zero, we know we are handling a return probe point.
-                                        * 
+                                        *
                                         *
                                         * Stack looks like this, as we've already popped the frame in the traced callee, and
                                         * we trap with lr set to the return address in the caller.
                                         *      [Higher addresses]
-                                        *      
+                                        *
                                         *      Frame of caller
                                         *      Extra args for callee
-                                        *      ------------------------ 
+                                        *      ------------------------
                                         *      arm_context_t
                                         *      ------------------------
                                         *      Frame from trap handler:  <sp at time of trap, traced PC >
@@ -198,7 +202,7 @@ fbt_invop(uintptr_t addr, uintptr_t * stack, uintptr_t rval)
                                }
                                CPU->cpu_dtrace_invop_underway = 0;
                        }
-               
+
                        /*
                                On other architectures, we return a DTRACE constant to let the callback function
                                know what was replaced. On the ARM, since the function prologue/epilogue machine code
@@ -280,8 +284,11 @@ fbt_perfCallback(
                        retval = KERN_SUCCESS;
                } else if (FBT_IS_ARM64_RET(emul)) {
                        lr = get_saved_state_lr(regs);
+#if __has_feature(ptrauth_calls)
+                       lr = (user_addr_t) ptrauth_strip((void *)lr, ptrauth_key_return_address);
+#endif
                        set_saved_state_pc(regs, lr);
-                       retval = KERN_SUCCESS;                  
+                       retval = KERN_SUCCESS;
                } else if (FBT_IS_ARM64_B_INSTR(emul)) {
                        pc = get_saved_state_pc(regs);
                        imm = FBT_GET_ARM64_B_IMM(emul);
@@ -301,20 +308,19 @@ fbt_perfCallback(
 }
 
 void
-fbt_provide_probe(struct modctl *ctl, uintptr_t instrLow, uintptr_t instrHigh, char *modname, char* symbolName, machine_inst_t* symbolStart)
+fbt_provide_probe(struct modctl *ctl, const char *modname, const char* symbolName, machine_inst_t* symbolStart, machine_inst_t *instrHigh)
 {
-       unsigned int    j;
         int            doenable = 0;
        dtrace_id_t     thisid;
 
        fbt_probe_t     *newfbt, *retfbt, *entryfbt;
        machine_inst_t *instr, *pushinstr = NULL, *limit, theInstr;
        int             foundPushLR, savedRegs;
-       
+
        /*
-        * Guard against null symbols
+        * Guard against null and invalid symbols
         */
-       if (!symbolStart || !instrLow || !instrHigh) {
+       if (!symbolStart || !instrHigh || instrHigh < symbolStart) {
                kprintf("dtrace: %s has an invalid address\n", symbolName);
                return;
        }
@@ -322,15 +328,13 @@ fbt_provide_probe(struct modctl *ctl, uintptr_t instrLow, uintptr_t instrHigh, c
        /*
         * Assume the compiler doesn't schedule instructions in the prologue.
         */
-
        foundPushLR = 0;
        savedRegs = -1;
        limit = (machine_inst_t *)instrHigh;
 
        assert(sizeof(*instr) == 4);
 
-       for (j = 0, instr = symbolStart, theInstr = 0;
-            (j < 8) && ((uintptr_t)instr >= instrLow) && (instrHigh > (uintptr_t)(instr)); j++, instr++)
+       for (instr = symbolStart, theInstr = 0; instr < instrHigh; instr++)
        {
                /*
                 * Count the number of time we pushed something onto the stack
@@ -361,7 +365,7 @@ fbt_provide_probe(struct modctl *ctl, uintptr_t instrLow, uintptr_t instrHigh, c
        newfbt = kmem_zalloc(sizeof(fbt_probe_t), KM_SLEEP);
        newfbt->fbtp_next = NULL;
        strlcpy( (char *)&(newfbt->fbtp_name), symbolName, MAX_FBTP_NAME_CHARS );
-               
+
        if (thisid != 0) {
                /*
                 * The dtrace_probe previously existed, so we have to hook
@@ -417,7 +421,7 @@ fbt_provide_probe(struct modctl *ctl, uintptr_t instrLow, uintptr_t instrHigh, c
        doenable=0;
 
        thisid = dtrace_probe_lookup(fbt_id, modname, symbolName, FBT_RETURN);
-               
+
        if (thisid != 0) {
                /* The dtrace_probe previously existed, so we have to
                 * find the end of the existing fbt chain.  If we find
@@ -455,7 +459,7 @@ again:
         * OK, it's an instruction.
         */
        theInstr = *instr;
-               
+
        /* Walked onto the start of the next routine? If so, bail out from this function */
        if (FBT_IS_ARM64_FRAME_PUSH(theInstr)) {
                if (!retfbt)
@@ -498,7 +502,7 @@ again:
                return;
 
        newfbt = kmem_zalloc(sizeof(fbt_probe_t), KM_SLEEP);
-       newfbt->fbtp_next = NULL;       
+       newfbt->fbtp_next = NULL;
        strlcpy( (char *)&(newfbt->fbtp_name), symbolName, MAX_FBTP_NAME_CHARS );
 
        if (retfbt == NULL) {
@@ -529,80 +533,3 @@ again:
        instr++;
        goto again;
 }
-
-void
-fbt_provide_module_kernel_syms(struct modctl *ctl)
-{
-       kernel_mach_header_t            *mh;
-       struct load_command             *cmd;
-       kernel_segment_command_t        *orig_ts = NULL, *orig_le = NULL;
-       struct symtab_command           *orig_st = NULL;
-       kernel_nlist_t                  *sym = NULL;
-       char                            *strings;
-       uintptr_t                       instrLow, instrHigh;
-       char                            *modname;
-       unsigned int                    i;
-
-       mh = (kernel_mach_header_t *)(ctl->mod_address);
-       modname = ctl->mod_modname;
-       
-       /*
-        * Employees of dtrace and their families are ineligible.  Void
-        * where prohibited.
-        */
-
-       if (mh->magic != MH_MAGIC_KERNEL)
-               return;
-
-       cmd = (struct load_command *) & mh[1];
-       for (i = 0; i < mh->ncmds; i++) {
-               if (cmd->cmd == LC_SEGMENT_KERNEL) {
-                       kernel_segment_command_t *orig_sg = (kernel_segment_command_t *) cmd;
-
-                       if (LIT_STRNEQL(orig_sg->segname, SEG_TEXT))
-                               orig_ts = orig_sg;
-                       else if (LIT_STRNEQL(orig_sg->segname, SEG_LINKEDIT))
-                               orig_le = orig_sg;
-                       else if (LIT_STRNEQL(orig_sg->segname, ""))
-                               orig_ts = orig_sg;      /* kexts have a single
-                                                        * unnamed segment */
-               } else if (cmd->cmd == LC_SYMTAB)
-                       orig_st = (struct symtab_command *) cmd;
-
-               cmd = (struct load_command *) ((caddr_t) cmd + cmd->cmdsize);
-       }
-
-       if ((orig_ts == NULL) || (orig_st == NULL) || (orig_le == NULL))
-               return;
-
-       sym = (kernel_nlist_t *)(orig_le->vmaddr + orig_st->symoff - orig_le->fileoff);
-       strings = (char *)(orig_le->vmaddr + orig_st->stroff - orig_le->fileoff);
-
-       /* Find extent of the TEXT section */
-       instrLow = (uintptr_t) orig_ts->vmaddr;
-       instrHigh = (uintptr_t) (orig_ts->vmaddr + orig_ts->vmsize);
-
-       for (i = 0; i < orig_st->nsyms; i++) {
-               uint8_t         n_type = sym[i].n_type & (N_TYPE | N_EXT);
-               char           *name = strings + sym[i].n_un.n_strx;
-
-               /* Check that the symbol is a global and that it has a name. */
-               if (((N_SECT | N_EXT) != n_type && (N_ABS | N_EXT) != n_type))
-                       continue;
-
-               if (0 == sym[i].n_un.n_strx)    /* iff a null, "", name. */
-                       continue;
-
-               /* Lop off omnipresent leading underscore. */
-               if (*name == '_')
-                       name += 1;
-
-                /*
-                * We're only blacklisting functions in the kernel for now.
-                */
-               if (MOD_IS_MACH_KERNEL(ctl) && fbt_excluded(name))
-                       continue;
-
-               fbt_provide_probe(ctl, instrLow, instrHigh, modname, name, (machine_inst_t*)sym[i].n_value);
-       }
-}