|
@@ -56,6 +56,7 @@
|
|
#include <linux/percpu.h>
|
|
#include <linux/percpu.h>
|
|
#include <linux/kmemleak.h>
|
|
#include <linux/kmemleak.h>
|
|
#include <linux/jump_label.h>
|
|
#include <linux/jump_label.h>
|
|
|
|
+#include <linux/pfn.h>
|
|
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/module.h>
|
|
#include <trace/events/module.h>
|
|
@@ -70,6 +71,26 @@
|
|
#define ARCH_SHF_SMALL 0
|
|
#define ARCH_SHF_SMALL 0
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Modules' sections will be aligned on page boundaries
|
|
|
|
+ * to ensure complete separation of code and data, but
|
|
|
|
+ * only when CONFIG_DEBUG_SET_MODULE_RONX=y
|
|
|
|
+ */
|
|
|
|
+#ifdef CONFIG_DEBUG_SET_MODULE_RONX
|
|
|
|
+# define debug_align(X) ALIGN(X, PAGE_SIZE)
|
|
|
|
+#else
|
|
|
|
+# define debug_align(X) (X)
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Given BASE and SIZE this macro calculates the number of pages the
|
|
|
|
+ * memory regions occupies
|
|
|
|
+ */
|
|
|
|
+#define MOD_NUMBER_OF_PAGES(BASE, SIZE) (((SIZE) > 0) ? \
|
|
|
|
+ (PFN_DOWN((unsigned long)(BASE) + (SIZE) - 1) - \
|
|
|
|
+ PFN_DOWN((unsigned long)BASE) + 1) \
|
|
|
|
+ : (0UL))
|
|
|
|
+
|
|
/* If this is set, the section belongs in the init part of the module */
|
|
/* If this is set, the section belongs in the init part of the module */
|
|
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
|
|
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
|
|
|
|
|
|
@@ -1542,6 +1563,115 @@ static int __unlink_module(void *_mod)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef CONFIG_DEBUG_SET_MODULE_RONX
|
|
|
|
+/*
|
|
|
|
+ * LKM RO/NX protection: protect module's text/ro-data
|
|
|
|
+ * from modification and any data from execution.
|
|
|
|
+ */
|
|
|
|
+void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
|
|
|
|
+{
|
|
|
|
+ unsigned long begin_pfn = PFN_DOWN((unsigned long)start);
|
|
|
|
+ unsigned long end_pfn = PFN_DOWN((unsigned long)end);
|
|
|
|
+
|
|
|
|
+ if (end_pfn > begin_pfn)
|
|
|
|
+ set(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void set_section_ro_nx(void *base,
|
|
|
|
+ unsigned long text_size,
|
|
|
|
+ unsigned long ro_size,
|
|
|
|
+ unsigned long total_size)
|
|
|
|
+{
|
|
|
|
+ /* begin and end PFNs of the current subsection */
|
|
|
|
+ unsigned long begin_pfn;
|
|
|
|
+ unsigned long end_pfn;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Set RO for module text and RO-data:
|
|
|
|
+ * - Always protect first page.
|
|
|
|
+ * - Do not protect last partial page.
|
|
|
|
+ */
|
|
|
|
+ if (ro_size > 0)
|
|
|
|
+ set_page_attributes(base, base + ro_size, set_memory_ro);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Set NX permissions for module data:
|
|
|
|
+ * - Do not protect first partial page.
|
|
|
|
+ * - Always protect last page.
|
|
|
|
+ */
|
|
|
|
+ if (total_size > text_size) {
|
|
|
|
+ begin_pfn = PFN_UP((unsigned long)base + text_size);
|
|
|
|
+ end_pfn = PFN_UP((unsigned long)base + total_size);
|
|
|
|
+ if (end_pfn > begin_pfn)
|
|
|
|
+ set_memory_nx(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Setting memory back to RW+NX before releasing it */
|
|
|
|
+void unset_section_ro_nx(struct module *mod, void *module_region)
|
|
|
|
+{
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ } 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);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Iterate through all modules and set each module's text as RW */
|
|
|
|
+void set_all_modules_text_rw()
|
|
|
|
+{
|
|
|
|
+ struct module *mod;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&module_mutex);
|
|
|
|
+ list_for_each_entry_rcu(mod, &modules, list) {
|
|
|
|
+ if ((mod->module_core) && (mod->core_text_size)) {
|
|
|
|
+ set_page_attributes(mod->module_core,
|
|
|
|
+ mod->module_core + mod->core_text_size,
|
|
|
|
+ set_memory_rw);
|
|
|
|
+ }
|
|
|
|
+ if ((mod->module_init) && (mod->init_text_size)) {
|
|
|
|
+ set_page_attributes(mod->module_init,
|
|
|
|
+ mod->module_init + mod->init_text_size,
|
|
|
|
+ set_memory_rw);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&module_mutex);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Iterate through all modules and set each module's text as RO */
|
|
|
|
+void set_all_modules_text_ro()
|
|
|
|
+{
|
|
|
|
+ struct module *mod;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&module_mutex);
|
|
|
|
+ list_for_each_entry_rcu(mod, &modules, list) {
|
|
|
|
+ if ((mod->module_core) && (mod->core_text_size)) {
|
|
|
|
+ set_page_attributes(mod->module_core,
|
|
|
|
+ mod->module_core + mod->core_text_size,
|
|
|
|
+ set_memory_ro);
|
|
|
|
+ }
|
|
|
|
+ if ((mod->module_init) && (mod->init_text_size)) {
|
|
|
|
+ set_page_attributes(mod->module_init,
|
|
|
|
+ mod->module_init + mod->init_text_size,
|
|
|
|
+ set_memory_ro);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&module_mutex);
|
|
|
|
+}
|
|
|
|
+#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) { }
|
|
|
|
+#endif
|
|
|
|
+
|
|
/* Free a module, remove from lists, etc. */
|
|
/* Free a module, remove from lists, etc. */
|
|
static void free_module(struct module *mod)
|
|
static void free_module(struct module *mod)
|
|
{
|
|
{
|
|
@@ -1566,6 +1696,7 @@ static void free_module(struct module *mod)
|
|
destroy_params(mod->kp, mod->num_kp);
|
|
destroy_params(mod->kp, mod->num_kp);
|
|
|
|
|
|
/* This may be NULL, but that's OK */
|
|
/* This may be NULL, but that's OK */
|
|
|
|
+ unset_section_ro_nx(mod, mod->module_init);
|
|
module_free(mod, mod->module_init);
|
|
module_free(mod, mod->module_init);
|
|
kfree(mod->args);
|
|
kfree(mod->args);
|
|
percpu_modfree(mod);
|
|
percpu_modfree(mod);
|
|
@@ -1574,6 +1705,7 @@ static void free_module(struct module *mod)
|
|
lockdep_free_key_range(mod->module_core, mod->core_size);
|
|
lockdep_free_key_range(mod->module_core, mod->core_size);
|
|
|
|
|
|
/* Finally, free the core (containing the module structure) */
|
|
/* Finally, free the core (containing the module structure) */
|
|
|
|
+ unset_section_ro_nx(mod, mod->module_core);
|
|
module_free(mod, mod->module_core);
|
|
module_free(mod, mod->module_core);
|
|
|
|
|
|
#ifdef CONFIG_MPU
|
|
#ifdef CONFIG_MPU
|
|
@@ -1777,8 +1909,19 @@ static void layout_sections(struct module *mod, struct load_info *info)
|
|
s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
|
|
s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
|
|
DEBUGP("\t%s\n", name);
|
|
DEBUGP("\t%s\n", name);
|
|
}
|
|
}
|
|
- if (m == 0)
|
|
|
|
|
|
+ switch (m) {
|
|
|
|
+ case 0: /* executable */
|
|
|
|
+ mod->core_size = debug_align(mod->core_size);
|
|
mod->core_text_size = mod->core_size;
|
|
mod->core_text_size = mod->core_size;
|
|
|
|
+ break;
|
|
|
|
+ case 1: /* RO: text and ro-data */
|
|
|
|
+ mod->core_size = debug_align(mod->core_size);
|
|
|
|
+ mod->core_ro_size = mod->core_size;
|
|
|
|
+ break;
|
|
|
|
+ case 3: /* whole core */
|
|
|
|
+ mod->core_size = debug_align(mod->core_size);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
DEBUGP("Init section allocation order:\n");
|
|
DEBUGP("Init section allocation order:\n");
|
|
@@ -1796,8 +1939,19 @@ static void layout_sections(struct module *mod, struct load_info *info)
|
|
| INIT_OFFSET_MASK);
|
|
| INIT_OFFSET_MASK);
|
|
DEBUGP("\t%s\n", sname);
|
|
DEBUGP("\t%s\n", sname);
|
|
}
|
|
}
|
|
- if (m == 0)
|
|
|
|
|
|
+ switch (m) {
|
|
|
|
+ case 0: /* executable */
|
|
|
|
+ mod->init_size = debug_align(mod->init_size);
|
|
mod->init_text_size = mod->init_size;
|
|
mod->init_text_size = mod->init_size;
|
|
|
|
+ break;
|
|
|
|
+ case 1: /* RO: text and ro-data */
|
|
|
|
+ mod->init_size = debug_align(mod->init_size);
|
|
|
|
+ mod->init_ro_size = mod->init_size;
|
|
|
|
+ break;
|
|
|
|
+ case 3: /* whole init */
|
|
|
|
+ mod->init_size = debug_align(mod->init_size);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2650,6 +2804,18 @@ static struct module *load_module(void __user *umod,
|
|
kfree(info.strmap);
|
|
kfree(info.strmap);
|
|
free_copy(&info);
|
|
free_copy(&info);
|
|
|
|
|
|
|
|
+ /* Set RO and NX regions for core */
|
|
|
|
+ set_section_ro_nx(mod->module_core,
|
|
|
|
+ mod->core_text_size,
|
|
|
|
+ mod->core_ro_size,
|
|
|
|
+ mod->core_size);
|
|
|
|
+
|
|
|
|
+ /* Set RO and NX regions for init */
|
|
|
|
+ set_section_ro_nx(mod->module_init,
|
|
|
|
+ mod->init_text_size,
|
|
|
|
+ mod->init_ro_size,
|
|
|
|
+ mod->init_size);
|
|
|
|
+
|
|
/* Done! */
|
|
/* Done! */
|
|
trace_module_load(mod);
|
|
trace_module_load(mod);
|
|
return mod;
|
|
return mod;
|
|
@@ -2753,6 +2919,7 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
|
|
mod->symtab = mod->core_symtab;
|
|
mod->symtab = mod->core_symtab;
|
|
mod->strtab = mod->core_strtab;
|
|
mod->strtab = mod->core_strtab;
|
|
#endif
|
|
#endif
|
|
|
|
+ unset_section_ro_nx(mod, mod->module_init);
|
|
module_free(mod, mod->module_init);
|
|
module_free(mod, mod->module_init);
|
|
mod->module_init = NULL;
|
|
mod->module_init = NULL;
|
|
mod->init_size = 0;
|
|
mod->init_size = 0;
|