diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2015-11-25 18:15:08 -0500 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2015-12-04 16:46:26 -0500 |
| commit | 85c898db6327353d38f3dd428457384cf81f83f8 (patch) | |
| tree | 9a353d57bbf1702d75d2854305d10e4bc9b9fc22 | |
| parent | 7523e4dc5057e157212b4741abd6256e03404cf1 (diff) | |
module: clean up RO/NX handling.
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.
Reviewed-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
| -rw-r--r-- | include/linux/module.h | 4 | ||||
| -rw-r--r-- | 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..4560d8f1545d 100644 --- a/include/linux/module.h +++ b/include/linux/module.h | |||
| @@ -762,9 +762,13 @@ extern int module_sysfs_initialized; | |||
| 762 | #ifdef CONFIG_DEBUG_SET_MODULE_RONX | 762 | #ifdef CONFIG_DEBUG_SET_MODULE_RONX |
| 763 | extern void set_all_modules_text_rw(void); | 763 | extern void set_all_modules_text_rw(void); |
| 764 | extern void set_all_modules_text_ro(void); | 764 | extern void set_all_modules_text_ro(void); |
| 765 | extern void module_enable_ro(const struct module *mod); | ||
| 766 | extern void module_disable_ro(const struct module *mod); | ||
| 765 | #else | 767 | #else |
| 766 | static inline void set_all_modules_text_rw(void) { } | 768 | static inline void set_all_modules_text_rw(void) { } |
| 767 | static inline void set_all_modules_text_ro(void) { } | 769 | static inline void set_all_modules_text_ro(void) { } |
| 770 | static inline void module_enable_ro(const struct module *mod) { } | ||
| 771 | static inline void module_disable_ro(const struct module *mod) { } | ||
| 768 | #endif | 772 | #endif |
| 769 | 773 | ||
| 770 | #ifdef CONFIG_GENERIC_BUG | 774 | #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 @@ | |||
| 80 | # define debug_align(X) (X) | 80 | # define debug_align(X) (X) |
| 81 | #endif | 81 | #endif |
| 82 | 82 | ||
| 83 | /* | ||
| 84 | * Given BASE and SIZE this macro calculates the number of pages the | ||
| 85 | * memory regions occupies | ||
| 86 | */ | ||
| 87 | #define MOD_NUMBER_OF_PAGES(BASE, SIZE) (((SIZE) > 0) ? \ | ||
| 88 | (PFN_DOWN((unsigned long)(BASE) + (SIZE) - 1) - \ | ||
| 89 | PFN_DOWN((unsigned long)BASE) + 1) \ | ||
| 90 | : (0UL)) | ||
| 91 | |||
| 92 | /* If this is set, the section belongs in the init part of the module */ | 83 | /* If this is set, the section belongs in the init part of the module */ |
| 93 | #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) | 84 | #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) |
| 94 | 85 | ||
| @@ -1858,74 +1849,75 @@ static void mod_sysfs_teardown(struct module *mod) | |||
| 1858 | /* | 1849 | /* |
| 1859 | * LKM RO/NX protection: protect module's text/ro-data | 1850 | * LKM RO/NX protection: protect module's text/ro-data |
| 1860 | * from modification and any data from execution. | 1851 | * from modification and any data from execution. |
| 1852 | * | ||
| 1853 | * General layout of module is: | ||
| 1854 | * [text] [read-only-data] [writable data] | ||
| 1855 | * text_size -----^ ^ ^ | ||
| 1856 | * ro_size ------------------------| | | ||
| 1857 | * size -------------------------------------------| | ||
| 1858 | * | ||
| 1859 | * These values are always page-aligned (as is base) | ||
| 1861 | */ | 1860 | */ |
| 1862 | void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages)) | 1861 | static void frob_text(const struct module_layout *layout, |
| 1862 | int (*set_memory)(unsigned long start, int num_pages)) | ||
| 1863 | { | 1863 | { |
| 1864 | unsigned long begin_pfn = PFN_DOWN((unsigned long)start); | 1864 | BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1)); |
| 1865 | unsigned long end_pfn = PFN_DOWN((unsigned long)end); | 1865 | BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1)); |
| 1866 | 1866 | set_memory((unsigned long)layout->base, | |
| 1867 | if (end_pfn > begin_pfn) | 1867 | layout->text_size >> PAGE_SHIFT); |
| 1868 | set(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn); | ||
| 1869 | } | 1868 | } |
| 1870 | 1869 | ||
| 1871 | static void set_section_ro_nx(void *base, | 1870 | static void frob_rodata(const struct module_layout *layout, |
| 1872 | unsigned long text_size, | 1871 | int (*set_memory)(unsigned long start, int num_pages)) |
| 1873 | unsigned long ro_size, | ||
| 1874 | unsigned long total_size, | ||
| 1875 | int (*set_ro)(unsigned long start, int num_pages), | ||
| 1876 | int (*set_nx)(unsigned long start, int num_pages)) | ||
| 1877 | { | 1872 | { |
| 1878 | /* begin and end PFNs of the current subsection */ | 1873 | BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1)); |
| 1879 | unsigned long begin_pfn; | 1874 | BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1)); |
| 1880 | unsigned long end_pfn; | 1875 | BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1)); |
| 1881 | 1876 | set_memory((unsigned long)layout->base + layout->text_size, | |
| 1882 | /* | 1877 | (layout->ro_size - layout->text_size) >> PAGE_SHIFT); |
| 1883 | * Set RO for module text and RO-data: | 1878 | } |
| 1884 | * - Always protect first page. | ||
| 1885 | * - Do not protect last partial page. | ||
| 1886 | */ | ||
| 1887 | if (ro_size > 0) | ||
| 1888 | set_page_attributes(base, base + ro_size, set_ro); | ||
| 1889 | 1879 | ||
| 1890 | /* | 1880 | static void frob_writable_data(const struct module_layout *layout, |
| 1891 | * Set NX permissions for module data: | 1881 | int (*set_memory)(unsigned long start, int num_pages)) |
| 1892 | * - Do not protect first partial page. | 1882 | { |
| 1893 | * - Always protect last page. | 1883 | BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1)); |
| 1894 | */ | 1884 | BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1)); |
| 1895 | if (total_size > text_size) { | 1885 | BUG_ON((unsigned long)layout->size & (PAGE_SIZE-1)); |
| 1896 | begin_pfn = PFN_UP((unsigned long)base + text_size); | 1886 | set_memory((unsigned long)layout->base + layout->ro_size, |
| 1897 | end_pfn = PFN_UP((unsigned long)base + total_size); | 1887 | (layout->size - layout->ro_size) >> PAGE_SHIFT); |
| 1898 | if (end_pfn > begin_pfn) | ||
| 1899 | set_nx(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn); | ||
| 1900 | } | ||
| 1901 | } | 1888 | } |
| 1902 | 1889 | ||
| 1903 | static void set_module_core_ro_nx(struct module *mod) | 1890 | /* livepatching wants to disable read-only so it can frob module. */ |
| 1891 | void module_disable_ro(const struct module *mod) | ||
| 1904 | { | 1892 | { |
| 1905 | set_section_ro_nx(mod->core_layout.base, mod->core_layout.text_size, | 1893 | frob_text(&mod->core_layout, set_memory_rw); |
| 1906 | mod->core_layout.ro_size, mod->core_layout.size, | 1894 | frob_rodata(&mod->core_layout, set_memory_rw); |
| 1907 | set_memory_ro, set_memory_nx); | 1895 | frob_text(&mod->init_layout, set_memory_rw); |
| 1896 | frob_rodata(&mod->init_layout, set_memory_rw); | ||
| 1908 | } | 1897 | } |
| 1909 | 1898 | ||
| 1910 | static void unset_module_core_ro_nx(struct module *mod) | 1899 | void module_enable_ro(const struct module *mod) |
| 1911 | { | 1900 | { |
| 1912 | set_section_ro_nx(mod->core_layout.base, mod->core_layout.text_size, | 1901 | frob_text(&mod->core_layout, set_memory_ro); |
| 1913 | mod->core_layout.ro_size, mod->core_layout.size, | 1902 | frob_rodata(&mod->core_layout, set_memory_ro); |
| 1914 | set_memory_rw, set_memory_x); | 1903 | frob_text(&mod->init_layout, set_memory_ro); |
| 1904 | frob_rodata(&mod->init_layout, set_memory_ro); | ||
| 1915 | } | 1905 | } |
| 1916 | 1906 | ||
| 1917 | static void set_module_init_ro_nx(struct module *mod) | 1907 | static void module_enable_nx(const struct module *mod) |
| 1918 | { | 1908 | { |
| 1919 | set_section_ro_nx(mod->init_layout.base, mod->init_layout.text_size, | 1909 | frob_rodata(&mod->core_layout, set_memory_nx); |
| 1920 | mod->init_layout.ro_size, mod->init_layout.size, | 1910 | frob_writable_data(&mod->core_layout, set_memory_nx); |
| 1921 | set_memory_ro, set_memory_nx); | 1911 | frob_rodata(&mod->init_layout, set_memory_nx); |
| 1912 | frob_writable_data(&mod->init_layout, set_memory_nx); | ||
| 1922 | } | 1913 | } |
| 1923 | 1914 | ||
| 1924 | static void unset_module_init_ro_nx(struct module *mod) | 1915 | static void module_disable_nx(const struct module *mod) |
| 1925 | { | 1916 | { |
| 1926 | set_section_ro_nx(mod->init_layout.base, mod->init_layout.text_size, | 1917 | frob_rodata(&mod->core_layout, set_memory_x); |
| 1927 | mod->init_layout.ro_size, mod->init_layout.size, | 1918 | frob_writable_data(&mod->core_layout, set_memory_x); |
| 1928 | set_memory_rw, set_memory_x); | 1919 | frob_rodata(&mod->init_layout, set_memory_x); |
| 1920 | frob_writable_data(&mod->init_layout, set_memory_x); | ||
| 1929 | } | 1921 | } |
| 1930 | 1922 | ||
| 1931 | /* Iterate through all modules and set each module's text as RW */ | 1923 | /* Iterate through all modules and set each module's text as RW */ |
| @@ -1937,16 +1929,9 @@ void set_all_modules_text_rw(void) | |||
| 1937 | list_for_each_entry_rcu(mod, &modules, list) { | 1929 | list_for_each_entry_rcu(mod, &modules, list) { |
| 1938 | if (mod->state == MODULE_STATE_UNFORMED) | 1930 | if (mod->state == MODULE_STATE_UNFORMED) |
| 1939 | continue; | 1931 | continue; |
| 1940 | if ((mod->core_layout.base) && (mod->core_layout.text_size)) { | 1932 | |
| 1941 | set_page_attributes(mod->core_layout.base, | 1933 | frob_text(&mod->core_layout, set_memory_rw); |
| 1942 | mod->core_layout.base + mod->core_layout.text_size, | 1934 | frob_text(&mod->init_layout, set_memory_rw); |
| 1943 | set_memory_rw); | ||
| 1944 | } | ||
| 1945 | if ((mod->init_layout.base) && (mod->init_layout.text_size)) { | ||
| 1946 | set_page_attributes(mod->init_layout.base, | ||
| 1947 | mod->init_layout.base + mod->init_layout.text_size, | ||
| 1948 | set_memory_rw); | ||
| 1949 | } | ||
| 1950 | } | 1935 | } |
| 1951 | mutex_unlock(&module_mutex); | 1936 | mutex_unlock(&module_mutex); |
| 1952 | } | 1937 | } |
| @@ -1960,24 +1945,25 @@ void set_all_modules_text_ro(void) | |||
| 1960 | list_for_each_entry_rcu(mod, &modules, list) { | 1945 | list_for_each_entry_rcu(mod, &modules, list) { |
| 1961 | if (mod->state == MODULE_STATE_UNFORMED) | 1946 | if (mod->state == MODULE_STATE_UNFORMED) |
| 1962 | continue; | 1947 | continue; |
| 1963 | if ((mod->core_layout.base) && (mod->core_layout.text_size)) { | 1948 | |
| 1964 | set_page_attributes(mod->core_layout.base, | 1949 | frob_text(&mod->core_layout, set_memory_ro); |
| 1965 | mod->core_layout.base + mod->core_layout.text_size, | 1950 | frob_text(&mod->init_layout, set_memory_ro); |
| 1966 | set_memory_ro); | ||
| 1967 | } | ||
| 1968 | if ((mod->init_layout.base) && (mod->init_layout.text_size)) { | ||
| 1969 | set_page_attributes(mod->init_layout.base, | ||
| 1970 | mod->init_layout.base + mod->init_layout.text_size, | ||
| 1971 | set_memory_ro); | ||
| 1972 | } | ||
| 1973 | } | 1951 | } |
| 1974 | mutex_unlock(&module_mutex); | 1952 | mutex_unlock(&module_mutex); |
| 1975 | } | 1953 | } |
| 1954 | |||
| 1955 | static void disable_ro_nx(const struct module_layout *layout) | ||
| 1956 | { | ||
| 1957 | frob_text(layout, set_memory_rw); | ||
| 1958 | frob_rodata(layout, set_memory_rw); | ||
| 1959 | frob_rodata(layout, set_memory_x); | ||
| 1960 | frob_writable_data(layout, set_memory_x); | ||
| 1961 | } | ||
| 1962 | |||
| 1976 | #else | 1963 | #else |
| 1977 | static void set_module_core_ro_nx(struct module *mod) { } | 1964 | static void disable_ro_nx(const struct module_layout *layout) { } |
| 1978 | static void set_module_init_ro_nx(struct module *mod) { } | 1965 | static void module_enable_nx(const struct module *mod) { } |
| 1979 | static void unset_module_core_ro_nx(struct module *mod) { } | 1966 | static void module_disable_nx(const struct module *mod) { } |
| 1980 | static void unset_module_init_ro_nx(struct module *mod) { } | ||
| 1981 | #endif | 1967 | #endif |
| 1982 | 1968 | ||
| 1983 | void __weak module_memfree(void *module_region) | 1969 | void __weak module_memfree(void *module_region) |
| @@ -2029,8 +2015,8 @@ static void free_module(struct module *mod) | |||
| 2029 | synchronize_sched(); | 2015 | synchronize_sched(); |
| 2030 | mutex_unlock(&module_mutex); | 2016 | mutex_unlock(&module_mutex); |
| 2031 | 2017 | ||
| 2032 | /* This may be NULL, but that's OK */ | 2018 | /* This may be empty, but that's OK */ |
| 2033 | unset_module_init_ro_nx(mod); | 2019 | disable_ro_nx(&mod->init_layout); |
| 2034 | module_arch_freeing_init(mod); | 2020 | module_arch_freeing_init(mod); |
| 2035 | module_memfree(mod->init_layout.base); | 2021 | module_memfree(mod->init_layout.base); |
| 2036 | kfree(mod->args); | 2022 | kfree(mod->args); |
| @@ -2040,7 +2026,7 @@ static void free_module(struct module *mod) | |||
| 2040 | lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size); | 2026 | lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size); |
| 2041 | 2027 | ||
| 2042 | /* Finally, free the core (containing the module structure) */ | 2028 | /* Finally, free the core (containing the module structure) */ |
| 2043 | unset_module_core_ro_nx(mod); | 2029 | disable_ro_nx(&mod->core_layout); |
| 2044 | module_memfree(mod->core_layout.base); | 2030 | module_memfree(mod->core_layout.base); |
| 2045 | 2031 | ||
| 2046 | #ifdef CONFIG_MPU | 2032 | #ifdef CONFIG_MPU |
| @@ -3275,7 +3261,7 @@ static noinline int do_init_module(struct module *mod) | |||
| 3275 | mod->strtab = mod->core_strtab; | 3261 | mod->strtab = mod->core_strtab; |
| 3276 | #endif | 3262 | #endif |
| 3277 | mod_tree_remove_init(mod); | 3263 | mod_tree_remove_init(mod); |
| 3278 | unset_module_init_ro_nx(mod); | 3264 | disable_ro_nx(&mod->init_layout); |
| 3279 | module_arch_freeing_init(mod); | 3265 | module_arch_freeing_init(mod); |
| 3280 | mod->init_layout.base = NULL; | 3266 | mod->init_layout.base = NULL; |
| 3281 | mod->init_layout.size = 0; | 3267 | mod->init_layout.size = 0; |
| @@ -3370,8 +3356,8 @@ static int complete_formation(struct module *mod, struct load_info *info) | |||
| 3370 | module_bug_finalize(info->hdr, info->sechdrs, mod); | 3356 | module_bug_finalize(info->hdr, info->sechdrs, mod); |
| 3371 | 3357 | ||
| 3372 | /* Set RO and NX regions */ | 3358 | /* Set RO and NX regions */ |
| 3373 | set_module_init_ro_nx(mod); | 3359 | module_enable_ro(mod); |
| 3374 | set_module_core_ro_nx(mod); | 3360 | module_enable_nx(mod); |
| 3375 | 3361 | ||
| 3376 | /* Mark state as coming so strong_try_module_get() ignores us, | 3362 | /* Mark state as coming so strong_try_module_get() ignores us, |
| 3377 | * but kallsyms etc. can see us. */ | 3363 | * but kallsyms etc. can see us. */ |
| @@ -3536,8 +3522,8 @@ static int load_module(struct load_info *info, const char __user *uargs, | |||
| 3536 | MODULE_STATE_GOING, mod); | 3522 | MODULE_STATE_GOING, mod); |
| 3537 | 3523 | ||
| 3538 | /* we can't deallocate the module until we clear memory protection */ | 3524 | /* we can't deallocate the module until we clear memory protection */ |
| 3539 | unset_module_init_ro_nx(mod); | 3525 | module_disable_ro(mod); |
| 3540 | unset_module_core_ro_nx(mod); | 3526 | module_disable_nx(mod); |
| 3541 | 3527 | ||
| 3542 | ddebug_cleanup: | 3528 | ddebug_cleanup: |
| 3543 | dynamic_debug_remove(info->debug); | 3529 | dynamic_debug_remove(info->debug); |
