Skip to content

Commit 0f01fb3

Browse files
committed
module: prepare to handle ROX allocations for text
jira LE-4694 Rebuild_History Non-Buildable kernel-6.12.0-55.43.1.el10_0 commit-author Mike Rapoport (Microsoft) <[email protected]> commit 0c133b1 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. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Mike Rapoport (Microsoft) <[email protected]> Reviewd-by: Luis Chamberlain <[email protected]> Tested-by: kdevops <[email protected]> Cc: Andreas Larsson <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Arnd Bergmann <[email protected]> Cc: Borislav Petkov (AMD) <[email protected]> Cc: Brian Cain <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Christophe Leroy <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Dinh Nguyen <[email protected]> Cc: Geert Uytterhoeven <[email protected]> Cc: Guo Ren <[email protected]> Cc: Helge Deller <[email protected]> Cc: Huacai Chen <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Johannes Berg <[email protected]> Cc: John Paul Adrian Glaubitz <[email protected]> Cc: Kent Overstreet <[email protected]> Cc: Liam R. Howlett <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Masami Hiramatsu (Google) <[email protected]> Cc: Matt Turner <[email protected]> Cc: Max Filippov <[email protected]> Cc: Michael Ellerman <[email protected]> Cc: Michal Simek <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: Palmer Dabbelt <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Richard Weinberger <[email protected]> Cc: Russell King <[email protected]> Cc: Song Liu <[email protected]> Cc: Stafford Horne <[email protected]> Cc: Steven Rostedt (Google) <[email protected]> Cc: Suren Baghdasaryan <[email protected]> Cc: Thomas Bogendoerfer <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Uladzislau Rezki (Sony) <[email protected]> Cc: Vineet Gupta <[email protected]> Cc: Will Deacon <[email protected]> Signed-off-by: Andrew Morton <[email protected]> (cherry picked from commit 0c133b1) Signed-off-by: Jonathan Maple <[email protected]>
1 parent d9905b2 commit 0f01fb3

File tree

7 files changed

+126
-8
lines changed

7 files changed

+126
-8
lines changed

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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,8 @@ 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
@@ -768,6 +770,15 @@ static inline bool is_livepatch_module(struct module *mod)
768770

769771
void set_module_sig_enforced(void);
770772

773+
void *__module_writable_address(struct module *mod, void *loc);
774+
775+
static inline void *module_writable_address(struct module *mod, void *loc)
776+
{
777+
if (!IS_ENABLED(CONFIG_ARCH_HAS_EXECMEM_ROX) || !mod)
778+
return loc;
779+
return __module_writable_address(mod, loc);
780+
}
781+
771782
#else /* !CONFIG_MODULES... */
772783

773784
static inline struct module *__module_address(unsigned long addr)
@@ -875,6 +886,11 @@ static inline bool module_is_coming(struct module *mod)
875886
{
876887
return false;
877888
}
889+
890+
static inline void *module_writable_address(struct module *mod, void *loc)
891+
{
892+
return loc;
893+
}
878894
#endif /* CONFIG_MODULES */
879895

880896
#ifdef CONFIG_SYSFS

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: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,18 @@ void __weak module_arch_freeing_init(struct module *mod)
11931193
{
11941194
}
11951195

1196+
void *__module_writable_address(struct module *mod, void *loc)
1197+
{
1198+
for_class_mod_mem_type(type, text) {
1199+
struct module_memory *mem = &mod->mem[type];
1200+
1201+
if (loc >= mem->base && loc < mem->base + mem->size)
1202+
return loc + (mem->rw_copy - mem->base);
1203+
}
1204+
1205+
return loc;
1206+
}
1207+
11961208
static int module_memory_alloc(struct module *mod, enum mod_mem_type type)
11971209
{
11981210
unsigned int size = PAGE_ALIGN(mod->mem[type].size);
@@ -1210,6 +1222,23 @@ static int module_memory_alloc(struct module *mod, enum mod_mem_type type)
12101222
if (!ptr)
12111223
return -ENOMEM;
12121224

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

1226-
memset(ptr, 0, size);
1227-
mod->mem[type].base = ptr;
1228-
12291255
return 0;
12301256
}
12311257

12321258
static void module_memory_free(struct module *mod, enum mod_mem_type type,
12331259
bool unload_codetags)
12341260
{
1235-
void *ptr = mod->mem[type].base;
1261+
struct module_memory *mem = &mod->mem[type];
1262+
void *ptr = mem->base;
1263+
1264+
if (mem->is_rox)
1265+
vfree(mem->rw_copy);
12361266

12371267
if (!unload_codetags && mod_mem_type_is_core_data(type))
12381268
return;
@@ -2255,6 +2285,7 @@ static int move_module(struct module *mod, struct load_info *info)
22552285
for_each_mod_mem_type(type) {
22562286
if (!mod->mem[type].size) {
22572287
mod->mem[type].base = NULL;
2288+
mod->mem[type].rw_copy = NULL;
22582289
continue;
22592290
}
22602291

@@ -2271,11 +2302,14 @@ static int move_module(struct module *mod, struct load_info *info)
22712302
void *dest;
22722303
Elf_Shdr *shdr = &info->sechdrs[i];
22732304
enum mod_mem_type type = shdr->sh_entsize >> SH_ENTSIZE_TYPE_SHIFT;
2305+
unsigned long offset = shdr->sh_entsize & SH_ENTSIZE_OFFSET_MASK;
2306+
unsigned long addr;
22742307

22752308
if (!(shdr->sh_flags & SHF_ALLOC))
22762309
continue;
22772310

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

22802314
if (shdr->sh_type != SHT_NOBITS) {
22812315
/*
@@ -2297,7 +2331,7 @@ static int move_module(struct module *mod, struct load_info *info)
22972331
* users of info can keep taking advantage and using the newly
22982332
* minted official memory area.
22992333
*/
2300-
shdr->sh_addr = (unsigned long)dest;
2334+
shdr->sh_addr = addr;
23012335
pr_debug("\t0x%lx 0x%.8lx %s\n", (long)shdr->sh_addr,
23022336
(long)shdr->sh_size, info->secstrings + shdr->sh_name);
23032337
}
@@ -2445,8 +2479,17 @@ int __weak module_finalize(const Elf_Ehdr *hdr,
24452479
return 0;
24462480
}
24472481

2482+
int __weak module_post_finalize(const Elf_Ehdr *hdr,
2483+
const Elf_Shdr *sechdrs,
2484+
struct module *me)
2485+
{
2486+
return 0;
2487+
}
2488+
24482489
static int post_relocation(struct module *mod, const struct load_info *info)
24492490
{
2491+
int ret;
2492+
24502493
/* Sort exception table now relocations are done. */
24512494
sort_extable(mod->extable, mod->extable + mod->num_exentries);
24522495

@@ -2458,7 +2501,24 @@ static int post_relocation(struct module *mod, const struct load_info *info)
24582501
add_kallsyms(mod, info);
24592502

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

24642524
/* 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)