Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 224087
b: refs/heads/master
c: 84e1c6b
h: refs/heads/master
i:
  224085: 98f9cc3
  224083: 893c2a4
  224079: db0b23a
v: v3
  • Loading branch information
matthieu castet authored and Ingo Molnar committed Nov 18, 2010
1 parent e46209b commit 54dc90b
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 4 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 5bd5a452662bc37c54fb6828db1a3faf87e6511c
refs/heads/master: 84e1c6bb38eb318e456558b610396d9f1afaabf0
11 changes: 11 additions & 0 deletions trunk/arch/x86/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ config DEBUG_RODATA_TEST
feature as well as for the change_page_attr() infrastructure.
If in doubt, say "N"

config DEBUG_SET_MODULE_RONX
bool "Set loadable kernel module data as NX and text as RO"
depends on MODULES
---help---
This option helps catch unintended modifications to loadable
kernel module's text and read-only data. It also prevents execution
of module data. Such protection may interfere with run-time code
patching and dynamic kernel tracing - and they might also protect
against certain classes of kernel exploits.
If in doubt, say "N".

config DEBUG_NX_TEST
tristate "Testcase for the NX non-executable stack feature"
depends on DEBUG_KERNEL && m
Expand Down
3 changes: 3 additions & 0 deletions trunk/arch/x86/kernel/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>

#include <trace/syscall.h>

Expand Down Expand Up @@ -49,13 +50,15 @@ static DEFINE_PER_CPU(int, save_modifying_code);
int ftrace_arch_code_modify_prepare(void)
{
set_kernel_text_rw();
set_all_modules_text_rw();
modifying_code = 1;
return 0;
}

int ftrace_arch_code_modify_post_process(void)
{
modifying_code = 0;
set_all_modules_text_ro();
set_kernel_text_ro();
return 0;
}
Expand Down
11 changes: 10 additions & 1 deletion trunk/include/linux/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ struct module
/* The size of the executable code in each section. */
unsigned int init_text_size, core_text_size;

/* Size of RO sections of the module (text+rodata) */
unsigned int init_ro_size, core_ro_size;

/* Arch-specific module values */
struct mod_arch_specific arch;

Expand Down Expand Up @@ -672,7 +675,6 @@ static inline int module_get_iter_tracepoints(struct tracepoint_iter *iter)
{
return 0;
}

#endif /* CONFIG_MODULES */

#ifdef CONFIG_SYSFS
Expand All @@ -687,6 +689,13 @@ extern int module_sysfs_initialized;

#define __MODULE_STRING(x) __stringify(x)

#ifdef CONFIG_DEBUG_SET_MODULE_RONX
extern void set_all_modules_text_rw(void);
extern void set_all_modules_text_ro(void);
#else
static inline void set_all_modules_text_rw(void) { }
static inline void set_all_modules_text_ro(void) { }
#endif

#ifdef CONFIG_GENERIC_BUG
void module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *,
Expand Down
171 changes: 169 additions & 2 deletions trunk/kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <linux/percpu.h>
#include <linux/kmemleak.h>
#include <linux/jump_label.h>
#include <linux/pfn.h>

#define CREATE_TRACE_POINTS
#include <trace/events/module.h>
Expand All @@ -70,6 +71,26 @@
#define ARCH_SHF_SMALL 0
#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 */
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))

Expand Down Expand Up @@ -1542,6 +1563,115 @@ static int __unlink_module(void *_mod)
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. */
static void free_module(struct module *mod)
{
Expand All @@ -1566,6 +1696,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);
module_free(mod, mod->module_init);
kfree(mod->args);
percpu_modfree(mod);
Expand All @@ -1574,6 +1705,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);
module_free(mod, mod->module_core);

#ifdef CONFIG_MPU
Expand Down Expand Up @@ -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);
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;
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");
Expand All @@ -1796,8 +1939,19 @@ static void layout_sections(struct module *mod, struct load_info *info)
| INIT_OFFSET_MASK);
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;
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;
}
}
}

Expand Down Expand Up @@ -2650,6 +2804,18 @@ static struct module *load_module(void __user *umod,
kfree(info.strmap);
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! */
trace_module_load(mod);
return mod;
Expand Down Expand Up @@ -2753,6 +2919,7 @@ 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);
module_free(mod, mod->module_init);
mod->module_init = NULL;
mod->init_size = 0;
Expand Down

0 comments on commit 54dc90b

Please sign in to comment.