OSDN Git Service

Merge tag 'x86_core_for_5.18_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[uclinux-h8/linux.git] / kernel / kprobes.c
index 21eccc9..185badc 100644 (file)
@@ -48,6 +48,9 @@
 #define KPROBE_HASH_BITS 6
 #define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
 
+#if !defined(CONFIG_OPTPROBES) || !defined(CONFIG_SYSCTL)
+#define kprobe_sysctls_init() do { } while (0)
+#endif
 
 static int kprobes_initialized;
 /* kprobe_table can be accessed by
@@ -938,10 +941,10 @@ static void unoptimize_all_kprobes(void)
 }
 
 static DEFINE_MUTEX(kprobe_sysctl_mutex);
-int sysctl_kprobes_optimization;
-int proc_kprobes_optimization_handler(struct ctl_table *table, int write,
-                                     void *buffer, size_t *length,
-                                     loff_t *ppos)
+static int sysctl_kprobes_optimization;
+static int proc_kprobes_optimization_handler(struct ctl_table *table,
+                                            int write, void *buffer,
+                                            size_t *length, loff_t *ppos)
 {
        int ret;
 
@@ -957,6 +960,24 @@ int proc_kprobes_optimization_handler(struct ctl_table *table, int write,
 
        return ret;
 }
+
+static struct ctl_table kprobe_sysctls[] = {
+       {
+               .procname       = "kprobes-optimization",
+               .data           = &sysctl_kprobes_optimization,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_kprobes_optimization_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+       {}
+};
+
+static void __init kprobe_sysctls_init(void)
+{
+       register_sysctl_init("debug", kprobe_sysctls);
+}
 #endif /* CONFIG_SYSCTL */
 
 /* Put a breakpoint for a probe. */
@@ -1468,24 +1489,68 @@ bool within_kprobe_blacklist(unsigned long addr)
 }
 
 /*
+ * 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;
 
@@ -1495,7 +1560,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);
 }
 
 /*
@@ -1541,14 +1607,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;
@@ -2026,11 +2088,6 @@ static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
 }
 NOKPROBE_SYMBOL(pre_handler_kretprobe);
 
-bool __weak arch_kprobe_on_func_entry(unsigned long offset)
-{
-       return !offset;
-}
-
 /**
  * kprobe_on_func_entry() -- check whether given address is function entry
  * @addr: Target address
@@ -2046,15 +2103,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;
@@ -2584,6 +2639,7 @@ static int __init init_kprobes(void)
                err = register_module_notifier(&kprobe_module_nb);
 
        kprobes_initialized = (err == 0);
+       kprobe_sysctls_init();
        return err;
 }
 early_initcall(init_kprobes);