/** * probe_kernel_read(): safely attempt to read from a location * @dst: pointer to the buffer that shall take the data * @src: address to read from * @size: size of the data chunk * * Safely read from address @src to the buffer at @dst. If a kernel fault * happens, handle that and return -EFAULT. * * We ensure that the copy_from_user is executed in atomic context so that * do_page_fault() doesn't attempt to take mmap_sem. This makes * probe_kernel_read() suitable for use within regions where the caller * already holds mmap_sem, or other locks which nest inside mmap_sem. */
long __weak probe_kernel_read(void *dst, constvoid *src, size_t size) __attribute__((alias("__probe_kernel_read")));
long __probe_kernel_read(void *dst, constvoid *src, size_t size) { long ret; mm_segment_t old_fs = get_fs();
/* Lookup the address for this symbol. Returns 0 if not found. */ unsignedlongkallsyms_lookup_name(constchar *name) { char namebuf[KSYM_NAME_LEN]; unsignedlong i; unsignedint off;
for (i = 0, off = 0; i < kallsyms_num_syms; i++) { off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
/** * text_poke - Update instructions on a live kernel * @addr: address to modify * @opcode: source of the copy * @len: length to copy * * Only atomic text poke/set should be allowed when not doing early patching. * It means the size must be writable atomically and the address must be aligned * in a way that permits an atomic write. It also makes sure we fit on a single * page. */ void *text_poke(void *addr, constvoid *opcode, size_t len) { unsignedlong flags; char *vaddr; structpage *pages[2]; int i;
/* * While boot memory allocator is runnig we cannot use struct * pages as they are not yet initialized. */ BUG_ON(!after_bootmem);
lockdep_assert_held(&text_mutex);
if (!core_kernel_text((unsignedlong)addr)) { pages[0] = vmalloc_to_page(addr); pages[1] = vmalloc_to_page(addr + PAGE_SIZE); } else { pages[0] = virt_to_page(addr); WARN_ON(!PageReserved(pages[0])); pages[1] = virt_to_page(addr + PAGE_SIZE); } BUG_ON(!pages[0]); local_irq_save(flags); set_fixmap(FIX_TEXT_POKE0, page_to_phys(pages[0])); if (pages[1]) set_fixmap(FIX_TEXT_POKE1, page_to_phys(pages[1])); vaddr = (char *)fix_to_virt(FIX_TEXT_POKE0); memcpy(&vaddr[(unsignedlong)addr & ~PAGE_MASK], opcode, len); clear_fixmap(FIX_TEXT_POKE0); if (pages[1]) clear_fixmap(FIX_TEXT_POKE1); local_flush_tlb(); sync_core(); /* Could also do a CLFLUSH here to speed up CPU recovery; but that causes hangs on some VIA CPUs. */ for (i = 0; i < len; i++) BUG_ON(((char *)addr)[i] != ((char *)opcode)[i]); local_irq_restore(flags); return addr; }