diff options
-rw-r--r-- | arch/x86/Kconfig.debug | 11 | ||||
-rw-r--r-- | arch/x86/kernel/ftrace.c | 3 | ||||
-rw-r--r-- | include/linux/module.h | 11 | ||||
-rw-r--r-- | kernel/module.c | 171 |
4 files changed, 193 insertions, 3 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index b59ee765414e..45143bbcfe5e 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug | |||
@@ -117,6 +117,17 @@ config DEBUG_RODATA_TEST | |||
117 | feature as well as for the change_page_attr() infrastructure. | 117 | feature as well as for the change_page_attr() infrastructure. |
118 | If in doubt, say "N" | 118 | If in doubt, say "N" |
119 | 119 | ||
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 | |||
120 | config DEBUG_NX_TEST | 131 | config DEBUG_NX_TEST |
121 | tristate "Testcase for the NX non-executable stack feature" | 132 | tristate "Testcase for the NX non-executable stack feature" |
122 | depends on DEBUG_KERNEL && m | 133 | depends on DEBUG_KERNEL && m |
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 3afb33f14d2d..298448656b60 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/sched.h> | 19 | #include <linux/sched.h> |
20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
21 | #include <linux/list.h> | 21 | #include <linux/list.h> |
22 | #include <linux/module.h> | ||
22 | 23 | ||
23 | #include <trace/syscall.h> | 24 | #include <trace/syscall.h> |
24 | 25 | ||
@@ -49,6 +50,7 @@ static DEFINE_PER_CPU(int, save_modifying_code); | |||
49 | int ftrace_arch_code_modify_prepare(void) | 50 | int ftrace_arch_code_modify_prepare(void) |
50 | { | 51 | { |
51 | set_kernel_text_rw(); | 52 | set_kernel_text_rw(); |
53 | set_all_modules_text_rw(); | ||
52 | modifying_code = 1; | 54 | modifying_code = 1; |
53 | return 0; | 55 | return 0; |
54 | } | 56 | } |
@@ -56,6 +58,7 @@ int ftrace_arch_code_modify_prepare(void) | |||
56 | int ftrace_arch_code_modify_post_process(void) | 58 | int ftrace_arch_code_modify_post_process(void) |
57 | { | 59 | { |
58 | modifying_code = 0; | 60 | modifying_code = 0; |
61 | set_all_modules_text_ro(); | ||
59 | set_kernel_text_ro(); | 62 | set_kernel_text_ro(); |
60 | return 0; | 63 | return 0; |
61 | } | 64 | } |
diff --git a/include/linux/module.h b/include/linux/module.h index b29e7458b966..ddaa689d71bd 100644 --- a/include/linux/module.h +++ b/include/linux/module.h | |||
@@ -308,6 +308,9 @@ struct module | |||
308 | /* The size of the executable code in each section. */ | 308 | /* The size of the executable code in each section. */ |
309 | unsigned int init_text_size, core_text_size; | 309 | unsigned int init_text_size, core_text_size; |
310 | 310 | ||
311 | /* Size of RO sections of the module (text+rodata) */ | ||
312 | unsigned int init_ro_size, core_ro_size; | ||
313 | |||
311 | /* Arch-specific module values */ | 314 | /* Arch-specific module values */ |
312 | struct mod_arch_specific arch; | 315 | struct mod_arch_specific arch; |
313 | 316 | ||
@@ -672,7 +675,6 @@ static inline int module_get_iter_tracepoints(struct tracepoint_iter *iter) | |||
672 | { | 675 | { |
673 | return 0; | 676 | return 0; |
674 | } | 677 | } |
675 | |||
676 | #endif /* CONFIG_MODULES */ | 678 | #endif /* CONFIG_MODULES */ |
677 | 679 | ||
678 | #ifdef CONFIG_SYSFS | 680 | #ifdef CONFIG_SYSFS |
@@ -687,6 +689,13 @@ extern int module_sysfs_initialized; | |||
687 | 689 | ||
688 | #define __MODULE_STRING(x) __stringify(x) | 690 | #define __MODULE_STRING(x) __stringify(x) |
689 | 691 | ||
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 | ||
690 | 699 | ||
691 | #ifdef CONFIG_GENERIC_BUG | 700 | #ifdef CONFIG_GENERIC_BUG |
692 | void module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *, | 701 | void module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *, |
diff --git a/kernel/module.c b/kernel/module.c index 437a74a7524a..ba421e6b4ada 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #include <linux/percpu.h> | 56 | #include <linux/percpu.h> |
57 | #include <linux/kmemleak.h> | 57 | #include <linux/kmemleak.h> |
58 | #include <linux/jump_label.h> | 58 | #include <linux/jump_label.h> |
59 | #include <linux/pfn.h> | ||
59 | 60 | ||
60 | #define CREATE_TRACE_POINTS | 61 | #define CREATE_TRACE_POINTS |
61 | #include <trace/events/module.h> | 62 | #include <trace/events/module.h> |
@@ -70,6 +71,26 @@ | |||
70 | #define ARCH_SHF_SMALL 0 | 71 | #define ARCH_SHF_SMALL 0 |
71 | #endif | 72 | #endif |
72 | 73 | ||
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 | |||
73 | /* If this is set, the section belongs in the init part of the module */ | 94 | /* If this is set, the section belongs in the init part of the module */ |
74 | #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) | 95 | #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) |
75 | 96 | ||
@@ -1542,6 +1563,115 @@ static int __unlink_module(void *_mod) | |||
1542 | return 0; | 1563 | return 0; |
1543 | } | 1564 | } |
1544 | 1565 | ||
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 | |||
1545 | /* Free a module, remove from lists, etc. */ | 1675 | /* Free a module, remove from lists, etc. */ |
1546 | static void free_module(struct module *mod) | 1676 | static void free_module(struct module *mod) |
1547 | { | 1677 | { |
@@ -1566,6 +1696,7 @@ static void free_module(struct module *mod) | |||
1566 | destroy_params(mod->kp, mod->num_kp); | 1696 | destroy_params(mod->kp, mod->num_kp); |
1567 | 1697 | ||
1568 | /* This may be NULL, but that's OK */ | 1698 | /* This may be NULL, but that's OK */ |
1699 | unset_section_ro_nx(mod, mod->module_init); | ||
1569 | module_free(mod, mod->module_init); | 1700 | module_free(mod, mod->module_init); |
1570 | kfree(mod->args); | 1701 | kfree(mod->args); |
1571 | percpu_modfree(mod); | 1702 | percpu_modfree(mod); |
@@ -1574,6 +1705,7 @@ static void free_module(struct module *mod) | |||
1574 | lockdep_free_key_range(mod->module_core, mod->core_size); | 1705 | lockdep_free_key_range(mod->module_core, mod->core_size); |
1575 | 1706 | ||
1576 | /* Finally, free the core (containing the module structure) */ | 1707 | /* Finally, free the core (containing the module structure) */ |
1708 | unset_section_ro_nx(mod, mod->module_core); | ||
1577 | module_free(mod, mod->module_core); | 1709 | module_free(mod, mod->module_core); |
1578 | 1710 | ||
1579 | #ifdef CONFIG_MPU | 1711 | #ifdef CONFIG_MPU |
@@ -1777,8 +1909,19 @@ static void layout_sections(struct module *mod, struct load_info *info) | |||
1777 | s->sh_entsize = get_offset(mod, &mod->core_size, s, i); | 1909 | s->sh_entsize = get_offset(mod, &mod->core_size, s, i); |
1778 | DEBUGP("\t%s\n", name); | 1910 | DEBUGP("\t%s\n", name); |
1779 | } | 1911 | } |
1780 | if (m == 0) | 1912 | switch (m) { |
1913 | case 0: /* executable */ | ||
1914 | mod->core_size = debug_align(mod->core_size); | ||
1781 | mod->core_text_size = mod->core_size; | 1915 | 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 | } | ||
1782 | } | 1925 | } |
1783 | 1926 | ||
1784 | DEBUGP("Init section allocation order:\n"); | 1927 | DEBUGP("Init section allocation order:\n"); |
@@ -1796,8 +1939,19 @@ static void layout_sections(struct module *mod, struct load_info *info) | |||
1796 | | INIT_OFFSET_MASK); | 1939 | | INIT_OFFSET_MASK); |
1797 | DEBUGP("\t%s\n", sname); | 1940 | DEBUGP("\t%s\n", sname); |
1798 | } | 1941 | } |
1799 | if (m == 0) | 1942 | switch (m) { |
1943 | case 0: /* executable */ | ||
1944 | mod->init_size = debug_align(mod->init_size); | ||
1800 | mod->init_text_size = mod->init_size; | 1945 | 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 | } | ||
1801 | } | 1955 | } |
1802 | } | 1956 | } |
1803 | 1957 | ||
@@ -2650,6 +2804,18 @@ static struct module *load_module(void __user *umod, | |||
2650 | kfree(info.strmap); | 2804 | kfree(info.strmap); |
2651 | free_copy(&info); | 2805 | free_copy(&info); |
2652 | 2806 | ||
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 | |||
2653 | /* Done! */ | 2819 | /* Done! */ |
2654 | trace_module_load(mod); | 2820 | trace_module_load(mod); |
2655 | return mod; | 2821 | return mod; |
@@ -2753,6 +2919,7 @@ SYSCALL_DEFINE3(init_module, void __user *, umod, | |||
2753 | mod->symtab = mod->core_symtab; | 2919 | mod->symtab = mod->core_symtab; |
2754 | mod->strtab = mod->core_strtab; | 2920 | mod->strtab = mod->core_strtab; |
2755 | #endif | 2921 | #endif |
2922 | unset_section_ro_nx(mod, mod->module_init); | ||
2756 | module_free(mod, mod->module_init); | 2923 | module_free(mod, mod->module_init); |
2757 | mod->module_init = NULL; | 2924 | mod->module_init = NULL; |
2758 | mod->init_size = 0; | 2925 | mod->init_size = 0; |