Merge tag 'folio-5.18d' of git://git.infradead.org/users/willy/pagecache
[linux-2.6-microblaze.git] / kernel / kprobes.c
index 94cab8c..dbe57df 100644 (file)
@@ -1237,6 +1237,27 @@ void kprobes_inc_nmissed_count(struct kprobe *p)
 }
 NOKPROBE_SYMBOL(kprobes_inc_nmissed_count);
 
+static struct kprobe kprobe_busy = {
+       .addr = (void *) get_kprobe,
+};
+
+void kprobe_busy_begin(void)
+{
+       struct kprobe_ctlblk *kcb;
+
+       preempt_disable();
+       __this_cpu_write(current_kprobe, &kprobe_busy);
+       kcb = get_kprobe_ctlblk();
+       kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+}
+
+void kprobe_busy_end(void)
+{
+       __this_cpu_write(current_kprobe, NULL);
+       preempt_enable();
+}
+
+#if !defined(CONFIG_KRETPROBE_ON_RETHOOK)
 static void free_rp_inst_rcu(struct rcu_head *head)
 {
        struct kretprobe_instance *ri = container_of(head, struct kretprobe_instance, rcu);
@@ -1258,26 +1279,6 @@ static void recycle_rp_inst(struct kretprobe_instance *ri)
 }
 NOKPROBE_SYMBOL(recycle_rp_inst);
 
-static struct kprobe kprobe_busy = {
-       .addr = (void *) get_kprobe,
-};
-
-void kprobe_busy_begin(void)
-{
-       struct kprobe_ctlblk *kcb;
-
-       preempt_disable();
-       __this_cpu_write(current_kprobe, &kprobe_busy);
-       kcb = get_kprobe_ctlblk();
-       kcb->kprobe_status = KPROBE_HIT_ACTIVE;
-}
-
-void kprobe_busy_end(void)
-{
-       __this_cpu_write(current_kprobe, NULL);
-       preempt_enable();
-}
-
 /*
  * This function is called from delayed_put_task_struct() when a task is
  * dead and cleaned up to recycle any kretprobe instances associated with
@@ -1327,6 +1328,7 @@ static inline void free_rp_inst(struct kretprobe *rp)
                rp->rph = NULL;
        }
 }
+#endif /* !CONFIG_KRETPROBE_ON_RETHOOK */
 
 /* Add the new probe to 'ap->list'. */
 static int add_new_kprobe(struct kprobe *ap, struct kprobe *p)
@@ -1488,25 +1490,69 @@ bool within_kprobe_blacklist(unsigned long addr)
        return false;
 }
 
+/*
+ * arch_adjust_kprobe_addr - adjust the address
+ * @addr: symbol base address
+ * @offset: offset within the symbol
+ * @on_func_entry: was this @addr+@offset on the function entry
+ *
+ * Typically returns @addr + @offset, except for special cases where the
+ * function might be prefixed by a CFI landing pad, in that case any offset
+ * inside the landing pad is mapped to the first 'real' instruction of the
+ * symbol.
+ *
+ * Specifically, for things like IBT/BTI, skip the resp. ENDBR/BTI.C
+ * instruction at +0.
+ */
+kprobe_opcode_t *__weak arch_adjust_kprobe_addr(unsigned long addr,
+                                               unsigned long offset,
+                                               bool *on_func_entry)
+{
+       *on_func_entry = !offset;
+       return (kprobe_opcode_t *)(addr + offset);
+}
+
 /*
  * If 'symbol_name' is specified, look it up and add the 'offset'
  * to it. This way, we can specify a relative address to a symbol.
  * This returns encoded errors if it fails to look up symbol or invalid
  * combination of parameters.
  */
-static kprobe_opcode_t *_kprobe_addr(kprobe_opcode_t *addr,
-                       const char *symbol_name, unsigned int offset)
+static kprobe_opcode_t *
+_kprobe_addr(kprobe_opcode_t *addr, const char *symbol_name,
+            unsigned long offset, bool *on_func_entry)
 {
        if ((symbol_name && addr) || (!symbol_name && !addr))
                goto invalid;
 
        if (symbol_name) {
+               /*
+                * Input: @sym + @offset
+                * Output: @addr + @offset
+                *
+                * NOTE: kprobe_lookup_name() does *NOT* fold the offset
+                *       argument into it's output!
+                */
                addr = kprobe_lookup_name(symbol_name, offset);
                if (!addr)
                        return ERR_PTR(-ENOENT);
        }
 
-       addr = (kprobe_opcode_t *)(((char *)addr) + offset);
+       /*
+        * So here we have @addr + @offset, displace it into a new
+        * @addr' + @offset' where @addr' is the symbol start address.
+        */
+       addr = (void *)addr + offset;
+       if (!kallsyms_lookup_size_offset((unsigned long)addr, NULL, &offset))
+               return ERR_PTR(-ENOENT);
+       addr = (void *)addr - offset;
+
+       /*
+        * Then ask the architecture to re-combine them, taking care of
+        * magical function entry details while telling us if this was indeed
+        * at the start of the function.
+        */
+       addr = arch_adjust_kprobe_addr((unsigned long)addr, offset, on_func_entry);
        if (addr)
                return addr;
 
@@ -1516,7 +1562,8 @@ invalid:
 
 static kprobe_opcode_t *kprobe_addr(struct kprobe *p)
 {
-       return _kprobe_addr(p->addr, p->symbol_name, p->offset);
+       bool on_func_entry;
+       return _kprobe_addr(p->addr, p->symbol_name, p->offset, &on_func_entry);
 }
 
 /*
@@ -1562,14 +1609,10 @@ static inline int warn_kprobe_rereg(struct kprobe *p)
 
 static int check_ftrace_location(struct kprobe *p)
 {
-       unsigned long ftrace_addr;
+       unsigned long addr = (unsigned long)p->addr;
 
-       ftrace_addr = ftrace_location((unsigned long)p->addr);
-       if (ftrace_addr) {
+       if (ftrace_location(addr) == addr) {
 #ifdef CONFIG_KPROBES_ON_FTRACE
-               /* Given address is not on the instruction boundary */
-               if ((unsigned long)p->addr != ftrace_addr)
-                       return -EILSEQ;
                p->flags |= KPROBE_FLAG_FTRACE;
 #else  /* !CONFIG_KPROBES_ON_FTRACE */
                return -EINVAL;
@@ -1884,6 +1927,7 @@ static struct notifier_block kprobe_exceptions_nb = {
 
 #ifdef CONFIG_KRETPROBES
 
+#if !defined(CONFIG_KRETPROBE_ON_RETHOOK)
 /* This assumes the 'tsk' is the current task or the is not running. */
 static kprobe_opcode_t *__kretprobe_find_ret_addr(struct task_struct *tsk,
                                                  struct llist_node **cur)
@@ -2046,11 +2090,57 @@ static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
        return 0;
 }
 NOKPROBE_SYMBOL(pre_handler_kretprobe);
+#else /* CONFIG_KRETPROBE_ON_RETHOOK */
+/*
+ * This kprobe pre_handler is registered with every kretprobe. When probe
+ * hits it will set up the return probe.
+ */
+static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
+{
+       struct kretprobe *rp = container_of(p, struct kretprobe, kp);
+       struct kretprobe_instance *ri;
+       struct rethook_node *rhn;
+
+       rhn = rethook_try_get(rp->rh);
+       if (!rhn) {
+               rp->nmissed++;
+               return 0;
+       }
+
+       ri = container_of(rhn, struct kretprobe_instance, node);
+
+       if (rp->entry_handler && rp->entry_handler(ri, regs))
+               rethook_recycle(rhn);
+       else
+               rethook_hook(rhn, regs, kprobe_ftrace(p));
+
+       return 0;
+}
+NOKPROBE_SYMBOL(pre_handler_kretprobe);
 
-bool __weak arch_kprobe_on_func_entry(unsigned long offset)
+static void kretprobe_rethook_handler(struct rethook_node *rh, void *data,
+                                     struct pt_regs *regs)
 {
-       return !offset;
+       struct kretprobe *rp = (struct kretprobe *)data;
+       struct kretprobe_instance *ri;
+       struct kprobe_ctlblk *kcb;
+
+       /* The data must NOT be null. This means rethook data structure is broken. */
+       if (WARN_ON_ONCE(!data))
+               return;
+
+       __this_cpu_write(current_kprobe, &rp->kp);
+       kcb = get_kprobe_ctlblk();
+       kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+       ri = container_of(rh, struct kretprobe_instance, node);
+       rp->handler(ri, regs);
+
+       __this_cpu_write(current_kprobe, NULL);
 }
+NOKPROBE_SYMBOL(kretprobe_rethook_handler);
+
+#endif /* !CONFIG_KRETPROBE_ON_RETHOOK */
 
 /**
  * kprobe_on_func_entry() -- check whether given address is function entry
@@ -2067,15 +2157,13 @@ bool __weak arch_kprobe_on_func_entry(unsigned long offset)
  */
 int kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset)
 {
-       kprobe_opcode_t *kp_addr = _kprobe_addr(addr, sym, offset);
+       bool on_func_entry;
+       kprobe_opcode_t *kp_addr = _kprobe_addr(addr, sym, offset, &on_func_entry);
 
        if (IS_ERR(kp_addr))
                return PTR_ERR(kp_addr);
 
-       if (!kallsyms_lookup_size_offset((unsigned long)kp_addr, NULL, &offset))
-               return -ENOENT;
-
-       if (!arch_kprobe_on_func_entry(offset))
+       if (!on_func_entry)
                return -EINVAL;
 
        return 0;
@@ -2121,6 +2209,29 @@ int register_kretprobe(struct kretprobe *rp)
                rp->maxactive = num_possible_cpus();
 #endif
        }
+#ifdef CONFIG_KRETPROBE_ON_RETHOOK
+       rp->rh = rethook_alloc((void *)rp, kretprobe_rethook_handler);
+       if (!rp->rh)
+               return -ENOMEM;
+
+       for (i = 0; i < rp->maxactive; i++) {
+               inst = kzalloc(sizeof(struct kretprobe_instance) +
+                              rp->data_size, GFP_KERNEL);
+               if (inst == NULL) {
+                       rethook_free(rp->rh);
+                       rp->rh = NULL;
+                       return -ENOMEM;
+               }
+               rethook_add_node(rp->rh, &inst->node);
+       }
+       rp->nmissed = 0;
+       /* Establish function entry probe point */
+       ret = register_kprobe(&rp->kp);
+       if (ret != 0) {
+               rethook_free(rp->rh);
+               rp->rh = NULL;
+       }
+#else  /* !CONFIG_KRETPROBE_ON_RETHOOK */
        rp->freelist.head = NULL;
        rp->rph = kzalloc(sizeof(struct kretprobe_holder), GFP_KERNEL);
        if (!rp->rph)
@@ -2145,6 +2256,7 @@ int register_kretprobe(struct kretprobe *rp)
        ret = register_kprobe(&rp->kp);
        if (ret != 0)
                free_rp_inst(rp);
+#endif
        return ret;
 }
 EXPORT_SYMBOL_GPL(register_kretprobe);
@@ -2183,7 +2295,11 @@ void unregister_kretprobes(struct kretprobe **rps, int num)
        for (i = 0; i < num; i++) {
                if (__unregister_kprobe_top(&rps[i]->kp) < 0)
                        rps[i]->kp.addr = NULL;
+#ifdef CONFIG_KRETPROBE_ON_RETHOOK
+               rethook_free(rps[i]->rh);
+#else
                rps[i]->rph->rp = NULL;
+#endif
        }
        mutex_unlock(&kprobe_mutex);
 
@@ -2191,7 +2307,9 @@ void unregister_kretprobes(struct kretprobe **rps, int num)
        for (i = 0; i < num; i++) {
                if (rps[i]->kp.addr) {
                        __unregister_kprobe_bottom(&rps[i]->kp);
+#ifndef CONFIG_KRETPROBE_ON_RETHOOK
                        free_rp_inst(rps[i]);
+#endif
                }
        }
 }