Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1265336 > unrolled thread

[PATCH 4/4] module: clean up RO/NX handling.

Started byRusty Russell <rusty@rustcorp.com.au>
First post2015-11-09 05:30 +0100
Last post2015-11-12 04:50 +0100
Articles 6 — 2 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  [PATCH 4/4] module: clean up RO/NX handling. Rusty Russell <rusty@rustcorp.com.au> - 2015-11-09 05:30 +0100
    Re: [PATCH 4/4] module: clean up RO/NX handling. Josh Poimboeuf <jpoimboe@redhat.com> - 2015-11-09 21:00 +0100
      Re: [PATCH 4/4] module: clean up RO/NX handling. Rusty Russell <rusty@rustcorp.com.au> - 2015-11-10 03:00 +0100
        Re: [PATCH 4/4] module: clean up RO/NX handling. Josh Poimboeuf <jpoimboe@redhat.com> - 2015-11-10 05:30 +0100
          Re: [PATCH 4/4] module: clean up RO/NX handling. Rusty Russell <rusty@rustcorp.com.au> - 2015-11-12 02:30 +0100
            Re: [PATCH 4/4] module: clean up RO/NX handling. Josh Poimboeuf <jpoimboe@redhat.com> - 2015-11-12 04:50 +0100

#1265336 — [PATCH 4/4] module: clean up RO/NX handling.

FromRusty Russell <rusty@rustcorp.com.au>
Date2015-11-09 05:30 +0100
Subject[PATCH 4/4] module: clean up RO/NX handling.
Message-ID<qsMfE-1po-11@gated-at.bofh.it>
Modules have three sections: text, rodata and writable data.  The code
handled the case where these overlapped, however they never can:
debug_align() ensures they are always page-aligned.

This is why we got away with manually traversing the pages in
set_all_modules_text_rw() without rounding.

We create three helper functions: frob_text(), frob_rodata() and
frob_writable_data().  We then call these explicitly at every point,
so it's clear what we're doing.

We also expose module_enable_ro() and module_disable_ro() for
livepatch to use.

Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 include/linux/module.h |   4 ++
 kernel/module.c        | 168 +++++++++++++++++++++++--------------------------
 2 files changed, 81 insertions(+), 91 deletions(-)

diff --git a/include/linux/module.h b/include/linux/module.h
index 6e68e8cf4d0d..b759034a0540 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -762,9 +762,13 @@ extern int module_sysfs_initialized;
 #ifdef CONFIG_DEBUG_SET_MODULE_RONX
 extern void set_all_modules_text_rw(void);
 extern void set_all_modules_text_ro(void);
+extern void module_enable_ro(const struct module *mod);
+extern void module_disable_ro(const struct module *mod);
 #else
 static inline void set_all_modules_text_rw(void) { }
 static inline void set_all_modules_text_ro(void) { }
+static inline module_enable_ro(const struct module *mod) { }
+static inline module_disable_ro(const struct module *mod) { }
 #endif
 
 #ifdef CONFIG_GENERIC_BUG
diff --git a/kernel/module.c b/kernel/module.c
index a0a3d6d9d5e8..77212128f34a 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -80,15 +80,6 @@
 # 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))
 
@@ -1858,74 +1849,75 @@ static void mod_sysfs_teardown(struct module *mod)
 /*
  * LKM RO/NX protection: protect module's text/ro-data
  * from modification and any data from execution.
+ *
+ * General layout of module is:
+ *          [text] [read-only-data] [writable data]
+ * text_size -----^                ^               ^
+ * ro_size ------------------------|               |
+ * size -------------------------------------------|
+ *
+ * These values are always page-aligned (as is base)
  */
-void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
+static void frob_text(const struct module_layout *layout,
+		      int (*set_memory)(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);
+	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
+	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
+	set_memory((unsigned long)layout->base,
+		   layout->text_size >> PAGE_SHIFT);
 }
 
-static void set_section_ro_nx(void *base,
-			unsigned long text_size,
-			unsigned long ro_size,
-			unsigned long total_size,
-			int (*set_ro)(unsigned long start, int num_pages),
-			int (*set_nx)(unsigned long start, int num_pages))
+static void frob_rodata(const struct module_layout *layout,
+			int (*set_memory)(unsigned long start, int num_pages))
 {
-	/* 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_ro);
+	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
+	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
+	BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
+	set_memory((unsigned long)layout->base + layout->text_size,
+		   (layout->ro_size - layout->text_size) >> PAGE_SHIFT);
+}
 
-	/*
-	 * 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_nx(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn);
-	}
+static void frob_writable_data(const struct module_layout *layout,
+			       int (*set_memory)(unsigned long start, int num_pages))
+{
+	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
+	BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
+	BUG_ON((unsigned long)layout->size & (PAGE_SIZE-1));
+	set_memory((unsigned long)layout->base + layout->ro_size,
+		   (layout->size - layout->ro_size) >> PAGE_SHIFT);
 }
 
-static void set_module_core_ro_nx(struct module *mod)
+/* livepatching wants to disable read-only so it can frob module. */
+void module_disable_ro(const struct module *mod)
 {
-	set_section_ro_nx(mod->core_layout.base, mod->core_layout.text_size,
-			  mod->core_layout.ro_size, mod->core_layout.size,
-			  set_memory_ro, set_memory_nx);
+	frob_text(&mod->core_layout, set_memory_rw);
+	frob_rodata(&mod->core_layout, set_memory_rw);
+	frob_text(&mod->init_layout, set_memory_rw);
+	frob_rodata(&mod->init_layout, set_memory_rw);
 }
 
-static void unset_module_core_ro_nx(struct module *mod)
+void module_enable_ro(const struct module *mod)
 {
-	set_section_ro_nx(mod->core_layout.base, mod->core_layout.text_size,
-			  mod->core_layout.ro_size, mod->core_layout.size,
-			  set_memory_rw, set_memory_x);
+	frob_text(&mod->core_layout, set_memory_ro);
+	frob_rodata(&mod->core_layout, set_memory_ro);
+	frob_text(&mod->init_layout, set_memory_ro);
+	frob_rodata(&mod->init_layout, set_memory_ro);
 }
 
-static void set_module_init_ro_nx(struct module *mod)
+static void module_enable_nx(const struct module *mod)
 {
-	set_section_ro_nx(mod->init_layout.base, mod->init_layout.text_size,
-			  mod->init_layout.ro_size, mod->init_layout.size,
-			  set_memory_ro, set_memory_nx);
+	frob_rodata(&mod->core_layout, set_memory_nx);
+	frob_writable_data(&mod->core_layout, set_memory_nx);
+	frob_rodata(&mod->init_layout, set_memory_nx);
+	frob_writable_data(&mod->init_layout, set_memory_nx);
 }
 
-static void unset_module_init_ro_nx(struct module *mod)
+static void module_disable_nx(const struct module *mod)
 {
-	set_section_ro_nx(mod->init_layout.base, mod->init_layout.text_size,
-			  mod->init_layout.ro_size, mod->init_layout.size,
-			  set_memory_rw, set_memory_x);
+	frob_rodata(&mod->core_layout, set_memory_x);
+	frob_writable_data(&mod->core_layout, set_memory_x);
+	frob_rodata(&mod->init_layout, set_memory_x);
+	frob_writable_data(&mod->init_layout, set_memory_x);
 }
 
 /* Iterate through all modules and set each module's text as RW */
@@ -1937,16 +1929,9 @@ void set_all_modules_text_rw(void)
 	list_for_each_entry_rcu(mod, &modules, list) {
 		if (mod->state == MODULE_STATE_UNFORMED)
 			continue;
-		if ((mod->core_layout.base) && (mod->core_layout.text_size)) {
-			set_page_attributes(mod->core_layout.base,
-						mod->core_layout.base + mod->core_layout.text_size,
-						set_memory_rw);
-		}
-		if ((mod->init_layout.base) && (mod->init_layout.text_size)) {
-			set_page_attributes(mod->init_layout.base,
-						mod->init_layout.base + mod->init_layout.text_size,
-						set_memory_rw);
-		}
+
+		frob_text(&mod->core_layout, set_memory_rw);
+		frob_text(&mod->init_layout, set_memory_rw);
 	}
 	mutex_unlock(&module_mutex);
 }
@@ -1960,24 +1945,25 @@ void set_all_modules_text_ro(void)
 	list_for_each_entry_rcu(mod, &modules, list) {
 		if (mod->state == MODULE_STATE_UNFORMED)
 			continue;
-		if ((mod->core_layout.base) && (mod->core_layout.text_size)) {
-			set_page_attributes(mod->core_layout.base,
-						mod->core_layout.base + mod->core_layout.text_size,
-						set_memory_ro);
-		}
-		if ((mod->init_layout.base) && (mod->init_layout.text_size)) {
-			set_page_attributes(mod->init_layout.base,
-						mod->init_layout.base + mod->init_layout.text_size,
-						set_memory_ro);
-		}
+
+		frob_text(&mod->core_layout, set_memory_ro);
+		frob_text(&mod->init_layout, set_memory_ro);
 	}
 	mutex_unlock(&module_mutex);
 }
+
+static void disable_ro_nx(const struct module_layout *layout)
+{
+	frob_text(layout, set_memory_rw);
+	frob_rodata(layout, set_memory_rw);
+	frob_rodata(layout, set_memory_x);
+	frob_writable_data(layout, set_memory_x);
+}
+
 #else
-static void set_module_core_ro_nx(struct module *mod) { }
-static void set_module_init_ro_nx(struct module *mod) { }
-static void unset_module_core_ro_nx(struct module *mod) { }
-static void unset_module_init_ro_nx(struct module *mod) { }
+static void disable_ro_nx(const struct module_layout *layout) { }
+static void module_enable_nx(const struct module *mod) { }
+static void module_disable_nx(const struct module *mod) { }
 #endif
 
 void __weak module_memfree(void *module_region)
@@ -2029,8 +2015,8 @@ static void free_module(struct module *mod)
 	synchronize_sched();
 	mutex_unlock(&module_mutex);
 
-	/* This may be NULL, but that's OK */
-	unset_module_init_ro_nx(mod);
+	/* This may be empty, but that's OK */
+	disable_ro_nx(&mod->init_layout);
 	module_arch_freeing_init(mod);
 	module_memfree(mod->init_layout.base);
 	kfree(mod->args);
@@ -2040,7 +2026,7 @@ static void free_module(struct module *mod)
 	lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size);
 
 	/* Finally, free the core (containing the module structure) */
-	unset_module_core_ro_nx(mod);
+	disable_ro_nx(&mod->core_layout);
 	module_memfree(mod->core_layout.base);
 
 #ifdef CONFIG_MPU
@@ -3275,7 +3261,7 @@ static noinline int do_init_module(struct module *mod)
 	mod->strtab = mod->core_strtab;
 #endif
 	mod_tree_remove_init(mod);
-	unset_module_init_ro_nx(mod);
+	disable_ro_nx(&mod->init_layout);
 	module_arch_freeing_init(mod);
 	mod->init_layout.base = NULL;
 	mod->init_layout.size = 0;
@@ -3370,8 +3356,8 @@ static int complete_formation(struct module *mod, struct load_info *info)
 	module_bug_finalize(info->hdr, info->sechdrs, mod);
 
 	/* Set RO and NX regions */
-	set_module_init_ro_nx(mod);
-	set_module_core_ro_nx(mod);
+	module_enable_ro(mod);
+	module_enable_nx(mod);
 
 	/* Mark state as coming so strong_try_module_get() ignores us,
 	 * but kallsyms etc. can see us. */
@@ -3536,8 +3522,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
 				     MODULE_STATE_GOING, mod);
 
 	/* we can't deallocate the module until we clear memory protection */
-	unset_module_init_ro_nx(mod);
-	unset_module_core_ro_nx(mod);
+	module_disable_ro(mod);
+	module_disable_nx(mod);
 
  ddebug_cleanup:
 	dynamic_debug_remove(info->debug);
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [next] | [standalone]


#1265975

FromJosh Poimboeuf <jpoimboe@redhat.com>
Date2015-11-09 21:00 +0100
Message-ID<qt0LE-2nj-5@gated-at.bofh.it>
In reply to#1265336
On Mon, Nov 09, 2015 at 02:53:57PM +1030, Rusty Russell wrote:

> @@ -1858,74 +1849,75 @@ static void mod_sysfs_teardown(struct module *mod)
>  /*
>   * LKM RO/NX protection: protect module's text/ro-data
>   * from modification and any data from execution.
> + *
> + * General layout of module is:
> + *          [text] [read-only-data] [writable data]
> + * text_size -----^                ^               ^
> + * ro_size ------------------------|               |
> + * size -------------------------------------------|
> + *
> + * These values are always page-aligned (as is base)
>   */
> -void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
> +static void frob_text(const struct module_layout *layout,
> +		      int (*set_memory)(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);
> +	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
> +	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
> +	set_memory((unsigned long)layout->base,
> +		   layout->text_size >> PAGE_SHIFT);

Should the set_memory() call be skipped if text_size is 0?

>  }
>  
> -static void set_section_ro_nx(void *base,
> -			unsigned long text_size,
> -			unsigned long ro_size,
> -			unsigned long total_size,
> -			int (*set_ro)(unsigned long start, int num_pages),
> -			int (*set_nx)(unsigned long start, int num_pages))
> +static void frob_rodata(const struct module_layout *layout,
> +			int (*set_memory)(unsigned long start, int num_pages))
>  {
> -	/* 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_ro);
> +	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
> +	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
> +	BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
> +	set_memory((unsigned long)layout->base + layout->text_size,
> +		   (layout->ro_size - layout->text_size) >> PAGE_SHIFT);
> +}

Same here, what if ro_size == text_size (no rodata)?

>  
> -	/*
> -	 * 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_nx(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn);
> -	}
> +static void frob_writable_data(const struct module_layout *layout,
> +			       int (*set_memory)(unsigned long start, int num_pages))
> +{
> +	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
> +	BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
> +	BUG_ON((unsigned long)layout->size & (PAGE_SIZE-1));
> +	set_memory((unsigned long)layout->base + layout->ro_size,
> +		   (layout->size - layout->ro_size) >> PAGE_SHIFT);
>  }

Ditto for size == ro_size (no writable data).


-- 
Josh
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1266173

FromRusty Russell <rusty@rustcorp.com.au>
Date2015-11-10 03:00 +0100
Message-ID<qt6o2-6Uw-7@gated-at.bofh.it>
In reply to#1265975
Josh Poimboeuf <jpoimboe@redhat.com> writes:
> On Mon, Nov 09, 2015 at 02:53:57PM +1030, Rusty Russell wrote:
>
>> @@ -1858,74 +1849,75 @@ static void mod_sysfs_teardown(struct module *mod)
>>  /*
>>   * LKM RO/NX protection: protect module's text/ro-data
>>   * from modification and any data from execution.
>> + *
>> + * General layout of module is:
>> + *          [text] [read-only-data] [writable data]
>> + * text_size -----^                ^               ^
>> + * ro_size ------------------------|               |
>> + * size -------------------------------------------|
>> + *
>> + * These values are always page-aligned (as is base)
>>   */
>> -void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
>> +static void frob_text(const struct module_layout *layout,
>> +		      int (*set_memory)(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);
>> +	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
>> +	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
>> +	set_memory((unsigned long)layout->base,
>> +		   layout->text_size >> PAGE_SHIFT);
>
> Should the set_memory() call be skipped if text_size is 0?

Not AFAICT.  And in practice:
1) Every module on my system has a .text section.
2) Every module has a rodata section (.modinfo)
3) Every module on my system has a .data section.

So I think it would be a premature optimization.

Thanks,
Rusty.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1266220

FromJosh Poimboeuf <jpoimboe@redhat.com>
Date2015-11-10 05:30 +0100
Message-ID<qt8Jb-cg-1@gated-at.bofh.it>
In reply to#1266173
On Tue, Nov 10, 2015 at 12:27:34PM +1030, Rusty Russell wrote:
> Josh Poimboeuf <jpoimboe@redhat.com> writes:
> > On Mon, Nov 09, 2015 at 02:53:57PM +1030, Rusty Russell wrote:
> >
> >> @@ -1858,74 +1849,75 @@ static void mod_sysfs_teardown(struct module *mod)
> >>  /*
> >>   * LKM RO/NX protection: protect module's text/ro-data
> >>   * from modification and any data from execution.
> >> + *
> >> + * General layout of module is:
> >> + *          [text] [read-only-data] [writable data]
> >> + * text_size -----^                ^               ^
> >> + * ro_size ------------------------|               |
> >> + * size -------------------------------------------|
> >> + *
> >> + * These values are always page-aligned (as is base)
> >>   */
> >> -void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
> >> +static void frob_text(const struct module_layout *layout,
> >> +		      int (*set_memory)(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);
> >> +	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
> >> +	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
> >> +	set_memory((unsigned long)layout->base,
> >> +		   layout->text_size >> PAGE_SHIFT);
> >
> > Should the set_memory() call be skipped if text_size is 0?
> 
> Not AFAICT.  And in practice:
> 1) Every module on my system has a .text section.
> 2) Every module has a rodata section (.modinfo)
> 3) Every module on my system has a .data section.
> 
> So I think it would be a premature optimization.

However, the frob functions are also used for init sections.

A search on my Fedora system's modules for .init.* sections shows that
most modules don't have .init.rodata and .init.data, and some modules
don't even have .init.text.

-- 
Josh
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1267578

FromRusty Russell <rusty@rustcorp.com.au>
Date2015-11-12 02:30 +0100
Message-ID<qtOS6-2dR-3@gated-at.bofh.it>
In reply to#1266220
Josh Poimboeuf <jpoimboe@redhat.com> writes:
> On Tue, Nov 10, 2015 at 12:27:34PM +1030, Rusty Russell wrote:
>> Josh Poimboeuf <jpoimboe@redhat.com> writes:
>> > On Mon, Nov 09, 2015 at 02:53:57PM +1030, Rusty Russell wrote:
>> >
>> >> @@ -1858,74 +1849,75 @@ static void mod_sysfs_teardown(struct module *mod)
>> >>  /*
>> >>   * LKM RO/NX protection: protect module's text/ro-data
>> >>   * from modification and any data from execution.
>> >> + *
>> >> + * General layout of module is:
>> >> + *          [text] [read-only-data] [writable data]
>> >> + * text_size -----^                ^               ^
>> >> + * ro_size ------------------------|               |
>> >> + * size -------------------------------------------|
>> >> + *
>> >> + * These values are always page-aligned (as is base)
>> >>   */
>> >> -void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
>> >> +static void frob_text(const struct module_layout *layout,
>> >> +		      int (*set_memory)(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);
>> >> +	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
>> >> +	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
>> >> +	set_memory((unsigned long)layout->base,
>> >> +		   layout->text_size >> PAGE_SHIFT);
>> >
>> > Should the set_memory() call be skipped if text_size is 0?
>> 
>> Not AFAICT.  And in practice:
>> 1) Every module on my system has a .text section.
>> 2) Every module has a rodata section (.modinfo)
>> 3) Every module on my system has a .data section.
>> 
>> So I think it would be a premature optimization.
>
> However, the frob functions are also used for init sections.
>
> A search on my Fedora system's modules for .init.* sections shows that
> most modules don't have .init.rodata and .init.data, and some modules
> don't even have .init.text.

Good point!  OK, let's do some trivial benchmarking...

diff --git a/kernel/module.c b/kernel/module.c
index 77212128f34a..94ea51a20958 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1906,10 +1906,19 @@ void module_enable_ro(const struct module *mod)
 
 static void module_enable_nx(const struct module *mod)
 {
+	size_t i;
+	ktime_t start, end;
 	frob_rodata(&mod->core_layout, set_memory_nx);
 	frob_writable_data(&mod->core_layout, set_memory_nx);
-	frob_rodata(&mod->init_layout, set_memory_nx);
-	frob_writable_data(&mod->init_layout, set_memory_nx);
+
+	start = ktime_get_boottime();
+	for (i = 0; i < 1000000; i++) {
+		frob_rodata(&mod->init_layout, set_memory_nx);
+		frob_writable_data(&mod->init_layout, set_memory_nx);
+	}
+	end = ktime_get_boottime();
+	printk("%s init time (ns): %lu\n", module_name(mod),
+	       ktime_to_ns(ktime_sub(end, start)));
 }
 
 static void module_disable_nx(const struct module *mod)

[    2.794462] parport init time (ns): 15277714
[    2.855277] lp init time (ns): 15207768
[    2.909701] mac_hid init time (ns): 15409571
[    2.975350] tpm_tis init time (ns): 15118394
[    3.062865] parport_pc init time (ns): 15646948
[    3.247979] virtio_balloon init time (ns): 15555578
[    3.291373] virtio_net init time (ns): 15236362
[    3.391361] serio_raw init time (ns): 15395063

Range & mean: 15118394-15646948(1.53559e+07+/-1.7e+05)

With a zero-check:

[    2.530933] parport init time (ns): 12133350
[    2.587167] lp init time (ns): 12059255
[    2.642342] mac_hid init time (ns): 12849836
[    2.698726] tpm_tis init time (ns): 12008736
[    2.768969] parport_pc init time (ns): 12057191
[    2.943308] virtio_net init time (ns): 12048224
[    2.989983] virtio_balloon init time (ns): 12077151
[    3.061752] serio_raw init time (ns): 12396804

Range & mean: 12008736-12849836(1.22038e+07+/-2.7e+05)

So, we did save 3ns, but it was 12ns even to call them to do nothing.
We'd save more by removing the BUG_ON checks I suspect...

Cheers,
Rusty.

diff --git a/kernel/module.c b/kernel/module.c
index 77212128f34a..9ac8952a0a72 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1863,8 +1863,9 @@ static void frob_text(const struct module_layout *layout,
 {
 	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
 	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
-	set_memory((unsigned long)layout->base,
-		   layout->text_size >> PAGE_SHIFT);
+	if (layout->text_size)
+		set_memory((unsigned long)layout->base,
+			   layout->text_size >> PAGE_SHIFT);
 }
 
 static void frob_rodata(const struct module_layout *layout,
@@ -1873,8 +1874,9 @@ static void frob_rodata(const struct module_layout *layout,
 	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
 	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
 	BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
-	set_memory((unsigned long)layout->base + layout->text_size,
-		   (layout->ro_size - layout->text_size) >> PAGE_SHIFT);
+	if (layout->ro_size != layout->text_size)
+		set_memory((unsigned long)layout->base + layout->text_size,
+			   (layout->ro_size - layout->text_size) >> PAGE_SHIFT);
 }
 
 static void frob_writable_data(const struct module_layout *layout,
@@ -1883,8 +1885,9 @@ static void frob_writable_data(const struct module_layout *layout,
 	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
 	BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
 	BUG_ON((unsigned long)layout->size & (PAGE_SIZE-1));
-	set_memory((unsigned long)layout->base + layout->ro_size,
-		   (layout->size - layout->ro_size) >> PAGE_SHIFT);
+	if (layout->size != layout->ro_size)
+		set_memory((unsigned long)layout->base + layout->ro_size,
+			   (layout->size - layout->ro_size) >> PAGE_SHIFT);
 }
 
 /* livepatching wants to disable read-only so it can frob module. */
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1267600

FromJosh Poimboeuf <jpoimboe@redhat.com>
Date2015-11-12 04:50 +0100
Message-ID<qtR3A-3AG-5@gated-at.bofh.it>
In reply to#1267578
On Thu, Nov 12, 2015 at 11:58:25AM +1030, Rusty Russell wrote:
> Josh Poimboeuf <jpoimboe@redhat.com> writes:
> > On Tue, Nov 10, 2015 at 12:27:34PM +1030, Rusty Russell wrote:
> >> Josh Poimboeuf <jpoimboe@redhat.com> writes:
> >> > On Mon, Nov 09, 2015 at 02:53:57PM +1030, Rusty Russell wrote:
> >> >
> >> >> @@ -1858,74 +1849,75 @@ static void mod_sysfs_teardown(struct module *mod)
> >> >>  /*
> >> >>   * LKM RO/NX protection: protect module's text/ro-data
> >> >>   * from modification and any data from execution.
> >> >> + *
> >> >> + * General layout of module is:
> >> >> + *          [text] [read-only-data] [writable data]
> >> >> + * text_size -----^                ^               ^
> >> >> + * ro_size ------------------------|               |
> >> >> + * size -------------------------------------------|
> >> >> + *
> >> >> + * These values are always page-aligned (as is base)
> >> >>   */
> >> >> -void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
> >> >> +static void frob_text(const struct module_layout *layout,
> >> >> +		      int (*set_memory)(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);
> >> >> +	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
> >> >> +	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
> >> >> +	set_memory((unsigned long)layout->base,
> >> >> +		   layout->text_size >> PAGE_SHIFT);
> >> >
> >> > Should the set_memory() call be skipped if text_size is 0?
> >> 
> >> Not AFAICT.  And in practice:
> >> 1) Every module on my system has a .text section.
> >> 2) Every module has a rodata section (.modinfo)
> >> 3) Every module on my system has a .data section.
> >> 
> >> So I think it would be a premature optimization.
> >
> > However, the frob functions are also used for init sections.
> >
> > A search on my Fedora system's modules for .init.* sections shows that
> > most modules don't have .init.rodata and .init.data, and some modules
> > don't even have .init.text.
> 
> Good point!  OK, let's do some trivial benchmarking...
> 
> diff --git a/kernel/module.c b/kernel/module.c
> index 77212128f34a..94ea51a20958 100644
> --- a/kernel/module.c
> +++ b/kernel/module.c
> @@ -1906,10 +1906,19 @@ void module_enable_ro(const struct module *mod)
>  
>  static void module_enable_nx(const struct module *mod)
>  {
> +	size_t i;
> +	ktime_t start, end;
>  	frob_rodata(&mod->core_layout, set_memory_nx);
>  	frob_writable_data(&mod->core_layout, set_memory_nx);
> -	frob_rodata(&mod->init_layout, set_memory_nx);
> -	frob_writable_data(&mod->init_layout, set_memory_nx);
> +
> +	start = ktime_get_boottime();
> +	for (i = 0; i < 1000000; i++) {
> +		frob_rodata(&mod->init_layout, set_memory_nx);
> +		frob_writable_data(&mod->init_layout, set_memory_nx);
> +	}
> +	end = ktime_get_boottime();
> +	printk("%s init time (ns): %lu\n", module_name(mod),
> +	       ktime_to_ns(ktime_sub(end, start)));
>  }
>  
>  static void module_disable_nx(const struct module *mod)
> 
> [    2.794462] parport init time (ns): 15277714
> [    2.855277] lp init time (ns): 15207768
> [    2.909701] mac_hid init time (ns): 15409571
> [    2.975350] tpm_tis init time (ns): 15118394
> [    3.062865] parport_pc init time (ns): 15646948
> [    3.247979] virtio_balloon init time (ns): 15555578
> [    3.291373] virtio_net init time (ns): 15236362
> [    3.391361] serio_raw init time (ns): 15395063
> 
> Range & mean: 15118394-15646948(1.53559e+07+/-1.7e+05)
> 
> With a zero-check:
> 
> [    2.530933] parport init time (ns): 12133350
> [    2.587167] lp init time (ns): 12059255
> [    2.642342] mac_hid init time (ns): 12849836
> [    2.698726] tpm_tis init time (ns): 12008736
> [    2.768969] parport_pc init time (ns): 12057191
> [    2.943308] virtio_net init time (ns): 12048224
> [    2.989983] virtio_balloon init time (ns): 12077151
> [    3.061752] serio_raw init time (ns): 12396804
> 
> Range & mean: 12008736-12849836(1.22038e+07+/-2.7e+05)
> 
> So, we did save 3ns, but it was 12ns even to call them to do nothing.
> We'd save more by removing the BUG_ON checks I suspect...

Ok, leaving the checks out seems harmless from a performance standpoint.

I was also curious about whether anything unexpected could potentially
happen in change_page_attr_set_clr() for numpages == 0.  But I guess it
seems to be fine (though I'm no mm expert).

I didn't quite get whether you decided to fold in the below patch.  But
either way:

Reviewed-by: Josh Poimboeuf <jpoimboe@redhat.com>

Thanks!


> Cheers,
> Rusty.
> 
> diff --git a/kernel/module.c b/kernel/module.c
> index 77212128f34a..9ac8952a0a72 100644
> --- a/kernel/module.c
> +++ b/kernel/module.c
> @@ -1863,8 +1863,9 @@ static void frob_text(const struct module_layout *layout,
>  {
>  	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
>  	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
> -	set_memory((unsigned long)layout->base,
> -		   layout->text_size >> PAGE_SHIFT);
> +	if (layout->text_size)
> +		set_memory((unsigned long)layout->base,
> +			   layout->text_size >> PAGE_SHIFT);
>  }
>  
>  static void frob_rodata(const struct module_layout *layout,
> @@ -1873,8 +1874,9 @@ static void frob_rodata(const struct module_layout *layout,
>  	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
>  	BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
>  	BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
> -	set_memory((unsigned long)layout->base + layout->text_size,
> -		   (layout->ro_size - layout->text_size) >> PAGE_SHIFT);
> +	if (layout->ro_size != layout->text_size)
> +		set_memory((unsigned long)layout->base + layout->text_size,
> +			   (layout->ro_size - layout->text_size) >> PAGE_SHIFT);
>  }
>  
>  static void frob_writable_data(const struct module_layout *layout,
> @@ -1883,8 +1885,9 @@ static void frob_writable_data(const struct module_layout *layout,
>  	BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
>  	BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
>  	BUG_ON((unsigned long)layout->size & (PAGE_SIZE-1));
> -	set_memory((unsigned long)layout->base + layout->ro_size,
> -		   (layout->size - layout->ro_size) >> PAGE_SHIFT);
> +	if (layout->size != layout->ro_size)
> +		set_memory((unsigned long)layout->base + layout->ro_size,
> +			   (layout->size - layout->ro_size) >> PAGE_SHIFT);
>  }
>  
>  /* livepatching wants to disable read-only so it can frob module. */

-- 
Josh
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web