static inline unsigned long native_pax_open_kernel(void) { unsigned long cr0; preempt_disable(); barrier(); cr0 = read_cr0() ^ X86_CR0_WP; BUG_ON(unlikely(cr0 & X86_CR0_WP)); write_cr0(cr0); return cr0 ^ X86_CR0_WP; } static inline unsigned long native_pax_close_kernel(void) { unsigned long cr0; cr0 = read_cr0() ^ X86_CR0_WP; BUG_ON(unlikely(!(cr0 & X86_CR0_WP))); write_cr0(cr0); barrier(); preempt_enable_no_resched(); return cr0 ^ X86_CR0_WP; }
/* * map_writable creates a shadow page mapping of the range * [addr, addr + len) so that we can write to code mapped read-only. * * It is similar to a generalized version of x86's text_poke. But * because one cannot use vmalloc/vfree() inside stop_machine, we use * map_writable to map the pages before stop_machine, then use the * mapping inside stop_machine, and unmap the pages afterwards. */ static void *map_writable(void *addr, size_t len) { void *vaddr; int nr_pages = DIV_ROUND_UP(offset_in_page(addr) + len, PAGE_SIZE); struct page **pages = kmalloc(nr_pages * sizeof(*pages), GFP_KERNEL); void *page_addr = (void *)((unsigned long)addr & PAGE_MASK); int i; if (pages == NULL) return NULL; for (i = 0; i < nr_pages; i++) { if (__module_address((unsigned long)page_addr) == NULL) { pages[i] = virt_to_page(page_addr); WARN_ON(!PageReserved(pages[i])); } else { pages[i] = vmalloc_to_page(page_addr); } if (pages[i] == NULL) { kfree(pages); return NULL; } page_addr += PAGE_SIZE; } vaddr = vmap(pages, nr_pages, VM_MAP, PAGE_KERNEL); kfree(pages); if (vaddr == NULL) return NULL; return vaddr + offset_in_page(addr); }
#include <linux/stop_machine.h> int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus)
stop_machine
executes the fn
function with a given set of processors active at the time of execution, which is set by the corresponding cpumask
mask. This is exactly what allows using this mechanism for modifying the kernel code, since setting the appropriate mask automatically eliminates the need to keep track of those kernel threads, the execution of which may affect the modified code.stop_machine
it is worth noting that the function being executed must work in atomic context, which automatically excludes the possibility of using the previously described mechanism for creating temporary mappings via vmap . However, this circumstance is not significant, because the required mappings can be prepared before calling stop_machine
.Source: https://habr.com/ru/post/207122/
All Articles