From 4af9d5d591f33a0d8d7fb0735e280fa51ccef53e Mon Sep 17 00:00:00 2001 From: Christian Hoff Date: Mon, 8 Mar 2021 20:19:16 +0100 Subject: [PATCH] Fix compilation of binder and ashmem on kernel 5.7 and later On kernel 5.7 and later, kallsyms_lookup_name() can no longer be called from a kernel module for reasons described here: https://lwn.net/Articles/813350/ As binder really needs to use kallsysms_lookup_name() to access some kernel functions that otherwise wouldn't be accessible, KProbes are used on later kernels to get the address of kallsysms_lookup_name(). The function is afterwards used just as before. This is a very dirty hack though and the much better solution would be if all the functions that are currently resolved with kallsysms_lookup_name() would get an EXPORT_SYMBOL() annotation to make them directly accessible to kernel modules. --- ashmem/deps.c | 60 ++++++++++++++++++++++++++++++++++- binder/deps.c | 88 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 132 insertions(+), 16 deletions(-) diff --git a/ashmem/deps.c b/ashmem/deps.c index ce6016f..4a89308 100644 --- a/ashmem/deps.c +++ b/ashmem/deps.c @@ -1,11 +1,69 @@ #include #include +#include +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)) + +#ifndef CONFIG_KPROBES +# error "Your kernel does not support KProbes, but this is required to compile ashmem as a kernel module on kernel 5.7 and later" +#endif + +typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); + +static int dummy_kprobe_handler(struct kprobe *p, struct pt_regs *regs) +{ + return 0; +} + +static kallsyms_lookup_name_t get_kallsyms_lookup_name_ptr(void) +{ + struct kprobe probe; + int ret; + kallsyms_lookup_name_t addr; + + memset(&probe, 0, sizeof(probe)); + probe.pre_handler = dummy_kprobe_handler; + probe.symbol_name = "kallsyms_lookup_name"; + ret = register_kprobe(&probe); + if (ret) + return NULL; + addr = (kallsyms_lookup_name_t) probe.addr; + unregister_kprobe(&probe); + + return addr; +} +#endif + +/* + * On kernel 5.7 and later, kallsyms_lookup_name() can no longer be called from a kernel + * module for reasons described here: https://lwn.net/Articles/813350/ + * As ashmem really needs to use kallsysms_lookup_name() to access some kernel + * functions that otherwise wouldn't be accessible, KProbes are used on later + * kernels to get the address of kallsysms_lookup_name(). The function is + * afterwards used just as before. This is a very dirty hack though and the much + * better solution would be if all the functions that are currently resolved + * with kallsysms_lookup_name() would get an EXPORT_SYMBOL() annotation to + * make them directly accessible to kernel modules. + */ +static unsigned long kallsyms_lookup_name_wrapper(const char *name) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)) + static kallsyms_lookup_name_t func_ptr = NULL; + if (!func_ptr) + func_ptr = get_kallsyms_lookup_name_ptr(); + + return func_ptr(name); +#else + return kallsyms_lookup_name(name); +#endif +} static int (*shmem_zero_setup_ptr)(struct vm_area_struct *) = NULL; int shmem_zero_setup(struct vm_area_struct *vma) { if (!shmem_zero_setup_ptr) - shmem_zero_setup_ptr = kallsyms_lookup_name("shmem_zero_setup"); + shmem_zero_setup_ptr = kallsyms_lookup_name_wrapper("shmem_zero_setup"); return shmem_zero_setup_ptr(vma); } diff --git a/binder/deps.c b/binder/deps.c index 774b8b1..6daef72 100644 --- a/binder/deps.c +++ b/binder/deps.c @@ -6,8 +6,66 @@ #include #include #include +#include #include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)) + +#ifndef CONFIG_KPROBES +# error "Your kernel does not support KProbes, but this is required to compile binder as a kernel module on kernel 5.7 and later" +#endif + +typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); + +static int dummy_kprobe_handler(struct kprobe *p, struct pt_regs *regs) +{ + return 0; +} + +static kallsyms_lookup_name_t get_kallsyms_lookup_name_ptr(void) +{ + struct kprobe probe; + int ret; + kallsyms_lookup_name_t addr; + + memset(&probe, 0, sizeof(probe)); + probe.pre_handler = dummy_kprobe_handler; + probe.symbol_name = "kallsyms_lookup_name"; + ret = register_kprobe(&probe); + if (ret) + return NULL; + addr = (kallsyms_lookup_name_t) probe.addr; + unregister_kprobe(&probe); + + return addr; +} +#endif + +/* + * On kernel 5.7 and later, kallsyms_lookup_name() can no longer be called from a kernel + * module for reasons described here: https://lwn.net/Articles/813350/ + * As binder really needs to use kallsysms_lookup_name() to access some kernel + * functions that otherwise wouldn't be accessible, KProbes are used on later + * kernels to get the address of kallsysms_lookup_name(). The function is + * afterwards used just as before. This is a very dirty hack though and the much + * better solution would be if all the functions that are currently resolved + * with kallsysms_lookup_name() would get an EXPORT_SYMBOL() annotation to + * make them directly accessible to kernel modules. + */ +static unsigned long kallsyms_lookup_name_wrapper(const char *name) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)) + static kallsyms_lookup_name_t func_ptr = NULL; + if (!func_ptr) + func_ptr = get_kallsyms_lookup_name_ptr(); + + return func_ptr(name); +#else + return kallsyms_lookup_name(name); +#endif +} + + static struct vm_struct *(*get_vm_area_ptr)(unsigned long, unsigned long) = NULL; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) static void (*zap_page_range_ptr)(struct vm_area_struct *, unsigned long, unsigned long) = NULL; @@ -31,7 +89,7 @@ static int (*security_binder_transfer_file_ptr)(struct task_struct *from, struct struct vm_struct *get_vm_area(unsigned long size, unsigned long flags) { if (!get_vm_area_ptr) - get_vm_area_ptr = kallsyms_lookup_name("get_vm_area"); + get_vm_area_ptr = kallsyms_lookup_name_wrapper("get_vm_area"); return get_vm_area_ptr(size, flags); } @@ -42,7 +100,7 @@ void zap_page_range(struct vm_area_struct *vma, unsigned long address, unsigned #endif { if (!zap_page_range_ptr) - zap_page_range_ptr = kallsyms_lookup_name("zap_page_range"); + zap_page_range_ptr = kallsyms_lookup_name_wrapper("zap_page_range"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) zap_page_range_ptr(vma, address, size); #else @@ -53,90 +111,90 @@ void zap_page_range(struct vm_area_struct *vma, unsigned long address, unsigned int map_kernel_range_noflush(unsigned long start, unsigned long size, pgprot_t prot, struct page **pages) { if (!map_kernel_range_noflush_ptr) - map_kernel_range_noflush_ptr = kallsyms_lookup_name("map_kernel_range_noflush"); + map_kernel_range_noflush_ptr = kallsyms_lookup_name_wrapper("map_kernel_range_noflush"); return map_kernel_range_noflush_ptr(start, size, prot, pages); } void unmap_kernel_range(unsigned long addr, unsigned long size) { if (!unmap_kernel_range_ptr) - unmap_kernel_range_ptr = kallsyms_lookup_name("unmap_kernel_range"); + unmap_kernel_range_ptr = kallsyms_lookup_name_wrapper("unmap_kernel_range"); unmap_kernel_range_ptr(addr, size); } struct files_struct *get_files_struct(struct task_struct *task) { if (!get_files_struct_ptr) - get_files_struct_ptr = kallsyms_lookup_name("get_files_struct"); + get_files_struct_ptr = kallsyms_lookup_name_wrapper("get_files_struct"); return get_files_struct_ptr(task); } void put_files_struct(struct files_struct *files) { if (!put_files_struct_ptr) - put_files_struct_ptr = kallsyms_lookup_name("put_files_struct"); + put_files_struct_ptr = kallsyms_lookup_name_wrapper("put_files_struct"); put_files_struct_ptr(files); } struct sighand_struct *__lock_task_sighand(struct task_struct *tsk, unsigned long *flags) { if (!__lock_task_sighand_ptr) - __lock_task_sighand_ptr = kallsyms_lookup_name("__lock_task_sighand"); + __lock_task_sighand_ptr = kallsyms_lookup_name_wrapper("__lock_task_sighand"); return __lock_task_sighand_ptr(tsk, flags); } int __alloc_fd(struct files_struct *files, unsigned start, unsigned end, unsigned flags) { if (!__alloc_fd_ptr) - __alloc_fd_ptr = kallsyms_lookup_name("__alloc_fd"); + __alloc_fd_ptr = kallsyms_lookup_name_wrapper("__alloc_fd"); return __alloc_fd_ptr(files, start, end, flags); } void __fd_install(struct files_struct *files, unsigned int fd, struct file *file) { if (!__fd_install_ptr) - __fd_install_ptr = kallsyms_lookup_name("__fd_install"); + __fd_install_ptr = kallsyms_lookup_name_wrapper("__fd_install"); __fd_install_ptr(files, fd, file); } int __close_fd(struct files_struct *files, unsigned int fd) { if (!__close_fd_ptr) - __close_fd_ptr = kallsyms_lookup_name("__close_fd_ptr"); + __close_fd_ptr = kallsyms_lookup_name_wrapper("__close_fd_ptr"); return __close_fd_ptr(files, fd); } int can_nice(const struct task_struct *p, const int nice) { if (!can_nice_ptr) - can_nice_ptr = kallsyms_lookup_name("can_nice"); + can_nice_ptr = kallsyms_lookup_name_wrapper("can_nice"); return can_nice_ptr(p, nice); } int security_binder_set_context_mgr(struct task_struct *mgr) { if (!security_binder_set_context_mgr_ptr) - security_binder_set_context_mgr_ptr = kallsyms_lookup_name("security_binder_set_context_mgr"); + security_binder_set_context_mgr_ptr = kallsyms_lookup_name_wrapper("security_binder_set_context_mgr"); return security_binder_set_context_mgr_ptr(mgr); } int security_binder_transaction(struct task_struct *from, struct task_struct *to) { if (!security_binder_transaction_ptr) - security_binder_transaction_ptr = kallsyms_lookup_name("security_binder_transaction"); + security_binder_transaction_ptr = kallsyms_lookup_name_wrapper("security_binder_transaction"); return security_binder_transaction_ptr(from, to); } int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) { if (!security_binder_transfer_binder_ptr) - security_binder_transfer_binder_ptr = kallsyms_lookup_name("security_binder_transfer_binder"); + security_binder_transfer_binder_ptr = kallsyms_lookup_name_wrapper("security_binder_transfer_binder"); return security_binder_transfer_binder_ptr(from, to); } int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) { if (!security_binder_transfer_file_ptr) - security_binder_transfer_file_ptr = kallsyms_lookup_name("security_binder_transfer_file"); + security_binder_transfer_file_ptr = kallsyms_lookup_name_wrapper("security_binder_transfer_file"); return security_binder_transfer_file_ptr(from, to, file); }