diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2017-05-25 04:57:51 -0400 |
---|---|---|
committer | Steven Rostedt (VMware) <rostedt@goodmis.org> | 2017-05-26 22:37:02 -0400 |
commit | 6ee98ffeea0bc9e072e419497d78697d8afcdd6d (patch) | |
tree | d1fdc5c65a3006941edc142b86935d6cffe55ce8 | |
parent | a53276e2826010338478ed94310874001a8097fa (diff) |
x86/ftrace: Make sure that ftrace trampolines are not RWX
ftrace use module_alloc() to allocate trampoline pages. The mapping of
module_alloc() is RWX, which makes sense as the memory is written to right
after allocation. But nothing makes these pages RO after writing to them.
Add proper set_memory_rw/ro() calls to protect the trampolines after
modification.
Link: http://lkml.kernel.org/r/alpine.DEB.2.20.1705251056410.1862@nanos
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
-rw-r--r-- | arch/x86/kernel/ftrace.c | 20 |
1 files changed, 14 insertions, 6 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 | } |
692 | static inline void tramp_free(void *tramp) | 692 | static 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 | } |
702 | static inline void tramp_free(void *tramp) { } | 706 | static 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 | ||