]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge tag 'trace-v4.12-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 27 May 2017 15:30:30 +0000 (08:30 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 27 May 2017 15:30:30 +0000 (08:30 -0700)
Pull ftrace fixes from Steven Rostedt:
 "There's been a few memory issues found with ftrace.

  One was simply a memory leak where not all was being freed that should
  have been in releasing a file pointer on set_graph_function.

  Then Thomas found that the ftrace trampolines were marked for
  read/write as well as execute. To shrink the possible attack surface,
  he added calls to set them to ro. Which also uncovered some other
  issues with freeing module allocated memory that had its permissions
  changed.

  Kprobes had a similar issue which is fixed and a selftest was added to
  trigger that issue again"

* tag 'trace-v4.12-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace:
  x86/ftrace: Make sure that ftrace trampolines are not RWX
  x86/mm/ftrace: Do not bug in early boot on irqs_disabled in cpu_flush_range()
  selftests/ftrace: Add a testcase for many kprobe events
  kprobes/x86: Fix to set RWX bits correctly before releasing trampoline
  ftrace: Fix memory leak in ftrace_graph_release()

arch/x86/kernel/ftrace.c
arch/x86/kernel/kprobes/core.c
arch/x86/mm/pageattr.c
kernel/kprobes.c
kernel/trace/ftrace.c
tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc [new file with mode: 0644]

index 0651e974dcb3a88211db1711a5969434d3163e5a..9bef1bbeba63885e3ba584960b97e8c190b4c5a8 100644 (file)
@@ -689,8 +689,12 @@ static inline void *alloc_tramp(unsigned long size)
 {
        return module_alloc(size);
 }
-static inline void tramp_free(void *tramp)
+static inline void tramp_free(void *tramp, int size)
 {
+       int npages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+
+       set_memory_nx((unsigned long)tramp, npages);
+       set_memory_rw((unsigned long)tramp, npages);
        module_memfree(tramp);
 }
 #else
@@ -699,7 +703,7 @@ static inline void *alloc_tramp(unsigned long size)
 {
        return NULL;
 }
-static inline void tramp_free(void *tramp) { }
+static inline void tramp_free(void *tramp, int size) { }
 #endif
 
 /* Defined as markers to the end of the ftrace default trampolines */
@@ -771,7 +775,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
        /* Copy ftrace_caller onto the trampoline memory */
        ret = probe_kernel_read(trampoline, (void *)start_offset, size);
        if (WARN_ON(ret < 0)) {
-               tramp_free(trampoline);
+               tramp_free(trampoline, *tramp_size);
                return 0;
        }
 
@@ -797,7 +801,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
 
        /* Are we pointing to the reference? */
        if (WARN_ON(memcmp(op_ptr.op, op_ref, 3) != 0)) {
-               tramp_free(trampoline);
+               tramp_free(trampoline, *tramp_size);
                return 0;
        }
 
@@ -839,7 +843,7 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
        unsigned long offset;
        unsigned long ip;
        unsigned int size;
-       int ret;
+       int ret, npages;
 
        if (ops->trampoline) {
                /*
@@ -848,11 +852,14 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
                 */
                if (!(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP))
                        return;
+               npages = PAGE_ALIGN(ops->trampoline_size) >> PAGE_SHIFT;
+               set_memory_rw(ops->trampoline, npages);
        } else {
                ops->trampoline = create_trampoline(ops, &size);
                if (!ops->trampoline)
                        return;
                ops->trampoline_size = size;
+               npages = PAGE_ALIGN(size) >> PAGE_SHIFT;
        }
 
        offset = calc_trampoline_call_offset(ops->flags & FTRACE_OPS_FL_SAVE_REGS);
@@ -863,6 +870,7 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
        /* Do a safe modify in case the trampoline is executing */
        new = ftrace_call_replace(ip, (unsigned long)func);
        ret = update_ftrace_func(ip, new);
+       set_memory_ro(ops->trampoline, npages);
 
        /* The update should never fail */
        WARN_ON(ret);
@@ -939,7 +947,7 @@ void arch_ftrace_trampoline_free(struct ftrace_ops *ops)
        if (!ops || !(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP))
                return;
 
-       tramp_free((void *)ops->trampoline);
+       tramp_free((void *)ops->trampoline, ops->trampoline_size);
        ops->trampoline = 0;
 }
 
index 5b2bbfbb371284942b2ce95a3b2495eb9ecc8981..6b877807598b9028527a2357fef630431021c383 100644 (file)
@@ -52,6 +52,7 @@
 #include <linux/ftrace.h>
 #include <linux/frame.h>
 #include <linux/kasan.h>
+#include <linux/moduleloader.h>
 
 #include <asm/text-patching.h>
 #include <asm/cacheflush.h>
@@ -417,6 +418,14 @@ static void prepare_boost(struct kprobe *p, struct insn *insn)
        }
 }
 
+/* Recover page to RW mode before releasing it */
+void free_insn_page(void *page)
+{
+       set_memory_nx((unsigned long)page & PAGE_MASK, 1);
+       set_memory_rw((unsigned long)page & PAGE_MASK, 1);
+       module_memfree(page);
+}
+
 static int arch_copy_kprobe(struct kprobe *p)
 {
        struct insn insn;
index 1dcd2be4cce44aabd5659b531962f918da1b8dd3..c8520b2c62d252f774f7c6b9df83eefa6a5925c2 100644 (file)
@@ -186,7 +186,7 @@ static void cpa_flush_range(unsigned long start, int numpages, int cache)
        unsigned int i, level;
        unsigned long addr;
 
-       BUG_ON(irqs_disabled());
+       BUG_ON(irqs_disabled() && !early_boot_irqs_disabled);
        WARN_ON(PAGE_ALIGN(start) != start);
 
        on_each_cpu(__cpa_flush_range, NULL, 1);
index 2d2d3a568e4e8b3aeedb16711c90e660f698d6cf..adfe3b4cfe05a101bcee0da8e72db6fe6a10cf72 100644 (file)
@@ -122,7 +122,7 @@ static void *alloc_insn_page(void)
        return module_alloc(PAGE_SIZE);
 }
 
-static void free_insn_page(void *page)
+void __weak free_insn_page(void *page)
 {
        module_memfree(page);
 }
index 74fdfe9ed3dba7fa659cb11feafac25919984bbb..9e5841dc14b5fa62e0c1470df704c3dc9b99f8ad 100644 (file)
@@ -5063,7 +5063,7 @@ ftrace_graph_release(struct inode *inode, struct file *file)
        }
 
  out:
-       kfree(fgd->new_hash);
+       free_ftrace_hash(fgd->new_hash);
        kfree(fgd);
 
        return ret;
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc b/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc
new file mode 100644 (file)
index 0000000..f4d1ff7
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+# description: Register/unregister many kprobe events
+
+# ftrace fentry skip size depends on the machine architecture.
+# Currently HAVE_KPROBES_ON_FTRACE defined on x86 and powerpc
+case `uname -m` in
+  x86_64|i[3456]86) OFFS=5;;
+  ppc*) OFFS=4;;
+  *) OFFS=0;;
+esac
+
+echo "Setup up to 256 kprobes"
+grep t /proc/kallsyms | cut -f3 -d" " | grep -v .*\\..* | \
+head -n 256 | while read i; do echo p ${i}+${OFFS} ; done > kprobe_events ||:
+
+echo 1 > events/kprobes/enable
+echo 0 > events/kprobes/enable
+echo > kprobe_events
+echo "Waiting for unoptimizing & freeing"
+sleep 5
+echo "Done"