Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

execmem: introduce execmem_alloc_rw()

Some callers of execmem_alloc() require the memory to be temporarily
writable even when it is allocated from ROX cache. These callers use
execemem_make_temp_rw() right after the call to execmem_alloc().

Wrap this sequence in execmem_alloc_rw() API.

Link: https://lkml.kernel.org/r/20250713071730.4117334-3-rppt@kernel.org
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Reviewed-by: Daniel Gomez <da.gomez@samsung.com>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Cc: Steven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Mike Rapoport (Microsoft) and committed by
Andrew Morton
838955f6 fcd90ad3

+51 -30
+1 -2
arch/x86/kernel/alternative.c
··· 120 120 121 121 static void *__its_alloc(struct its_array *pages) 122 122 { 123 - void *page __free(execmem) = execmem_alloc(EXECMEM_MODULE_TEXT, PAGE_SIZE); 123 + void *page __free(execmem) = execmem_alloc_rw(EXECMEM_MODULE_TEXT, PAGE_SIZE); 124 124 if (!page) 125 125 return NULL; 126 126 ··· 237 237 if (!page) 238 238 return NULL; 239 239 240 - execmem_make_temp_rw(page, PAGE_SIZE); 241 240 if (pages == &its_pages) 242 241 set_memory_x((unsigned long)page, 1); 243 242
+22 -16
include/linux/execmem.h
··· 68 68 void execmem_fill_trapping_insns(void *ptr, size_t size, bool writable); 69 69 70 70 /** 71 - * execmem_make_temp_rw - temporarily remap region with read-write 72 - * permissions 73 - * @ptr: address of the region to remap 74 - * @size: size of the region to remap 75 - * 76 - * Remaps a part of the cached large page in the ROX cache in the range 77 - * [@ptr, @ptr + @size) as writable and not executable. The caller must 78 - * have exclusive ownership of this range and ensure nothing will try to 79 - * execute code in this range. 80 - * 81 - * Return: 0 on success or negative error code on failure. 82 - */ 83 - int execmem_make_temp_rw(void *ptr, size_t size); 84 - 85 - /** 86 71 * execmem_restore_rox - restore read-only-execute permissions 87 72 * @ptr: address of the region to remap 88 73 * @size: size of the region to remap ··· 80 95 */ 81 96 int execmem_restore_rox(void *ptr, size_t size); 82 97 #else 83 - static inline int execmem_make_temp_rw(void *ptr, size_t size) { return 0; } 84 98 static inline int execmem_restore_rox(void *ptr, size_t size) { return 0; } 85 99 #endif 86 100 ··· 148 164 * Return: a pointer to the allocated memory or %NULL 149 165 */ 150 166 void *execmem_alloc(enum execmem_type type, size_t size); 167 + 168 + /** 169 + * execmem_alloc_rw - allocate writable executable memory 170 + * @type: type of the allocation 171 + * @size: how many bytes of memory are required 172 + * 173 + * Allocates memory that will contain executable code, either generated or 174 + * loaded from kernel modules. 175 + * 176 + * Allocates memory that will contain data coupled with executable code, 177 + * like data sections in kernel modules. 178 + * 179 + * Forces writable permissions on the allocated memory and the caller is 180 + * responsible to manage the permissions afterwards. 181 + * 182 + * For architectures that use ROX cache the permissions will be set to R+W. 183 + * For architectures that don't use ROX cache the default permissions for @type 184 + * will be used as they must be writable. 185 + * 186 + * Return: a pointer to the allocated memory or %NULL 187 + */ 188 + void *execmem_alloc_rw(enum execmem_type type, size_t size); 151 189 152 190 /** 153 191 * execmem_free - free executable memory
+2 -11
kernel/module/main.c
··· 1292 1292 else 1293 1293 execmem_type = EXECMEM_MODULE_TEXT; 1294 1294 1295 - ptr = execmem_alloc(execmem_type, size); 1295 + ptr = execmem_alloc_rw(execmem_type, size); 1296 1296 if (!ptr) 1297 1297 return -ENOMEM; 1298 1298 1299 - if (execmem_is_rox(execmem_type)) { 1300 - int err = execmem_make_temp_rw(ptr, size); 1301 - 1302 - if (err) { 1303 - execmem_free(ptr); 1304 - return -ENOMEM; 1305 - } 1306 - 1307 - mod->mem[type].is_rox = true; 1308 - } 1299 + mod->mem[type].is_rox = execmem_is_rox(execmem_type); 1309 1300 1310 1301 /* 1311 1302 * The pointer to these blocks of memory are stored on the module
+26 -1
mm/execmem.c
··· 336 336 return true; 337 337 } 338 338 339 - int execmem_make_temp_rw(void *ptr, size_t size) 339 + static int execmem_force_rw(void *ptr, size_t size) 340 340 { 341 341 unsigned int nr = PAGE_ALIGN(size) >> PAGE_SHIFT; 342 342 unsigned long addr = (unsigned long)ptr; ··· 358 358 } 359 359 360 360 #else /* CONFIG_ARCH_HAS_EXECMEM_ROX */ 361 + /* 362 + * when ROX cache is not used the permissions defined by architectures for 363 + * execmem ranges that are updated before use (e.g. EXECMEM_MODULE_TEXT) must 364 + * be writable anyway 365 + */ 366 + static inline int execmem_force_rw(void *ptr, size_t size) 367 + { 368 + return 0; 369 + } 370 + 361 371 static void *execmem_cache_alloc(struct execmem_range *range, size_t size) 362 372 { 363 373 return NULL; ··· 395 385 p = execmem_vmalloc(range, size, pgprot, vm_flags); 396 386 397 387 return kasan_reset_tag(p); 388 + } 389 + 390 + void *execmem_alloc_rw(enum execmem_type type, size_t size) 391 + { 392 + void *p __free(execmem) = execmem_alloc(type, size); 393 + int err; 394 + 395 + if (!p) 396 + return NULL; 397 + 398 + err = execmem_force_rw(p, size); 399 + if (err) 400 + return NULL; 401 + 402 + return no_free_ptr(p); 398 403 } 399 404 400 405 void execmem_free(void *ptr)