|
@@ -57,6 +57,7 @@
|
|
|
#include <linux/kmemleak.h>
|
|
|
#include <linux/jump_label.h>
|
|
|
#include <linux/pfn.h>
|
|
|
+#include <linux/bsearch.h>
|
|
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
|
#include <trace/events/module.h>
|
|
@@ -240,23 +241,24 @@ static bool each_symbol_in_section(const struct symsearch *arr,
|
|
|
struct module *owner,
|
|
|
bool (*fn)(const struct symsearch *syms,
|
|
|
struct module *owner,
|
|
|
- unsigned int symnum, void *data),
|
|
|
+ void *data),
|
|
|
void *data)
|
|
|
{
|
|
|
- unsigned int i, j;
|
|
|
+ unsigned int j;
|
|
|
|
|
|
for (j = 0; j < arrsize; j++) {
|
|
|
- for (i = 0; i < arr[j].stop - arr[j].start; i++)
|
|
|
- if (fn(&arr[j], owner, i, data))
|
|
|
- return true;
|
|
|
+ if (fn(&arr[j], owner, data))
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/* Returns true as soon as fn returns true, otherwise false. */
|
|
|
-bool each_symbol(bool (*fn)(const struct symsearch *arr, struct module *owner,
|
|
|
- unsigned int symnum, void *data), void *data)
|
|
|
+bool each_symbol_section(bool (*fn)(const struct symsearch *arr,
|
|
|
+ struct module *owner,
|
|
|
+ void *data),
|
|
|
+ void *data)
|
|
|
{
|
|
|
struct module *mod;
|
|
|
static const struct symsearch arr[] = {
|
|
@@ -309,7 +311,7 @@ bool each_symbol(bool (*fn)(const struct symsearch *arr, struct module *owner,
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(each_symbol);
|
|
|
+EXPORT_SYMBOL_GPL(each_symbol_section);
|
|
|
|
|
|
struct find_symbol_arg {
|
|
|
/* Input */
|
|
@@ -323,15 +325,12 @@ struct find_symbol_arg {
|
|
|
const struct kernel_symbol *sym;
|
|
|
};
|
|
|
|
|
|
-static bool find_symbol_in_section(const struct symsearch *syms,
|
|
|
- struct module *owner,
|
|
|
- unsigned int symnum, void *data)
|
|
|
+static bool check_symbol(const struct symsearch *syms,
|
|
|
+ struct module *owner,
|
|
|
+ unsigned int symnum, void *data)
|
|
|
{
|
|
|
struct find_symbol_arg *fsa = data;
|
|
|
|
|
|
- if (strcmp(syms->start[symnum].name, fsa->name) != 0)
|
|
|
- return false;
|
|
|
-
|
|
|
if (!fsa->gplok) {
|
|
|
if (syms->licence == GPL_ONLY)
|
|
|
return false;
|
|
@@ -365,6 +364,30 @@ static bool find_symbol_in_section(const struct symsearch *syms,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+static int cmp_name(const void *va, const void *vb)
|
|
|
+{
|
|
|
+ const char *a;
|
|
|
+ const struct kernel_symbol *b;
|
|
|
+ a = va; b = vb;
|
|
|
+ return strcmp(a, b->name);
|
|
|
+}
|
|
|
+
|
|
|
+static bool find_symbol_in_section(const struct symsearch *syms,
|
|
|
+ struct module *owner,
|
|
|
+ void *data)
|
|
|
+{
|
|
|
+ struct find_symbol_arg *fsa = data;
|
|
|
+ struct kernel_symbol *sym;
|
|
|
+
|
|
|
+ sym = bsearch(fsa->name, syms->start, syms->stop - syms->start,
|
|
|
+ sizeof(struct kernel_symbol), cmp_name);
|
|
|
+
|
|
|
+ if (sym != NULL && check_symbol(syms, owner, sym - syms->start, data))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
/* Find a symbol and return it, along with, (optional) crc and
|
|
|
* (optional) module which owns it. Needs preempt disabled or module_mutex. */
|
|
|
const struct kernel_symbol *find_symbol(const char *name,
|
|
@@ -379,7 +402,7 @@ const struct kernel_symbol *find_symbol(const char *name,
|
|
|
fsa.gplok = gplok;
|
|
|
fsa.warn = warn;
|
|
|
|
|
|
- if (each_symbol(find_symbol_in_section, &fsa)) {
|
|
|
+ if (each_symbol_section(find_symbol_in_section, &fsa)) {
|
|
|
if (owner)
|
|
|
*owner = fsa.owner;
|
|
|
if (crc)
|
|
@@ -1607,27 +1630,28 @@ static void set_section_ro_nx(void *base,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* Setting memory back to RW+NX before releasing it */
|
|
|
-void unset_section_ro_nx(struct module *mod, void *module_region)
|
|
|
+static void unset_module_core_ro_nx(struct module *mod)
|
|
|
{
|
|
|
- unsigned long total_pages;
|
|
|
-
|
|
|
- if (mod->module_core == module_region) {
|
|
|
- /* Set core as NX+RW */
|
|
|
- total_pages = MOD_NUMBER_OF_PAGES(mod->module_core, mod->core_size);
|
|
|
- set_memory_nx((unsigned long)mod->module_core, total_pages);
|
|
|
- set_memory_rw((unsigned long)mod->module_core, total_pages);
|
|
|
+ set_page_attributes(mod->module_core + mod->core_text_size,
|
|
|
+ mod->module_core + mod->core_size,
|
|
|
+ set_memory_x);
|
|
|
+ set_page_attributes(mod->module_core,
|
|
|
+ mod->module_core + mod->core_ro_size,
|
|
|
+ set_memory_rw);
|
|
|
+}
|
|
|
|
|
|
- } else if (mod->module_init == module_region) {
|
|
|
- /* Set init as NX+RW */
|
|
|
- total_pages = MOD_NUMBER_OF_PAGES(mod->module_init, mod->init_size);
|
|
|
- set_memory_nx((unsigned long)mod->module_init, total_pages);
|
|
|
- set_memory_rw((unsigned long)mod->module_init, total_pages);
|
|
|
- }
|
|
|
+static void unset_module_init_ro_nx(struct module *mod)
|
|
|
+{
|
|
|
+ set_page_attributes(mod->module_init + mod->init_text_size,
|
|
|
+ mod->module_init + mod->init_size,
|
|
|
+ set_memory_x);
|
|
|
+ set_page_attributes(mod->module_init,
|
|
|
+ mod->module_init + mod->init_ro_size,
|
|
|
+ set_memory_rw);
|
|
|
}
|
|
|
|
|
|
/* Iterate through all modules and set each module's text as RW */
|
|
|
-void set_all_modules_text_rw()
|
|
|
+void set_all_modules_text_rw(void)
|
|
|
{
|
|
|
struct module *mod;
|
|
|
|
|
@@ -1648,7 +1672,7 @@ void set_all_modules_text_rw()
|
|
|
}
|
|
|
|
|
|
/* Iterate through all modules and set each module's text as RO */
|
|
|
-void set_all_modules_text_ro()
|
|
|
+void set_all_modules_text_ro(void)
|
|
|
{
|
|
|
struct module *mod;
|
|
|
|
|
@@ -1669,7 +1693,8 @@ void set_all_modules_text_ro()
|
|
|
}
|
|
|
#else
|
|
|
static inline void set_section_ro_nx(void *base, unsigned long text_size, unsigned long ro_size, unsigned long total_size) { }
|
|
|
-static inline void unset_section_ro_nx(struct module *mod, void *module_region) { }
|
|
|
+static void unset_module_core_ro_nx(struct module *mod) { }
|
|
|
+static void unset_module_init_ro_nx(struct module *mod) { }
|
|
|
#endif
|
|
|
|
|
|
/* Free a module, remove from lists, etc. */
|
|
@@ -1696,7 +1721,7 @@ static void free_module(struct module *mod)
|
|
|
destroy_params(mod->kp, mod->num_kp);
|
|
|
|
|
|
/* This may be NULL, but that's OK */
|
|
|
- unset_section_ro_nx(mod, mod->module_init);
|
|
|
+ unset_module_init_ro_nx(mod);
|
|
|
module_free(mod, mod->module_init);
|
|
|
kfree(mod->args);
|
|
|
percpu_modfree(mod);
|
|
@@ -1705,7 +1730,7 @@ static void free_module(struct module *mod)
|
|
|
lockdep_free_key_range(mod->module_core, mod->core_size);
|
|
|
|
|
|
/* Finally, free the core (containing the module structure) */
|
|
|
- unset_section_ro_nx(mod, mod->module_core);
|
|
|
+ unset_module_core_ro_nx(mod);
|
|
|
module_free(mod, mod->module_core);
|
|
|
|
|
|
#ifdef CONFIG_MPU
|
|
@@ -2030,11 +2055,8 @@ static const struct kernel_symbol *lookup_symbol(const char *name,
|
|
|
const struct kernel_symbol *start,
|
|
|
const struct kernel_symbol *stop)
|
|
|
{
|
|
|
- const struct kernel_symbol *ks = start;
|
|
|
- for (; ks < stop; ks++)
|
|
|
- if (strcmp(ks->name, name) == 0)
|
|
|
- return ks;
|
|
|
- return NULL;
|
|
|
+ return bsearch(name, start, stop - start,
|
|
|
+ sizeof(struct kernel_symbol), cmp_name);
|
|
|
}
|
|
|
|
|
|
static int is_exported(const char *name, unsigned long value,
|
|
@@ -2931,10 +2953,11 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
|
|
|
mod->symtab = mod->core_symtab;
|
|
|
mod->strtab = mod->core_strtab;
|
|
|
#endif
|
|
|
- unset_section_ro_nx(mod, mod->module_init);
|
|
|
+ unset_module_init_ro_nx(mod);
|
|
|
module_free(mod, mod->module_init);
|
|
|
mod->module_init = NULL;
|
|
|
mod->init_size = 0;
|
|
|
+ mod->init_ro_size = 0;
|
|
|
mod->init_text_size = 0;
|
|
|
mutex_unlock(&module_mutex);
|
|
|
|