Skip to content

Commit 16b4e3b

Browse files
committed
module: prepare to handle ROX allocations for text
In order to support ROX allocations for module text, it is necessary to handle modifications to the code, such as relocations and alternatives patching, without write access to that memory. One option is to use text patching, but this would make module loading extremely slow and will expose executable code that is not finally formed. A better way is to have memory allocated with ROX permissions contain invalid instructions and keep a writable, but not executable copy of the module text. The relocations and alternative patches would be done on the writable copy using the addresses of the ROX memory. Once the module is completely ready, the updated text will be copied to ROX memory using text patching in one go and the writable copy will be freed. Add support for that to module initialization code and provide necessary interfaces in execmem. Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
1 parent 25d7b11 commit 16b4e3b

7 files changed

Lines changed: 124 additions & 8 deletions

File tree

include/linux/execmem.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ enum execmem_type {
4646
/**
4747
* enum execmem_range_flags - options for executable memory allocations
4848
* @EXECMEM_KASAN_SHADOW: allocate kasan shadow
49+
* @EXECMEM_ROX_CACHE: allocations should use ROX cache of huge pages
4950
*/
5051
enum execmem_range_flags {
5152
EXECMEM_KASAN_SHADOW = (1 << 0),
53+
EXECMEM_ROX_CACHE = (1 << 1),
5254
};
5355

5456
/**
@@ -123,6 +125,27 @@ void *execmem_alloc(enum execmem_type type, size_t size);
123125
*/
124126
void execmem_free(void *ptr);
125127

128+
/**
129+
* execmem_update_copy - copy an update to executable memory
130+
* @dst: destination address to update
131+
* @src: source address containing the data
132+
* @size: how many bytes of memory shold be copied
133+
*
134+
* Copy @size bytes from @src to @dst using text poking if the memory at
135+
* @dst is read-only.
136+
*
137+
* Return: a pointer to @dst or NULL on error
138+
*/
139+
void *execmem_update_copy(void *dst, const void *src, size_t size);
140+
141+
/**
142+
* execmem_is_rox - check if execmem is read-only
143+
* @type - the execmem type to check
144+
*
145+
* Return: %true if the @type is read-only, %false if it's writable
146+
*/
147+
bool execmem_is_rox(enum execmem_type type);
148+
126149
#if defined(CONFIG_EXECMEM) && !defined(CONFIG_ARCH_WANTS_EXECMEM_LATE)
127150
void execmem_init(void);
128151
#else

include/linux/module.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,13 +367,24 @@ enum mod_mem_type {
367367

368368
struct module_memory {
369369
void *base;
370+
void *rw_copy;
371+
bool is_rox;
370372
unsigned int size;
371373

372374
#ifdef CONFIG_MODULES_TREE_LOOKUP
373375
struct mod_tree_node mtn;
374376
#endif
375377
};
376378

379+
#ifdef CONFIG_MODULES
380+
void *module_writable_address(struct module *mod, void *loc);
381+
#else
382+
static inline void *module_writable_address(struct module *mod, void *loc)
383+
{
384+
return loc;
385+
}
386+
#endif
387+
377388
#ifdef CONFIG_MODULES_TREE_LOOKUP
378389
/* Only touch one cacheline for common rbtree-for-core-layout case. */
379390
#define __module_memory_align ____cacheline_aligned

include/linux/moduleloader.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ int module_finalize(const Elf_Ehdr *hdr,
108108
const Elf_Shdr *sechdrs,
109109
struct module *mod);
110110

111+
int module_post_finalize(const Elf_Ehdr *hdr,
112+
const Elf_Shdr *sechdrs,
113+
struct module *mod);
114+
111115
#ifdef CONFIG_MODULES
112116
void flush_module_init_free_work(void);
113117
#else

kernel/module/debug_kmemleak.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ void kmemleak_load_module(const struct module *mod,
1414
{
1515
/* only scan writable, non-executable sections */
1616
for_each_mod_mem_type(type) {
17-
if (type != MOD_DATA && type != MOD_INIT_DATA)
17+
if (type != MOD_DATA && type != MOD_INIT_DATA &&
18+
!mod->mem[type].is_rox)
1819
kmemleak_no_scan(mod->mem[type].base);
1920
}
2021
}

kernel/module/main.c

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,21 @@ void __weak module_arch_freeing_init(struct module *mod)
11891189
{
11901190
}
11911191

1192+
void *module_writable_address(struct module *mod, void *loc)
1193+
{
1194+
if (!mod)
1195+
return loc;
1196+
1197+
for_class_mod_mem_type(type, text) {
1198+
struct module_memory *mem = &mod->mem[type];
1199+
1200+
if (loc >= mem->base && loc < mem->base + mem->size)
1201+
return loc + (mem->rw_copy - mem->base);
1202+
}
1203+
1204+
return loc;
1205+
}
1206+
11921207
static int module_memory_alloc(struct module *mod, enum mod_mem_type type)
11931208
{
11941209
unsigned int size = PAGE_ALIGN(mod->mem[type].size);
@@ -1206,6 +1221,23 @@ static int module_memory_alloc(struct module *mod, enum mod_mem_type type)
12061221
if (!ptr)
12071222
return -ENOMEM;
12081223

1224+
mod->mem[type].base = ptr;
1225+
1226+
if (execmem_is_rox(execmem_type)) {
1227+
ptr = vzalloc(size);
1228+
1229+
if (!ptr) {
1230+
execmem_free(mod->mem[type].base);
1231+
return -ENOMEM;
1232+
}
1233+
1234+
mod->mem[type].rw_copy = ptr;
1235+
mod->mem[type].is_rox = true;
1236+
} else {
1237+
mod->mem[type].rw_copy = mod->mem[type].base;
1238+
memset(mod->mem[type].base, 0, size);
1239+
}
1240+
12091241
/*
12101242
* The pointer to these blocks of memory are stored on the module
12111243
* structure and we keep that around so long as the module is
@@ -1219,16 +1251,17 @@ static int module_memory_alloc(struct module *mod, enum mod_mem_type type)
12191251
*/
12201252
kmemleak_not_leak(ptr);
12211253

1222-
memset(ptr, 0, size);
1223-
mod->mem[type].base = ptr;
1224-
12251254
return 0;
12261255
}
12271256

12281257
static void module_memory_free(struct module *mod, enum mod_mem_type type,
12291258
bool unload_codetags)
12301259
{
1231-
void *ptr = mod->mem[type].base;
1260+
struct module_memory *mem = &mod->mem[type];
1261+
void *ptr = mem->base;
1262+
1263+
if (mem->is_rox)
1264+
vfree(mem->rw_copy);
12321265

12331266
if (!unload_codetags && mod_mem_type_is_core_data(type))
12341267
return;
@@ -2251,6 +2284,7 @@ static int move_module(struct module *mod, struct load_info *info)
22512284
for_each_mod_mem_type(type) {
22522285
if (!mod->mem[type].size) {
22532286
mod->mem[type].base = NULL;
2287+
mod->mem[type].rw_copy = NULL;
22542288
continue;
22552289
}
22562290

@@ -2267,11 +2301,14 @@ static int move_module(struct module *mod, struct load_info *info)
22672301
void *dest;
22682302
Elf_Shdr *shdr = &info->sechdrs[i];
22692303
enum mod_mem_type type = shdr->sh_entsize >> SH_ENTSIZE_TYPE_SHIFT;
2304+
unsigned long offset = shdr->sh_entsize & SH_ENTSIZE_OFFSET_MASK;
2305+
unsigned long addr;
22702306

22712307
if (!(shdr->sh_flags & SHF_ALLOC))
22722308
continue;
22732309

2274-
dest = mod->mem[type].base + (shdr->sh_entsize & SH_ENTSIZE_OFFSET_MASK);
2310+
addr = (unsigned long)mod->mem[type].base + offset;
2311+
dest = mod->mem[type].rw_copy + offset;
22752312

22762313
if (shdr->sh_type != SHT_NOBITS) {
22772314
/*
@@ -2293,7 +2330,7 @@ static int move_module(struct module *mod, struct load_info *info)
22932330
* users of info can keep taking advantage and using the newly
22942331
* minted official memory area.
22952332
*/
2296-
shdr->sh_addr = (unsigned long)dest;
2333+
shdr->sh_addr = addr;
22972334
pr_debug("\t0x%lx 0x%.8lx %s\n", (long)shdr->sh_addr,
22982335
(long)shdr->sh_size, info->secstrings + shdr->sh_name);
22992336
}
@@ -2441,8 +2478,17 @@ int __weak module_finalize(const Elf_Ehdr *hdr,
24412478
return 0;
24422479
}
24432480

2481+
int __weak module_post_finalize(const Elf_Ehdr *hdr,
2482+
const Elf_Shdr *sechdrs,
2483+
struct module *me)
2484+
{
2485+
return 0;
2486+
}
2487+
24442488
static int post_relocation(struct module *mod, const struct load_info *info)
24452489
{
2490+
int ret;
2491+
24462492
/* Sort exception table now relocations are done. */
24472493
sort_extable(mod->extable, mod->extable + mod->num_exentries);
24482494

@@ -2454,7 +2500,24 @@ static int post_relocation(struct module *mod, const struct load_info *info)
24542500
add_kallsyms(mod, info);
24552501

24562502
/* Arch-specific module finalizing. */
2457-
return module_finalize(info->hdr, info->sechdrs, mod);
2503+
ret = module_finalize(info->hdr, info->sechdrs, mod);
2504+
if (ret)
2505+
return ret;
2506+
2507+
for_each_mod_mem_type(type) {
2508+
struct module_memory *mem = &mod->mem[type];
2509+
2510+
if (mem->is_rox) {
2511+
if (!execmem_update_copy(mem->base, mem->rw_copy,
2512+
mem->size))
2513+
return -ENOMEM;
2514+
2515+
vfree(mem->rw_copy);
2516+
mem->rw_copy = NULL;
2517+
}
2518+
}
2519+
2520+
return module_post_finalize(info->hdr, info->sechdrs, mod);
24582521
}
24592522

24602523
/* Call module constructors. */

kernel/module/strict_rwx.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ int module_enable_text_rox(const struct module *mod)
3434
for_class_mod_mem_type(type, text) {
3535
int ret;
3636

37+
if (mod->mem[type].is_rox)
38+
continue;
39+
3740
if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
3841
ret = module_set_memory(mod, type, set_memory_rox);
3942
else

mm/execmem.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/vmalloc.h>
1111
#include <linux/execmem.h>
1212
#include <linux/moduleloader.h>
13+
#include <linux/text-patching.h>
1314

1415
static struct execmem_info *execmem_info __ro_after_init;
1516
static struct execmem_info default_execmem_info __ro_after_init;
@@ -69,6 +70,16 @@ void execmem_free(void *ptr)
6970
vfree(ptr);
7071
}
7172

73+
void *execmem_update_copy(void *dst, const void *src, size_t size)
74+
{
75+
return text_poke_copy(dst, src, size);
76+
}
77+
78+
bool execmem_is_rox(enum execmem_type type)
79+
{
80+
return !!(execmem_info->ranges[type].flags & EXECMEM_ROX_CACHE);
81+
}
82+
7283
static bool execmem_validate(struct execmem_info *info)
7384
{
7485
struct execmem_range *r = &info->ranges[EXECMEM_DEFAULT];

0 commit comments

Comments
 (0)