11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /* Rewritten by Rusty Russell, on the backs of many others...
31da177e4SLinus Torvalds Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
41da177e4SLinus Torvalds
51da177e4SLinus Torvalds */
6e1478d8eSChristophe Leroy #include <linux/elf.h>
78b96f011SFrederic Weisbecker #include <linux/ftrace.h>
8f80d2d77SDmitri Vorobiev #include <linux/memory.h>
98a293be0SPaul Gortmaker #include <linux/extable.h>
10505f2b97SIngo Molnar #include <linux/module.h>
11505f2b97SIngo Molnar #include <linux/mutex.h>
12505f2b97SIngo Molnar #include <linux/init.h>
135b485629SMasami Hiramatsu #include <linux/kprobes.h>
1474451e66SDaniel Borkmann #include <linux/filter.h>
15505f2b97SIngo Molnar
161da177e4SLinus Torvalds #include <asm/sections.h>
177c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
18505f2b97SIngo Molnar
19505f2b97SIngo Molnar /*
20505f2b97SIngo Molnar * mutex protecting text section modification (dynamic code patching).
21505f2b97SIngo Molnar * some users need to sleep (allocating memory...) while they hold this lock.
22505f2b97SIngo Molnar *
23e846d139SZhou Chengming * Note: Also protects SMP-alternatives modification on x86.
24e846d139SZhou Chengming *
25505f2b97SIngo Molnar * NOT exported to modules - patching kernel text is a really delicate matter.
26505f2b97SIngo Molnar */
27505f2b97SIngo Molnar DEFINE_MUTEX(text_mutex);
281da177e4SLinus Torvalds
291da177e4SLinus Torvalds extern struct exception_table_entry __start___ex_table[];
301da177e4SLinus Torvalds extern struct exception_table_entry __stop___ex_table[];
311da177e4SLinus Torvalds
32d219e2e8SDavid Daney /* Cleared by build time tools if the table is already sorted. */
3300b71030SAndi Kleen u32 __initdata __visible main_extable_sort_needed = 1;
34d219e2e8SDavid Daney
351da177e4SLinus Torvalds /* Sort the kernel's built-in exception table */
sort_main_extable(void)361da177e4SLinus Torvalds void __init sort_main_extable(void)
371da177e4SLinus Torvalds {
3863174f61SNathan Chancellor if (main_extable_sort_needed &&
3963174f61SNathan Chancellor &__stop___ex_table > &__start___ex_table) {
40bec1b9e7SBorislav Petkov pr_notice("Sorting __ex_table...\n");
411da177e4SLinus Torvalds sort_extable(__start___ex_table, __stop___ex_table);
42bec1b9e7SBorislav Petkov }
431da177e4SLinus Torvalds }
441da177e4SLinus Torvalds
4549ec9177SSantosh Sivaraj /* Given an address, look for it in the kernel exception table */
4649ec9177SSantosh Sivaraj const
search_kernel_exception_table(unsigned long addr)4749ec9177SSantosh Sivaraj struct exception_table_entry *search_kernel_exception_table(unsigned long addr)
4849ec9177SSantosh Sivaraj {
4949ec9177SSantosh Sivaraj return search_extable(__start___ex_table,
5049ec9177SSantosh Sivaraj __stop___ex_table - __start___ex_table, addr);
5149ec9177SSantosh Sivaraj }
5249ec9177SSantosh Sivaraj
531da177e4SLinus Torvalds /* Given an address, look for it in the exception tables. */
search_exception_tables(unsigned long addr)541da177e4SLinus Torvalds const struct exception_table_entry *search_exception_tables(unsigned long addr)
551da177e4SLinus Torvalds {
561da177e4SLinus Torvalds const struct exception_table_entry *e;
571da177e4SLinus Torvalds
5849ec9177SSantosh Sivaraj e = search_kernel_exception_table(addr);
591da177e4SLinus Torvalds if (!e)
601da177e4SLinus Torvalds e = search_module_extables(addr);
613dec541bSAlexei Starovoitov if (!e)
623dec541bSAlexei Starovoitov e = search_bpf_extables(addr);
631da177e4SLinus Torvalds return e;
641da177e4SLinus Torvalds }
651da177e4SLinus Torvalds
core_kernel_text(unsigned long addr)66c0d80ddaSMarcin Nowakowski int notrace core_kernel_text(unsigned long addr)
671da177e4SLinus Torvalds {
68808b6456SKefeng Wang if (is_kernel_text(addr))
691da177e4SLinus Torvalds return 1;
701da177e4SLinus Torvalds
71d2635f20SChristophe Leroy if (system_state < SYSTEM_FREEING_INITMEM &&
72b9ad8fe7SKefeng Wang is_kernel_inittext(addr))
731da177e4SLinus Torvalds return 1;
741da177e4SLinus Torvalds return 0;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds
__kernel_text_address(unsigned long addr)773861a17bSFrederic Weisbecker int __kernel_text_address(unsigned long addr)
781da177e4SLinus Torvalds {
799aadde91SSteven Rostedt (VMware) if (kernel_text_address(addr))
8074451e66SDaniel Borkmann return 1;
814a44bac1SIngo Molnar /*
824a44bac1SIngo Molnar * There might be init symbols in saved stacktraces.
834a44bac1SIngo Molnar * Give those symbols a chance to be printed in
844a44bac1SIngo Molnar * backtraces (such as lockdep traces).
854a44bac1SIngo Molnar *
864a44bac1SIngo Molnar * Since we are after the module-symbols check, there's
874a44bac1SIngo Molnar * no danger of address overlap:
884a44bac1SIngo Molnar */
89b9ad8fe7SKefeng Wang if (is_kernel_inittext(addr))
904a44bac1SIngo Molnar return 1;
914a44bac1SIngo Molnar return 0;
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds
kernel_text_address(unsigned long addr)941da177e4SLinus Torvalds int kernel_text_address(unsigned long addr)
951da177e4SLinus Torvalds {
96e8cac8b1SSteven Rostedt (VMware) bool no_rcu;
97e8cac8b1SSteven Rostedt (VMware) int ret = 1;
98e8cac8b1SSteven Rostedt (VMware)
991da177e4SLinus Torvalds if (core_kernel_text(addr))
1001da177e4SLinus Torvalds return 1;
101e8cac8b1SSteven Rostedt (VMware)
102e8cac8b1SSteven Rostedt (VMware) /*
103e8cac8b1SSteven Rostedt (VMware) * If a stack dump happens while RCU is not watching, then
104e8cac8b1SSteven Rostedt (VMware) * RCU needs to be notified that it requires to start
105e8cac8b1SSteven Rostedt (VMware) * watching again. This can happen either by tracing that
106e8cac8b1SSteven Rostedt (VMware) * triggers a stack trace, or a WARN() that happens during
107e8cac8b1SSteven Rostedt (VMware) * coming back from idle, or cpu on or offlining.
108e8cac8b1SSteven Rostedt (VMware) *
109e9b4e606SJiri Olsa * is_module_text_address() as well as the kprobe slots,
110e9b4e606SJiri Olsa * is_bpf_text_address() and is_bpf_image_address require
111e9b4e606SJiri Olsa * RCU to be watching.
112e8cac8b1SSteven Rostedt (VMware) */
113e8cac8b1SSteven Rostedt (VMware) no_rcu = !rcu_is_watching();
114e8cac8b1SSteven Rostedt (VMware)
115e8cac8b1SSteven Rostedt (VMware) /* Treat this like an NMI as it can happen anywhere */
116e8cac8b1SSteven Rostedt (VMware) if (no_rcu)
117*493c1822SFrederic Weisbecker ct_nmi_enter();
118e8cac8b1SSteven Rostedt (VMware)
119aec0be2dSSteven Rostedt (Red Hat) if (is_module_text_address(addr))
120e8cac8b1SSteven Rostedt (VMware) goto out;
1215b485629SMasami Hiramatsu if (is_ftrace_trampoline(addr))
122e8cac8b1SSteven Rostedt (VMware) goto out;
1235b485629SMasami Hiramatsu if (is_kprobe_optinsn_slot(addr) || is_kprobe_insn_slot(addr))
124e8cac8b1SSteven Rostedt (VMware) goto out;
12574451e66SDaniel Borkmann if (is_bpf_text_address(addr))
126e8cac8b1SSteven Rostedt (VMware) goto out;
127e8cac8b1SSteven Rostedt (VMware) ret = 0;
128e8cac8b1SSteven Rostedt (VMware) out:
129e8cac8b1SSteven Rostedt (VMware) if (no_rcu)
130*493c1822SFrederic Weisbecker ct_nmi_exit();
131e8cac8b1SSteven Rostedt (VMware)
132e8cac8b1SSteven Rostedt (VMware) return ret;
1331da177e4SLinus Torvalds }
134ab7476cfSArjan van de Ven
135ab7476cfSArjan van de Ven /*
136e1478d8eSChristophe Leroy * On some architectures (PPC64, IA64, PARISC) function pointers
137ab7476cfSArjan van de Ven * are actually only tokens to some data that then holds the
138ab7476cfSArjan van de Ven * real function address. As a result, to find if a function
139ab7476cfSArjan van de Ven * pointer is part of the kernel text, we need to do some
140ab7476cfSArjan van de Ven * special dereferencing first.
141ab7476cfSArjan van de Ven */
142e1478d8eSChristophe Leroy #ifdef CONFIG_HAVE_FUNCTION_DESCRIPTORS
dereference_function_descriptor(void * ptr)143e1478d8eSChristophe Leroy void *dereference_function_descriptor(void *ptr)
144e1478d8eSChristophe Leroy {
145e1478d8eSChristophe Leroy func_desc_t *desc = ptr;
146e1478d8eSChristophe Leroy void *p;
147e1478d8eSChristophe Leroy
148e1478d8eSChristophe Leroy if (!get_kernel_nofault(p, (void *)&desc->addr))
149e1478d8eSChristophe Leroy ptr = p;
150e1478d8eSChristophe Leroy return ptr;
151e1478d8eSChristophe Leroy }
152b6491339SChristophe Leroy EXPORT_SYMBOL_GPL(dereference_function_descriptor);
153e1478d8eSChristophe Leroy
dereference_kernel_function_descriptor(void * ptr)154e1478d8eSChristophe Leroy void *dereference_kernel_function_descriptor(void *ptr)
155e1478d8eSChristophe Leroy {
156e1478d8eSChristophe Leroy if (ptr < (void *)__start_opd || ptr >= (void *)__end_opd)
157e1478d8eSChristophe Leroy return ptr;
158e1478d8eSChristophe Leroy
159e1478d8eSChristophe Leroy return dereference_function_descriptor(ptr);
160e1478d8eSChristophe Leroy }
161e1478d8eSChristophe Leroy #endif
162e1478d8eSChristophe Leroy
func_ptr_is_kernel_text(void * ptr)163ab7476cfSArjan van de Ven int func_ptr_is_kernel_text(void *ptr)
164ab7476cfSArjan van de Ven {
165ab7476cfSArjan van de Ven unsigned long addr;
166ab7476cfSArjan van de Ven addr = (unsigned long) dereference_function_descriptor(ptr);
167ab7476cfSArjan van de Ven if (core_kernel_text(addr))
168ab7476cfSArjan van de Ven return 1;
169a6e6abd5SRusty Russell return is_module_text_address(addr);
170ab7476cfSArjan van de Ven }
171