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