Skip to content

Commit 84e1c6b

Browse files
mat-cIngo Molnar
authored andcommitted
x86: Add RO/NX protection for loadable kernel modules
This patch is a logical extension of the protection provided by CONFIG_DEBUG_RODATA to LKMs. The protection is provided by splitting module_core and module_init into three logical parts each and setting appropriate page access permissions for each individual section: 1. Code: RO+X 2. RO data: RO+NX 3. RW data: RW+NX In order to achieve proper protection, layout_sections() have been modified to align each of the three parts mentioned above onto page boundary. Next, the corresponding page access permissions are set right before successful exit from load_module(). Further, free_module() and sys_init_module have been modified to set module_core and module_init as RW+NX right before calling module_free(). By default, the original section layout and access flags are preserved. When compiled with CONFIG_DEBUG_SET_MODULE_RONX=y, the patch will page-align each group of sections to ensure that each page contains only one type of content and will enforce RO/NX for each group of pages. -v1: Initial proof-of-concept patch. -v2: The patch have been re-written to reduce the number of #ifdefs and to make it architecture-agnostic. Code formatting has also been corrected. -v3: Opportunistic RO/NX protection is now unconditional. Section page-alignment is enabled when CONFIG_DEBUG_RODATA=y. -v4: Removed most macros and improved coding style. -v5: Changed page-alignment and RO/NX section size calculation -v6: Fixed comments. Restricted RO/NX enforcement to x86 only -v7: Introduced CONFIG_DEBUG_SET_MODULE_RONX, added calls to set_all_modules_text_rw() and set_all_modules_text_ro() in ftrace -v8: updated for compatibility with linux 2.6.33-rc5 -v9: coding style fixes -v10: more coding style fixes -v11: minor adjustments for -tip -v12: minor adjustments for v2.6.35-rc2-tip -v13: minor adjustments for v2.6.37-rc1-tip Signed-off-by: Siarhei Liakh <[email protected]> Signed-off-by: Xuxian Jiang <[email protected]> Acked-by: Arjan van de Ven <[email protected]> Reviewed-by: James Morris <[email protected]> Signed-off-by: H. Peter Anvin <[email protected]> Cc: Andi Kleen <[email protected]> Cc: Rusty Russell <[email protected]> Cc: Stephen Rothwell <[email protected]> Cc: Dave Jones <[email protected]> Cc: Kees Cook <[email protected]> Cc: Linus Torvalds <[email protected]> LKML-Reference: <[email protected]> [ minor cleanliness edits, -v14: build failure fix ] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 5bd5a45 commit 84e1c6b

File tree

4 files changed

+193
-3
lines changed

4 files changed

+193
-3
lines changed

arch/x86/Kconfig.debug

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,17 @@ config DEBUG_RODATA_TEST
117117
feature as well as for the change_page_attr() infrastructure.
118118
If in doubt, say "N"
119119

120+
config DEBUG_SET_MODULE_RONX
121+
bool "Set loadable kernel module data as NX and text as RO"
122+
depends on MODULES
123+
---help---
124+
This option helps catch unintended modifications to loadable
125+
kernel module's text and read-only data. It also prevents execution
126+
of module data. Such protection may interfere with run-time code
127+
patching and dynamic kernel tracing - and they might also protect
128+
against certain classes of kernel exploits.
129+
If in doubt, say "N".
130+
120131
config DEBUG_NX_TEST
121132
tristate "Testcase for the NX non-executable stack feature"
122133
depends on DEBUG_KERNEL && m

arch/x86/kernel/ftrace.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/sched.h>
2020
#include <linux/init.h>
2121
#include <linux/list.h>
22+
#include <linux/module.h>
2223

2324
#include <trace/syscall.h>
2425

@@ -49,13 +50,15 @@ static DEFINE_PER_CPU(int, save_modifying_code);
4950
int ftrace_arch_code_modify_prepare(void)
5051
{
5152
set_kernel_text_rw();
53+
set_all_modules_text_rw();
5254
modifying_code = 1;
5355
return 0;
5456
}
5557

5658
int ftrace_arch_code_modify_post_process(void)
5759
{
5860
modifying_code = 0;
61+
set_all_modules_text_ro();
5962
set_kernel_text_ro();
6063
return 0;
6164
}

include/linux/module.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ struct module
308308
/* The size of the executable code in each section. */
309309
unsigned int init_text_size, core_text_size;
310310

311+
/* Size of RO sections of the module (text+rodata) */
312+
unsigned int init_ro_size, core_ro_size;
313+
311314
/* Arch-specific module values */
312315
struct mod_arch_specific arch;
313316

@@ -672,7 +675,6 @@ static inline int module_get_iter_tracepoints(struct tracepoint_iter *iter)
672675
{
673676
return 0;
674677
}
675-
676678
#endif /* CONFIG_MODULES */
677679

678680
#ifdef CONFIG_SYSFS
@@ -687,6 +689,13 @@ extern int module_sysfs_initialized;
687689

688690
#define __MODULE_STRING(x) __stringify(x)
689691

692+
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
693+
extern void set_all_modules_text_rw(void);
694+
extern void set_all_modules_text_ro(void);
695+
#else
696+
static inline void set_all_modules_text_rw(void) { }
697+
static inline void set_all_modules_text_ro(void) { }
698+
#endif
690699

691700
#ifdef CONFIG_GENERIC_BUG
692701
void module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *,

kernel/module.c

Lines changed: 169 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include <linux/percpu.h>
5757
#include <linux/kmemleak.h>
5858
#include <linux/jump_label.h>
59+
#include <linux/pfn.h>
5960

6061
#define CREATE_TRACE_POINTS
6162
#include <trace/events/module.h>
@@ -70,6 +71,26 @@
7071
#define ARCH_SHF_SMALL 0
7172
#endif
7273

74+
/*
75+
* Modules' sections will be aligned on page boundaries
76+
* to ensure complete separation of code and data, but
77+
* only when CONFIG_DEBUG_SET_MODULE_RONX=y
78+
*/
79+
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
80+
# define debug_align(X) ALIGN(X, PAGE_SIZE)
81+
#else
82+
# define debug_align(X) (X)
83+
#endif
84+
85+
/*
86+
* Given BASE and SIZE this macro calculates the number of pages the
87+
* memory regions occupies
88+
*/
89+
#define MOD_NUMBER_OF_PAGES(BASE, SIZE) (((SIZE) > 0) ? \
90+
(PFN_DOWN((unsigned long)(BASE) + (SIZE) - 1) - \
91+
PFN_DOWN((unsigned long)BASE) + 1) \
92+
: (0UL))
93+
7394
/* If this is set, the section belongs in the init part of the module */
7495
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
7596

@@ -1542,6 +1563,115 @@ static int __unlink_module(void *_mod)
15421563
return 0;
15431564
}
15441565

1566+
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
1567+
/*
1568+
* LKM RO/NX protection: protect module's text/ro-data
1569+
* from modification and any data from execution.
1570+
*/
1571+
void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
1572+
{
1573+
unsigned long begin_pfn = PFN_DOWN((unsigned long)start);
1574+
unsigned long end_pfn = PFN_DOWN((unsigned long)end);
1575+
1576+
if (end_pfn > begin_pfn)
1577+
set(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn);
1578+
}
1579+
1580+
static void set_section_ro_nx(void *base,
1581+
unsigned long text_size,
1582+
unsigned long ro_size,
1583+
unsigned long total_size)
1584+
{
1585+
/* begin and end PFNs of the current subsection */
1586+
unsigned long begin_pfn;
1587+
unsigned long end_pfn;
1588+
1589+
/*
1590+
* Set RO for module text and RO-data:
1591+
* - Always protect first page.
1592+
* - Do not protect last partial page.
1593+
*/
1594+
if (ro_size > 0)
1595+
set_page_attributes(base, base + ro_size, set_memory_ro);
1596+
1597+
/*
1598+
* Set NX permissions for module data:
1599+
* - Do not protect first partial page.
1600+
* - Always protect last page.
1601+
*/
1602+
if (total_size > text_size) {
1603+
begin_pfn = PFN_UP((unsigned long)base + text_size);
1604+
end_pfn = PFN_UP((unsigned long)base + total_size);
1605+
if (end_pfn > begin_pfn)
1606+
set_memory_nx(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn);
1607+
}
1608+
}
1609+
1610+
/* Setting memory back to RW+NX before releasing it */
1611+
void unset_section_ro_nx(struct module *mod, void *module_region)
1612+
{
1613+
unsigned long total_pages;
1614+
1615+
if (mod->module_core == module_region) {
1616+
/* Set core as NX+RW */
1617+
total_pages = MOD_NUMBER_OF_PAGES(mod->module_core, mod->core_size);
1618+
set_memory_nx((unsigned long)mod->module_core, total_pages);
1619+
set_memory_rw((unsigned long)mod->module_core, total_pages);
1620+
1621+
} else if (mod->module_init == module_region) {
1622+
/* Set init as NX+RW */
1623+
total_pages = MOD_NUMBER_OF_PAGES(mod->module_init, mod->init_size);
1624+
set_memory_nx((unsigned long)mod->module_init, total_pages);
1625+
set_memory_rw((unsigned long)mod->module_init, total_pages);
1626+
}
1627+
}
1628+
1629+
/* Iterate through all modules and set each module's text as RW */
1630+
void set_all_modules_text_rw()
1631+
{
1632+
struct module *mod;
1633+
1634+
mutex_lock(&module_mutex);
1635+
list_for_each_entry_rcu(mod, &modules, list) {
1636+
if ((mod->module_core) && (mod->core_text_size)) {
1637+
set_page_attributes(mod->module_core,
1638+
mod->module_core + mod->core_text_size,
1639+
set_memory_rw);
1640+
}
1641+
if ((mod->module_init) && (mod->init_text_size)) {
1642+
set_page_attributes(mod->module_init,
1643+
mod->module_init + mod->init_text_size,
1644+
set_memory_rw);
1645+
}
1646+
}
1647+
mutex_unlock(&module_mutex);
1648+
}
1649+
1650+
/* Iterate through all modules and set each module's text as RO */
1651+
void set_all_modules_text_ro()
1652+
{
1653+
struct module *mod;
1654+
1655+
mutex_lock(&module_mutex);
1656+
list_for_each_entry_rcu(mod, &modules, list) {
1657+
if ((mod->module_core) && (mod->core_text_size)) {
1658+
set_page_attributes(mod->module_core,
1659+
mod->module_core + mod->core_text_size,
1660+
set_memory_ro);
1661+
}
1662+
if ((mod->module_init) && (mod->init_text_size)) {
1663+
set_page_attributes(mod->module_init,
1664+
mod->module_init + mod->init_text_size,
1665+
set_memory_ro);
1666+
}
1667+
}
1668+
mutex_unlock(&module_mutex);
1669+
}
1670+
#else
1671+
static inline void set_section_ro_nx(void *base, unsigned long text_size, unsigned long ro_size, unsigned long total_size) { }
1672+
static inline void unset_section_ro_nx(struct module *mod, void *module_region) { }
1673+
#endif
1674+
15451675
/* Free a module, remove from lists, etc. */
15461676
static void free_module(struct module *mod)
15471677
{
@@ -1566,6 +1696,7 @@ static void free_module(struct module *mod)
15661696
destroy_params(mod->kp, mod->num_kp);
15671697

15681698
/* This may be NULL, but that's OK */
1699+
unset_section_ro_nx(mod, mod->module_init);
15691700
module_free(mod, mod->module_init);
15701701
kfree(mod->args);
15711702
percpu_modfree(mod);
@@ -1574,6 +1705,7 @@ static void free_module(struct module *mod)
15741705
lockdep_free_key_range(mod->module_core, mod->core_size);
15751706

15761707
/* Finally, free the core (containing the module structure) */
1708+
unset_section_ro_nx(mod, mod->module_core);
15771709
module_free(mod, mod->module_core);
15781710

15791711
#ifdef CONFIG_MPU
@@ -1777,8 +1909,19 @@ static void layout_sections(struct module *mod, struct load_info *info)
17771909
s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
17781910
DEBUGP("\t%s\n", name);
17791911
}
1780-
if (m == 0)
1912+
switch (m) {
1913+
case 0: /* executable */
1914+
mod->core_size = debug_align(mod->core_size);
17811915
mod->core_text_size = mod->core_size;
1916+
break;
1917+
case 1: /* RO: text and ro-data */
1918+
mod->core_size = debug_align(mod->core_size);
1919+
mod->core_ro_size = mod->core_size;
1920+
break;
1921+
case 3: /* whole core */
1922+
mod->core_size = debug_align(mod->core_size);
1923+
break;
1924+
}
17821925
}
17831926

17841927
DEBUGP("Init section allocation order:\n");
@@ -1796,8 +1939,19 @@ static void layout_sections(struct module *mod, struct load_info *info)
17961939
| INIT_OFFSET_MASK);
17971940
DEBUGP("\t%s\n", sname);
17981941
}
1799-
if (m == 0)
1942+
switch (m) {
1943+
case 0: /* executable */
1944+
mod->init_size = debug_align(mod->init_size);
18001945
mod->init_text_size = mod->init_size;
1946+
break;
1947+
case 1: /* RO: text and ro-data */
1948+
mod->init_size = debug_align(mod->init_size);
1949+
mod->init_ro_size = mod->init_size;
1950+
break;
1951+
case 3: /* whole init */
1952+
mod->init_size = debug_align(mod->init_size);
1953+
break;
1954+
}
18011955
}
18021956
}
18031957

@@ -2650,6 +2804,18 @@ static struct module *load_module(void __user *umod,
26502804
kfree(info.strmap);
26512805
free_copy(&info);
26522806

2807+
/* Set RO and NX regions for core */
2808+
set_section_ro_nx(mod->module_core,
2809+
mod->core_text_size,
2810+
mod->core_ro_size,
2811+
mod->core_size);
2812+
2813+
/* Set RO and NX regions for init */
2814+
set_section_ro_nx(mod->module_init,
2815+
mod->init_text_size,
2816+
mod->init_ro_size,
2817+
mod->init_size);
2818+
26532819
/* Done! */
26542820
trace_module_load(mod);
26552821
return mod;
@@ -2753,6 +2919,7 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
27532919
mod->symtab = mod->core_symtab;
27542920
mod->strtab = mod->core_strtab;
27552921
#endif
2922+
unset_section_ro_nx(mod, mod->module_init);
27562923
module_free(mod, mod->module_init);
27572924
mod->module_init = NULL;
27582925
mod->init_size = 0;

0 commit comments

Comments
 (0)