Merge commit 'v2.6.37-rc7' into x86/security
authorIngo Molnar <mingo@elte.hu>
Thu, 23 Dec 2010 08:48:41 +0000 (09:48 +0100)
committerIngo Molnar <mingo@elte.hu>
Thu, 23 Dec 2010 08:48:41 +0000 (09:48 +0100)
1  2 
include/linux/module.h
kernel/module.c

diff --combined include/linux/module.h
index ddaa689d71bdee5bd7ee5ca0df9a436417992d03,7575bbbdf2a2b8e6a716fb6252c3d8e958756f52..8b17fd8c790d8601f8aff0a33c7b909984e30545
@@@ -308,9 -308,6 +308,9 @@@ struct modul
        /* 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;
  
@@@ -520,7 -517,7 +520,7 @@@ static inline void __module_get(struct 
  #define symbol_put_addr(p) do { } while(0)
  
  #endif /* CONFIG_MODULE_UNLOAD */
- int use_module(struct module *a, struct module *b);
+ int ref_module(struct module *a, struct module *b);
  
  /* This is a #define so the string doesn't get put in every .o file */
  #define module_name(mod)                      \
@@@ -675,6 -672,7 +675,6 @@@ static inline int module_get_iter_trace
  {
        return 0;
  }
 -
  #endif /* CONFIG_MODULES */
  
  #ifdef CONFIG_SYSFS
@@@ -689,13 -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 *,
diff --combined kernel/module.c
index ba421e6b4ada7c14d5d61303831068b8f6ec976c,d190664f25ff3fa10dca29f37b483f08ad07eae1..562f665c721f2d094a77cad153c24a5d46215955
@@@ -56,7 -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>
  #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))
  
@@@ -1563,115 -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)
  {
        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);
        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
@@@ -1909,19 -1777,8 +1909,19 @@@ static void layout_sections(struct modu
                        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");
                                         | 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;
 +              }
        }
  }
  
@@@ -2480,6 -2326,18 +2480,18 @@@ static void find_module_sections(struc
        kmemleak_scan_area(mod->trace_events, sizeof(*mod->trace_events) *
                           mod->num_trace_events, GFP_KERNEL);
  #endif
+ #ifdef CONFIG_TRACING
+       mod->trace_bprintk_fmt_start = section_objs(info, "__trace_printk_fmt",
+                                        sizeof(*mod->trace_bprintk_fmt_start),
+                                        &mod->num_trace_bprintk_fmt);
+       /*
+        * This section contains pointers to allocated objects in the trace
+        * code and not scanning it leads to false positives.
+        */
+       kmemleak_scan_area(mod->trace_bprintk_fmt_start,
+                          sizeof(*mod->trace_bprintk_fmt_start) *
+                          mod->num_trace_bprintk_fmt, GFP_KERNEL);
+ #endif
  #ifdef CONFIG_FTRACE_MCOUNT_RECORD
        /* sechdrs[0].sh_size is always zero */
        mod->ftrace_callsites = section_objs(info, "__mcount_loc",
@@@ -2804,18 -2662,6 +2816,18 @@@ static struct module *load_module(void 
        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;
@@@ -2919,7 -2765,6 +2931,7 @@@ SYSCALL_DEFINE3(init_module, void __use
        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;