Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | // SPDX-License-Identifier: GPL-2.0-or-later /* * Module strict rwx * * Copyright (C) 2015 Rusty Russell */ #include <linux/module.h> #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/set_memory.h> #include "internal.h" /* * 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] [ro-after-init] [writable data] * text_size -----^ ^ ^ ^ * ro_size ------------------------| | | * ro_after_init_size -----------------------------| | * size -----------------------------------------------------------| * * These values are always page-aligned (as is base) when * CONFIG_STRICT_MODULE_RWX is set. */ /* * Since some arches are moving towards PAGE_KERNEL module allocations instead * of PAGE_KERNEL_EXEC, keep frob_text() and module_enable_x() independent of * CONFIG_STRICT_MODULE_RWX because they are needed regardless of whether we * are strict. */ static void frob_text(const struct module_layout *layout, int (*set_memory)(unsigned long start, int num_pages)) { set_memory((unsigned long)layout->base, PAGE_ALIGN(layout->text_size) >> PAGE_SHIFT); } static void frob_rodata(const struct module_layout *layout, int (*set_memory)(unsigned long start, int num_pages)) { set_memory((unsigned long)layout->base + layout->text_size, (layout->ro_size - layout->text_size) >> PAGE_SHIFT); } static void frob_ro_after_init(const struct module_layout *layout, int (*set_memory)(unsigned long start, int num_pages)) { set_memory((unsigned long)layout->base + layout->ro_size, (layout->ro_after_init_size - layout->ro_size) >> PAGE_SHIFT); } static void frob_writable_data(const struct module_layout *layout, int (*set_memory)(unsigned long start, int num_pages)) { set_memory((unsigned long)layout->base + layout->ro_after_init_size, (layout->size - layout->ro_after_init_size) >> PAGE_SHIFT); } static bool layout_check_misalignment(const struct module_layout *layout) { return WARN_ON(!PAGE_ALIGNED(layout->base)) || WARN_ON(!PAGE_ALIGNED(layout->text_size)) || WARN_ON(!PAGE_ALIGNED(layout->ro_size)) || WARN_ON(!PAGE_ALIGNED(layout->ro_after_init_size)) || WARN_ON(!PAGE_ALIGNED(layout->size)); } bool module_check_misalignment(const struct module *mod) { if (!IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) return false; return layout_check_misalignment(&mod->core_layout) || layout_check_misalignment(&mod->data_layout) || layout_check_misalignment(&mod->init_layout); } void module_enable_x(const struct module *mod) { if (!PAGE_ALIGNED(mod->core_layout.base) || !PAGE_ALIGNED(mod->init_layout.base)) return; frob_text(&mod->core_layout, set_memory_x); frob_text(&mod->init_layout, set_memory_x); } void module_enable_ro(const struct module *mod, bool after_init) { if (!IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) return; #ifdef CONFIG_STRICT_MODULE_RWX if (!rodata_enabled) return; #endif set_vm_flush_reset_perms(mod->core_layout.base); set_vm_flush_reset_perms(mod->init_layout.base); frob_text(&mod->core_layout, set_memory_ro); frob_rodata(&mod->data_layout, set_memory_ro); frob_text(&mod->init_layout, set_memory_ro); frob_rodata(&mod->init_layout, set_memory_ro); if (after_init) frob_ro_after_init(&mod->data_layout, set_memory_ro); } void module_enable_nx(const struct module *mod) { if (!IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) return; frob_rodata(&mod->data_layout, set_memory_nx); frob_ro_after_init(&mod->data_layout, set_memory_nx); frob_writable_data(&mod->data_layout, set_memory_nx); frob_rodata(&mod->init_layout, set_memory_nx); frob_writable_data(&mod->init_layout, set_memory_nx); } int module_enforce_rwx_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, char *secstrings, struct module *mod) { const unsigned long shf_wx = SHF_WRITE | SHF_EXECINSTR; int i; if (!IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) return 0; for (i = 0; i < hdr->e_shnum; i++) { if ((sechdrs[i].sh_flags & shf_wx) == shf_wx) { pr_err("%s: section %s (index %d) has invalid WRITE|EXEC flags\n", mod->name, secstrings + sechdrs[i].sh_name, i); return -ENOEXEC; } } return 0; } |