Skip to contentSkip to conversation

RO/NX protection for loadable kernel modules

PATCH Applied Siarhei Liakh 4 participants 7 messages Feb 1, 2010 – Oct 20, 2010

Subsystems (4)

v10 of 12 Applied Latest is v12 →

lore
Share & tools
lore
#0 lore raw

PATCH v10 1/1 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 have been corrected also.
V3: Opportunistic RO/NX protectiuon 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

The patch have been developed for Linux 2.6.33-rc5 by Siarhei Liakh <[email protected]> and Xuxian Jiang <[email protected]>.

---

arch/x86/Kconfig.debug +11 -0
index bc01e3e..7c80e28 100644
@@ -115,6 +115,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"
+	default n
+	depends on X86 && MODULES
+	---help---
+	  This option helps to catch unintended modifications to loadable
+	  kernel module's text and read-only data. It also prevents execution
+	  of LKM's data. Such protection may interfere with run-time code
+	  patching and dynamic kernel tracing.
+	  If in doubt, say "N".
+
 config DEBUG_NX_TEST
 	tristate "Testcase for the NX non-executable stack feature"
 	depends on DEBUG_KERNEL && m
arch/x86/kernel/ftrace.c +3 -1
index 3096892..b47a154 100644
@@ -19,6 +19,7 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/list.h>
+#include <linux/module.h>
 
 #include <trace/syscall.h>
 
@@ -29,16 +30,17 @@
 
 
 #ifdef CONFIG_DYNAMIC_FTRACE
-
 int ftrace_arch_code_modify_prepare(void)
 {
 	set_kernel_text_rw();
+	set_all_modules_text_rw();
 	return 0;
 }
 
 int ftrace_arch_code_modify_post_process(void)
 {
 	set_kernel_text_ro();
+	set_all_modules_text_ro();
 	return 0;
 }
 
include/linux/module.h +6 -0
index 6cb1a3c..c6040ce 100644
@@ -300,6 +300,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;
 
@@ -542,6 +545,9 @@ extern void print_modules(void);
 extern void module_update_tracepoints(void);
 extern int module_get_iter_tracepoints(struct tracepoint_iter *iter);
 
+void set_all_modules_text_rw(void);
+void set_all_modules_text_ro(void);
+
 #else /* !CONFIG_MODULES... */
 #define EXPORT_SYMBOL(sym)
 #define EXPORT_SYMBOL_GPL(sym)
@@ -662,6 +668,13 @@ static inline int

module_get_iter_tracepoints(struct tracepoint_iter *iter)
return 0;
}

+static inline void set_all_modules_text_rw()
+{
+}
+
+static inline void set_all_modules_text_ro()
+{
+}
#endif /* CONFIG_MODULES */

struct device_driver;

kernel/module.c +167 -2

213 lines not on this page · raw message

__user *umod,

trace_module_load(mod);

+ /* 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! */
return mod;

@@ -2504,6 +2681,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;

#1 lore raw
Quoted 8 lines · Siarhei Liakh

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

Thanks, applied!

Cheers,
Rusty.

#2 lore raw
Quoted 11 lines · Rusty Russell
Quoted 8 lines · Siarhei Liakh

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

Thanks, applied!

I also applied this to the -tip tree... do you prefer to carry it or should I leave it as is?

-hpa

Acked-by
#3 lore raw
Quoted 7 lines · H. Peter Anvin
Quoted 6 lines · Rusty Russell
Quoted 5 lines · Siarhei Liakh

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:

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines

1. Code: RO+X
2. RO data: RO+NX
3. RW data: RW+NX

Thanks, applied!

I also applied this to the -tip tree... do you prefer to carry it or should I leave it as is?

I'm always happy for you to do my work for me! There are no other module patches likely to conflict.

Cheers,
Rusty.

#4 lore raw

Hi,

Quoted 14 lines · Rusty Russell
Quoted 13 lines · H. Peter Anvin
Quoted 11 lines · Rusty Russell
Quoted 8 lines · Siarhei Liakh

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

Thanks, applied!

Quoted 7 lines
Quoted 2 lines

I also applied this to the -tip tree... do you prefer to carry it or should I leave it as is?

I'm always happy for you to do my work for me! There are no other module patches likely to conflict.

Sorry for losing track, but where did this patch end up? I don't see it in any of the trees I've looked in.

Thanks,

-Kees

Signature · 2 lines
Kees Cook
Ubuntu Security Team
#5 lore raw
Quoted 1 line · Kees Cook

Hi,

...

Quoted 2 lines

Sorry for losing track, but where did this patch end up? I don't see it in any of the trees I've looked in.

Good question... This would be quite nice to have of course.

Rusty.

#6 lore raw

[tip:x86/mm] x86: RO/NX protection for loadable kernel modules

In reply to Siarhei Liakh
Commit-ID:  65187d24fa3ef60f691f847c792e8eaca7e19251
Gitweb:     http://git.kernel.org/tip/65187d24fa3ef60f691f847c792e8eaca7e19251
Author:     Siarhei Liakh <[email protected]>
AuthorDate: Mon, 1 Feb 2010 18:39:53 -0500
Committer:  H. Peter Anvin <[email protected]>
CommitDate: Wed, 17 Feb 2010 10:11:24 -0800

x86: 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 have been corrected also.
V3: Opportunistic RO/NX protectiuon 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

LKML-Reference: <[email protected]>

---
 arch/x86/Kconfig.debug   |   11 +++
 arch/x86/kernel/ftrace.c |    4 +-
 include/linux/module.h   |   13 +++
 kernel/module.c          |  182 +++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 207 insertions(+), 3 deletions(-)
arch/x86/Kconfig.debug +11 -0
index bc01e3e..7c80e28 100644
@@ -115,6 +115,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"
+	default n
+	depends on X86 && MODULES
+	---help---
+	  This option helps to catch unintended modifications to loadable
+	  kernel module's text and read-only data. It also prevents execution
+	  of LKM's data. Such protection may interfere with run-time code
+	  patching and dynamic kernel tracing.
+	  If in doubt, say "N".
+
 config DEBUG_NX_TEST
 	tristate "Testcase for the NX non-executable stack feature"
 	depends on DEBUG_KERNEL && m
arch/x86/kernel/ftrace.c +3 -1
index 3096892..b47a154 100644
@@ -19,6 +19,7 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/list.h>
+#include <linux/module.h>
 
 #include <trace/syscall.h>
 
@@ -29,16 +30,17 @@
 
 
 #ifdef CONFIG_DYNAMIC_FTRACE
-
 int ftrace_arch_code_modify_prepare(void)
 {
 	set_kernel_text_rw();
+	set_all_modules_text_rw();
 	return 0;
 }
 
 int ftrace_arch_code_modify_post_process(void)
 {
 	set_kernel_text_ro();
+	set_all_modules_text_ro();
 	return 0;
 }
 
include/linux/module.h +13 -0
index 6cb1a3c..c6040ce 100644
@@ -300,6 +300,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;
 
@@ -542,6 +545,9 @@ extern void print_modules(void);
 extern void module_update_tracepoints(void);
 extern int module_get_iter_tracepoints(struct tracepoint_iter *iter);
 
+void set_all_modules_text_rw(void);
+void set_all_modules_text_ro(void);
+
 #else /* !CONFIG_MODULES... */
 #define EXPORT_SYMBOL(sym)
 #define EXPORT_SYMBOL_GPL(sym)
@@ -662,6 +668,13 @@ static inline int module_get_iter_tracepoints(struct tracepoint_iter *iter)
 	return 0;
 }
 
+static inline void set_all_modules_text_rw()
+{
+}
+
+static inline void set_all_modules_text_ro()
+{
+}
 #endif /* CONFIG_MODULES */
 
 struct device_driver;
kernel/module.c +180 -2

238 lines not on this page · raw message