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

execmem: add API for temporal remapping as RW and restoring ROX afterwards

Using a writable copy for ROX memory is cumbersome and error prone.

Add API that allow temporarily remapping of ranges in the ROX cache as
writable and then restoring their read-only-execute permissions.

This API will be later used in modules code and will allow removing nasty
games with writable copy in alternatives patching on x86.

The restoring of the ROX permissions relies on the ability of architecture
to reconstruct large pages in its set_memory_rox() method.

Signed-off-by: "Mike Rapoport (Microsoft)" <rppt@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20250126074733.1384926-6-rppt@kernel.org

authored by

Mike Rapoport (Microsoft) and committed by
Peter Zijlstra
05e555b8 925f4264

+53
+31
include/linux/execmem.h
··· 65 65 * Architectures that use EXECMEM_ROX_CACHE must implement this. 66 66 */ 67 67 void execmem_fill_trapping_insns(void *ptr, size_t size, bool writable); 68 + 69 + /** 70 + * execmem_make_temp_rw - temporarily remap region with read-write 71 + * permissions 72 + * @ptr: address of the region to remap 73 + * @size: size of the region to remap 74 + * 75 + * Remaps a part of the cached large page in the ROX cache in the range 76 + * [@ptr, @ptr + @size) as writable and not executable. The caller must 77 + * have exclusive ownership of this range and ensure nothing will try to 78 + * execute code in this range. 79 + * 80 + * Return: 0 on success or negative error code on failure. 81 + */ 82 + int execmem_make_temp_rw(void *ptr, size_t size); 83 + 84 + /** 85 + * execmem_restore_rox - restore read-only-execute permissions 86 + * @ptr: address of the region to remap 87 + * @size: size of the region to remap 88 + * 89 + * Restores read-only-execute permissions on a range [@ptr, @ptr + @size) 90 + * after it was temporarily remapped as writable. Relies on architecture 91 + * implementation of set_memory_rox() to restore mapping using large pages. 92 + * 93 + * Return: 0 on success or negative error code on failure. 94 + */ 95 + int execmem_restore_rox(void *ptr, size_t size); 96 + #else 97 + static inline int execmem_make_temp_rw(void *ptr, size_t size) { return 0; } 98 + static inline int execmem_restore_rox(void *ptr, size_t size) { return 0; } 68 99 #endif 69 100 70 101 /**
+22
mm/execmem.c
··· 335 335 336 336 return true; 337 337 } 338 + 339 + int execmem_make_temp_rw(void *ptr, size_t size) 340 + { 341 + unsigned int nr = PAGE_ALIGN(size) >> PAGE_SHIFT; 342 + unsigned long addr = (unsigned long)ptr; 343 + int ret; 344 + 345 + ret = set_memory_nx(addr, nr); 346 + if (ret) 347 + return ret; 348 + 349 + return set_memory_rw(addr, nr); 350 + } 351 + 352 + int execmem_restore_rox(void *ptr, size_t size) 353 + { 354 + unsigned int nr = PAGE_ALIGN(size) >> PAGE_SHIFT; 355 + unsigned long addr = (unsigned long)ptr; 356 + 357 + return set_memory_rox(addr, nr); 358 + } 359 + 338 360 #else /* CONFIG_ARCH_HAS_EXECMEM_ROX */ 339 361 static void *execmem_cache_alloc(struct execmem_range *range, size_t size) 340 362 {