aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-27 11:30:30 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-27 11:30:30 -0400
commit77d64656950b299f5e7c70f1e3a0bdc991b51497 (patch)
tree91e5a8c9d9e5e791301225e4e0b658b4fd5b3e81
parentc86daad2c25bfd4a33d48b7691afaa96d9c5ab46 (diff)
parent6ee98ffeea0bc9e072e419497d78697d8afcdd6d (diff)
Merge tag 'trace-v4.12-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace
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()
-rw-r--r--arch/x86/kernel/ftrace.c20
-rw-r--r--arch/x86/kernel/kprobes/core.c9
-rw-r--r--arch/x86/mm/pageattr.c2
-rw-r--r--kernel/kprobes.c2
-rw-r--r--kernel/trace/ftrace.c2
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc21
6 files changed, 47 insertions, 9 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 0651e974dcb3..9bef1bbeba63 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -689,8 +689,12 @@ static inline void *alloc_tramp(unsigned long size)
689{ 689{
690 return module_alloc(size); 690 return module_alloc(size);
691} 691}
692static inline void tramp_free(void *tramp) 692static inline void tramp_free(void *tramp, int size)
693{ 693{
694 int npages = PAGE_ALIGN(size) >> PAGE_SHIFT;
695
696 set_memory_nx((unsigned long)tramp, npages);
697 set_memory_rw((unsigned long)tramp, npages);
694 module_memfree(tramp); 698 module_memfree(tramp);
695} 699}
696#else 700#else
@@ -699,7 +703,7 @@ static inline void *alloc_tramp(unsigned long size)
699{ 703{
700 return NULL; 704 return NULL;
701} 705}
702static inline void tramp_free(void *tramp) { } 706static inline void tramp_free(void *tramp, int size) { }
703#endif 707#endif
704 708
705/* Defined as markers to the end of the ftrace default trampolines */ 709/* 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)
771 /* Copy ftrace_caller onto the trampoline memory */ 775 /* Copy ftrace_caller onto the trampoline memory */
772 ret = probe_kernel_read(trampoline, (void *)start_offset, size); 776 ret = probe_kernel_read(trampoline, (void *)start_offset, size);
773 if (WARN_ON(ret < 0)) { 777 if (WARN_ON(ret < 0)) {
774 tramp_free(trampoline); 778 tramp_free(trampoline, *tramp_size);
775 return 0; 779 return 0;
776 } 780 }
777 781
@@ -797,7 +801,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
797 801
798 /* Are we pointing to the reference? */ 802 /* Are we pointing to the reference? */
799 if (WARN_ON(memcmp(op_ptr.op, op_ref, 3) != 0)) { 803 if (WARN_ON(memcmp(op_ptr.op, op_ref, 3) != 0)) {
800 tramp_free(trampoline); 804 tramp_free(trampoline, *tramp_size);
801 return 0; 805 return 0;
802 } 806 }
803 807
@@ -839,7 +843,7 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
839 unsigned long offset; 843 unsigned long offset;
840 unsigned long ip; 844 unsigned long ip;
841 unsigned int size; 845 unsigned int size;
842 int ret; 846 int ret, npages;
843 847
844 if (ops->trampoline) { 848 if (ops->trampoline) {
845 /* 849 /*
@@ -848,11 +852,14 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
848 */ 852 */
849 if (!(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP)) 853 if (!(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP))
850 return; 854 return;
855 npages = PAGE_ALIGN(ops->trampoline_size) >> PAGE_SHIFT;
856 set_memory_rw(ops->trampoline, npages);
851 } else { 857 } else {
852 ops->trampoline = create_trampoline(ops, &size); 858 ops->trampoline = create_trampoline(ops, &size);
853 if (!ops->trampoline) 859 if (!ops->trampoline)
854 return; 860 return;
855 ops->trampoline_size = size; 861 ops->trampoline_size = size;
862 npages = PAGE_ALIGN(size) >> PAGE_SHIFT;
856 } 863 }
857 864
858 offset = calc_trampoline_call_offset(ops->flags & FTRACE_OPS_FL_SAVE_REGS); 865 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)
863 /* Do a safe modify in case the trampoline is executing */ 870 /* Do a safe modify in case the trampoline is executing */
864 new = ftrace_call_replace(ip, (unsigned long)func); 871 new = ftrace_call_replace(ip, (unsigned long)func);
865 ret = update_ftrace_func(ip, new); 872 ret = update_ftrace_func(ip, new);
873 set_memory_ro(ops->trampoline, npages);
866 874
867 /* The update should never fail */ 875 /* The update should never fail */
868 WARN_ON(ret); 876 WARN_ON(ret);
@@ -939,7 +947,7 @@ void arch_ftrace_trampoline_free(struct ftrace_ops *ops)
939 if (!ops || !(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP)) 947 if (!ops || !(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP))
940 return; 948 return;
941 949
942 tramp_free((void *)ops->trampoline); 950 tramp_free((void *)ops->trampoline, ops->trampoline_size);
943 ops->trampoline = 0; 951 ops->trampoline = 0;
944} 952}
945 953
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index 5b2bbfbb3712..6b877807598b 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -52,6 +52,7 @@
52#include <linux/ftrace.h> 52#include <linux/ftrace.h>
53#include <linux/frame.h> 53#include <linux/frame.h>
54#include <linux/kasan.h> 54#include <linux/kasan.h>
55#include <linux/moduleloader.h>
55 56
56#include <asm/text-patching.h> 57#include <asm/text-patching.h>
57#include <asm/cacheflush.h> 58#include <asm/cacheflush.h>
@@ -417,6 +418,14 @@ static void prepare_boost(struct kprobe *p, struct insn *insn)
417 } 418 }
418} 419}
419 420
421/* Recover page to RW mode before releasing it */
422void free_insn_page(void *page)
423{
424 set_memory_nx((unsigned long)page & PAGE_MASK, 1);
425 set_memory_rw((unsigned long)page & PAGE_MASK, 1);
426 module_memfree(page);
427}
428
420static int arch_copy_kprobe(struct kprobe *p) 429static int arch_copy_kprobe(struct kprobe *p)
421{ 430{
422 struct insn insn; 431 struct insn insn;
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 1dcd2be4cce4..c8520b2c62d2 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -186,7 +186,7 @@ static void cpa_flush_range(unsigned long start, int numpages, int cache)
186 unsigned int i, level; 186 unsigned int i, level;
187 unsigned long addr; 187 unsigned long addr;
188 188
189 BUG_ON(irqs_disabled()); 189 BUG_ON(irqs_disabled() && !early_boot_irqs_disabled);
190 WARN_ON(PAGE_ALIGN(start) != start); 190 WARN_ON(PAGE_ALIGN(start) != start);
191 191
192 on_each_cpu(__cpa_flush_range, NULL, 1); 192 on_each_cpu(__cpa_flush_range, NULL, 1);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 2d2d3a568e4e..adfe3b4cfe05 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -122,7 +122,7 @@ static void *alloc_insn_page(void)
122 return module_alloc(PAGE_SIZE); 122 return module_alloc(PAGE_SIZE);
123} 123}
124 124
125static void free_insn_page(void *page) 125void __weak free_insn_page(void *page)
126{ 126{
127 module_memfree(page); 127 module_memfree(page);
128} 128}
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 74fdfe9ed3db..9e5841dc14b5 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5063,7 +5063,7 @@ ftrace_graph_release(struct inode *inode, struct file *file)
5063 } 5063 }
5064 5064
5065 out: 5065 out:
5066 kfree(fgd->new_hash); 5066 free_ftrace_hash(fgd->new_hash);
5067 kfree(fgd); 5067 kfree(fgd);
5068 5068
5069 return ret; 5069 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
index 000000000000..f4d1ff785d67
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc
@@ -0,0 +1,21 @@
1#!/bin/sh
2# description: Register/unregister many kprobe events
3
4# ftrace fentry skip size depends on the machine architecture.
5# Currently HAVE_KPROBES_ON_FTRACE defined on x86 and powerpc
6case `uname -m` in
7 x86_64|i[3456]86) OFFS=5;;
8 ppc*) OFFS=4;;
9 *) OFFS=0;;
10esac
11
12echo "Setup up to 256 kprobes"
13grep t /proc/kallsyms | cut -f3 -d" " | grep -v .*\\..* | \
14head -n 256 | while read i; do echo p ${i}+${OFFS} ; done > kprobe_events ||:
15
16echo 1 > events/kprobes/enable
17echo 0 > events/kprobes/enable
18echo > kprobe_events
19echo "Waiting for unoptimizing & freeing"
20sleep 5
21echo "Done"