linux-next.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6] RO/NX protection for loadable kernel modules
@ 2009-09-11  2:50 Siarhei Liakh
  2009-09-21 21:40 ` Dave Jones
  2009-09-21 23:59 ` Rusty Russell
  0 siblings, 2 replies; 6+ messages in thread
From: Siarhei Liakh @ 2009-09-11  2:50 UTC (permalink / raw)
  To: linux-kernel, linux-security-module, linux-next
  Cc: Arjan van de Ven, James Morris, Andrew Morton, Andi Kleen,
	Thomas Gleixner, H. Peter Anvin, Ingo Molnar, Rusty Russell,
	Stephen Rothwell

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 is preserved and RO/NX is
enforced only for whole pages of same content.
However, when compiled with CONFIG_DEBUG_RODATA=y, the patch
will page-align each group of sections to ensure that each page contains
only one type of content.
RO/NX enforcement is active on x86 only.

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

The patch have been developed for Linux 2.6.30 by Siarhei Liakh
<sliakh.lkml@gmail.com> and Xuxian Jiang <jiang@cs.ncsu.edu>.

---

Signed-off-by: Siarhei Liakh <sliakh.lkml@gmail.com>
Signed-off-by: Xuxian Jiang <jiang@cs.ncsu.edu>
Acked-by: Arjan van de Ven <arjan@linux.intel.com>

diff --git a/include/linux/module.h b/include/linux/module.h
index 627ac08..5ba770e 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -293,6 +293,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;

diff --git a/kernel/module.c b/kernel/module.c
index e797812..660278a 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -52,6 +52,7 @@
 #include <linux/ftrace.h>
 #include <linux/async.h>
 #include <linux/percpu.h>
+#include <linux/pfn.h>

 #if 0
 #define DEBUGP printk
@@ -63,6 +64,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_RODATA=y
+ */
+#ifdef CONFIG_DEBUG_RODATA
+#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 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))

@@ -1471,6 +1492,69 @@ static int __unlink_module(void *_mod)
 	return 0;
 }

+/*
+ * LKM RO/NX protection: protect module's text/ro-data
+ * from modification and any data from execution.
+ */
+static void set_section_ro_nx(void *base,
+			unsigned long text_size,
+			unsigned long ro_size,
+			unsigned long total_size)
+{
+#ifdef CONFIG_X86
+	/* 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) {
+		begin_pfn = PFN_DOWN((unsigned long)base);
+		end_pfn = PFN_DOWN((unsigned long)base + ro_size);
+		if (end_pfn > begin_pfn)
+			set_memory_ro(begin_pfn << PAGE_SHIFT,
+						end_pfn - begin_pfn);
+	}
+
+	/*
+	 * 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);
+	}
+#endif
+}
+
+/* Setting memory back to RW+NX before releasing it */
+void unset_section_ro_nx(struct module *mod, void *module_region)
+{
+#ifdef CONFIG_X86
+	unsigned long total_pages;
+
+	if (mod->module_core == module_region) {
+		/* Set core as NX+RW */
+		total_pages = 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 = 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);
+	}
+#endif
+}
+
 /* Free a module, remove from lists, etc (must hold module_mutex). */
 static void free_module(struct module *mod)
 {
@@ -1493,6 +1577,7 @@ static void free_module(struct module *mod)
 	ftrace_release(mod->module_core, mod->core_size);

 	/* 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);
 	if (mod->percpu)
@@ -1505,6 +1590,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);
 }

@@ -1678,8 +1764,19 @@ static void layout_sections(struct module *mod,
 			s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
 			DEBUGP("\t%s\n", secstrings + s->sh_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");
@@ -1696,8 +1793,19 @@ static void layout_sections(struct module *mod,
 					 | INIT_OFFSET_MASK);
 			DEBUGP("\t%s\n", secstrings + s->sh_name);
 		}
-		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;
+		}
 	}
 }

@@ -2291,6 +2399,18 @@ static noinline struct module *load_module(void
__user *umod,
 	/* Get rid of temporary copy */
 	vfree(hdr);

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

@@ -2394,6 +2514,7 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
 	mutex_lock(&module_mutex);
 	/* Drop initial reference. */
 	module_put(mod);
+	unset_section_ro_nx(mod, mod->module_init);
 	module_free(mod, mod->module_init);
 	mod->module_init = NULL;
 	mod->init_size = 0;

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v6] RO/NX protection for loadable kernel modules
  2009-09-11  2:50 [PATCH v6] RO/NX protection for loadable kernel modules Siarhei Liakh
@ 2009-09-21 21:40 ` Dave Jones
  2009-09-22 15:49   ` Siarhei Liakh
  2009-09-21 23:59 ` Rusty Russell
  1 sibling, 1 reply; 6+ messages in thread
From: Dave Jones @ 2009-09-21 21:40 UTC (permalink / raw)
  To: Siarhei Liakh
  Cc: linux-kernel, linux-security-module, linux-next, Arjan van de Ven,
	James Morris, Andrew Morton, Andi Kleen, Thomas Gleixner,
	H. Peter Anvin, Ingo Molnar, Rusty Russell, Stephen Rothwell

On Thu, Sep 10, 2009 at 10:50:47PM -0400, Siarhei Liakh wrote:
 > 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

Hi Siarhei,
 I tried out this patch on 2.6.31, and got the following trace during bootup..

------------[ cut here ]------------
WARNING: at kernel/trace/ftrace.c:1003 ftrace_bug+0x198/0x27e() (Not tainted)
Hardware name: VGN-Z540N
Modules linked in: output(+)
Pid: 115, comm: modprobe Not tainted 2.6.31-23.fc12.x86_64 #1
Call Trace:
 [<ffffffff81064244>] warn_slowpath_common+0x95/0xc3
 [<ffffffffa0000193>] ? video_output_register+0x11/0x10a [output]
 [<ffffffff81064299>] warn_slowpath_null+0x27/0x3d
 [<ffffffff810d4a0e>] ftrace_bug+0x198/0x27e
 [<ffffffffa0000193>] ? video_output_register+0x11/0x10a [output]
 [<ffffffff810d5c66>] ftrace_convert_nops+0x201/0x2b9
 [<ffffffffa0000193>] ? video_output_register+0x11/0x10a [output]
 [<ffffffff810d5d6a>] ftrace_module_notify+0x4c/0x7f
 [<ffffffff815099c5>] notifier_call_chain+0x72/0xba
 [<ffffffff81086db1>] ? __blocking_notifier_call_chain+0x4c/0x8e
 [<ffffffff81086dc8>] __blocking_notifier_call_chain+0x63/0x8e
 [<ffffffff81086e1a>] blocking_notifier_call_chain+0x27/0x3d
 [<ffffffff810a466a>] sys_init_module+0xb7/0x249
 [<ffffffff81011f42>] system_call_fastpath+0x16/0x1b
---[ end trace 95b33ebf87286ae6 ]---
ftrace faulted on writing [<ffffffffa0000193>] video_output_register+0x11/0x10a [output]


I guess ftrace is trying to NOP out something in the modules which
are marked read-only ?

	Dave

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v6] RO/NX protection for loadable kernel modules
  2009-09-11  2:50 [PATCH v6] RO/NX protection for loadable kernel modules Siarhei Liakh
  2009-09-21 21:40 ` Dave Jones
@ 2009-09-21 23:59 ` Rusty Russell
  2009-09-22 15:50   ` Siarhei Liakh
  1 sibling, 1 reply; 6+ messages in thread
From: Rusty Russell @ 2009-09-21 23:59 UTC (permalink / raw)
  To: Siarhei Liakh
  Cc: linux-kernel, linux-security-module, linux-next, Arjan van de Ven,
	James Morris, Andrew Morton, Andi Kleen, Thomas Gleixner,
	H. Peter Anvin, Ingo Molnar, Stephen Rothwell

On Fri, 11 Sep 2009 12:20:47 pm Siarhei Liakh wrote:
> +/*
> + * LKM RO/NX protection: protect module's text/ro-data
> + * from modification and any data from execution.
> + */
> +static void set_section_ro_nx(void *base,
> +			unsigned long text_size,
> +			unsigned long ro_size,
> +			unsigned long total_size)
> +{
> +#ifdef CONFIG_X86

This is usually considered a bad idea.  Make a new config option,
CONFIG_HAVE_SET_MEMORY_PROTECTION, select it in arch/x86/Kconfig.
Then wrap the entire functions in one #ifdef CONFIG_HAVE_....
and put empty versions in the #else.

Thanks,
Rusty.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v6] RO/NX protection for loadable kernel modules
  2009-09-21 21:40 ` Dave Jones
@ 2009-09-22 15:49   ` Siarhei Liakh
  2009-10-05 11:57     ` Siarhei Liakh
  0 siblings, 1 reply; 6+ messages in thread
From: Siarhei Liakh @ 2009-09-22 15:49 UTC (permalink / raw)
  To: Dave Jones, Siarhei Liakh, linux-kernel, linux-security-module,
	linux-next, Arjan

> Hi Siarhei,
>  I tried out this patch on 2.6.31, and got the following trace during bootup..
>
> ------------[ cut here ]------------
> WARNING: at kernel/trace/ftrace.c:1003 ftrace_bug+0x198/0x27e() (Not tainted)
> Hardware name: VGN-Z540N
...
> ---[ end trace 95b33ebf87286ae6 ]---
> ftrace faulted on writing [<ffffffffa0000193>] video_output_register+0x11/0x10a [output]
>
>
> I guess ftrace is trying to NOP out something in the modules which
> are marked read-only ?

Entirely possible. I will review ftrace and make sure the patch
accounts for this.

Thank you.
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v6] RO/NX protection for loadable kernel modules
  2009-09-21 23:59 ` Rusty Russell
@ 2009-09-22 15:50   ` Siarhei Liakh
  0 siblings, 0 replies; 6+ messages in thread
From: Siarhei Liakh @ 2009-09-22 15:50 UTC (permalink / raw)
  To: Rusty Russell
  Cc: linux-kernel, linux-security-module, linux-next, Arjan van de Ven,
	James Morris, Andrew Morton, Andi Kleen, Thomas Gleixner,
	H. Peter Anvin, Ingo Molnar, Stephen Rothwell

> This is usually considered a bad idea.  Make a new config option,
> CONFIG_HAVE_SET_MEMORY_PROTECTION, select it in arch/x86/Kconfig.
> Then wrap the entire functions in one #ifdef CONFIG_HAVE_....
> and put empty versions in the #else.

Ok, will do.
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v6] RO/NX protection for loadable kernel modules
  2009-09-22 15:49   ` Siarhei Liakh
@ 2009-10-05 11:57     ` Siarhei Liakh
  0 siblings, 0 replies; 6+ messages in thread
From: Siarhei Liakh @ 2009-10-05 11:57 UTC (permalink / raw)
  To: Dave Jones, Siarhei Liakh, linux-kernel, linux-security-module,
	linux-next, Arjan

On Tue, Sep 22, 2009 at 11:49 AM, Siarhei Liakh <sliakh.lkml@gmail.com> wrote:
>> Hi Siarhei,
>>  I tried out this patch on 2.6.31, and got the following trace during bootup..
>>
>> ------------[ cut here ]------------
>> WARNING: at kernel/trace/ftrace.c:1003 ftrace_bug+0x198/0x27e() (Not tainted)
>> Hardware name: VGN-Z540N
> ...
>> ---[ end trace 95b33ebf87286ae6 ]---
>> ftrace faulted on writing [<ffffffffa0000193>] video_output_register+0x11/0x10a [output]
>>
>>
>> I guess ftrace is trying to NOP out something in the modules which
>> are marked read-only ?
>
> Entirely possible. I will review ftrace and make sure the patch
> accounts for this.

I've posted new version of the patch that also sets each module's text
as RW when ftrace preps the kernel for modifications: [PATCH v7] RO/NX
protection for loadable kernel modules

Let me know how that works for you.
Thank you.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2009-10-05 11:57 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-09-11  2:50 [PATCH v6] RO/NX protection for loadable kernel modules Siarhei Liakh
2009-09-21 21:40 ` Dave Jones
2009-09-22 15:49   ` Siarhei Liakh
2009-10-05 11:57     ` Siarhei Liakh
2009-09-21 23:59 ` Rusty Russell
2009-09-22 15:50   ` Siarhei Liakh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).