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) <rppt@kernel.org> 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/20241023162711.2579610-5-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org> Reviewd-by: Luis Chamberlain <mcgrof@kernel.org> Tested-by: kdevops <kdevops@lists.linux.dev> Cc: Andreas Larsson <andreas@gaisler.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Ard Biesheuvel <ardb@kernel.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Borislav Petkov (AMD) <bp@alien8.de> Cc: Brian Cain <bcain@quicinc.com> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Christoph Hellwig <hch@lst.de> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: Dinh Nguyen <dinguyen@kernel.org> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Cc: Guo Ren <guoren@kernel.org> Cc: Helge Deller <deller@gmx.de> Cc: Huacai Chen <chenhuacai@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Johannes Berg <johannes@sipsolutions.net> Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Cc: Kent Overstreet <kent.overstreet@linux.dev> Cc: Liam R. Howlett <Liam.Howlett@Oracle.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Masami Hiramatsu (Google) <mhiramat@kernel.org> Cc: Matt Turner <mattst88@gmail.com> Cc: Max Filippov <jcmvbkbc@gmail.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Michal Simek <monstr@monstr.eu> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Richard Weinberger <richard@nod.at> Cc: Russell King <linux@armlinux.org.uk> Cc: Song Liu <song@kernel.org> Cc: Stafford Horne <shorne@gmail.com> Cc: Steven Rostedt (Google) <rostedt@goodmis.org> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Uladzislau Rezki (Sony) <urezki@gmail.com> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Will Deacon <will@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> (cherry picked from commit 0c133b1) Signed-off-by: Jonathan Maple <jmaple@ciq.com>
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)