diff --git a/.gitignore b/.gitignore index 04cd038..12e0aa8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,61 +1,17 @@ -# Created by https://www.toptal.com/developers/gitignore/api/C -# Edit at https://www.toptal.com/developers/gitignore?templates=C - -### C ### -# Prerequisites -*.d - # Object files *.o *.ko -*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order +*.mod +*.mod.c +*.mod.o +.*.cmd Module.symvers -Mkfile.old +modules.order + +# Binaries +test/binder_test +test/ashmem_test -# End of https://www.toptal.com/developers/gitignore/api/C -.cache.mk -*.o.* -*.swp +# Logs +build.log diff --git a/README.md b/README.md index e59ec5d..8ccfa68 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ -English | [简体中文](./README_zh.md) # ReDroid Kernel Modules This repository contains the kernel modules necessary to run *ReDroid* instances. -Currently, **4.14+** kernel is supported. Many Linux distributions already meet -this requirement (Ubuntu 16.04+, AmazonLinux 2, Alibaba Cloud Linux 2 etc.). If you are using -customized kernel, make sure the corresponding kernel headers are present in your system. +Currently, kernels **4.14** through **6.8+** are supported. Many Linux distributions meet +this requirement (Ubuntu 16.04 - 24.04, AmazonLinux 2, Alibaba Cloud Linux 2, etc.). -**for kernel >= 5.7, please install either by building customized kernel or `modprobe` (Ubuntu etc.)** +**Note for newer kernels (>= 5.7):** +While some custom kernels may have `ashmem` and `binder` built-in, standard distribution kernels (like Ubuntu's generic kernel) often do not. In these cases, you should use this repository to compile and install the modules. ## Build & Deploy - [Manual](#manual) @@ -22,6 +21,12 @@ sudo apt-get install -y git kmod make gcc linux-headers-`uname -r` sudo make # build kernel modules sudo make install # build and install *unsigned* kernel modules +# Ubuntu 24.04+ (Kernel 6.8+) +sudo apt-get install -y git kmod make gcc linux-headers-`uname -r` +make # build kernel modules +# Load modules using the helper script +sudo ./load_modules.sh + # Ubuntu 20.04+ (kernel 5.0+) sudo modprobe ashmem_linux sudo modprobe binder_linux devices=binder,hwbinder,vndbinder @@ -61,6 +66,23 @@ grep ashmem /proc/misc # output should like: 56 ashmem ``` *Please refer the related docs if you want to sign these kernel modules.* +## Testing +You can verify the modules using the provided tests in `test/` directory. + +### Binder +```bash +cd test +gcc -I../binder test.c -o binder_test +sudo ./binder_test /dev/binderfs/binder-control my-test-binder +``` + +### Ashmem +```bash +cd test +gcc ashmem_test.c -o ashmem_test +sudo ./ashmem_test +``` + ## DKMS DKMS can help to automatically build and deploy kernel modules if kernel upgraded. You need to have `dkms` and linux-headers on your system. You can install them by diff --git a/ashmem/ashmem.c b/ashmem/ashmem.c index 5605ccf..21e6c04 100644 --- a/ashmem/ashmem.c +++ b/ashmem/ashmem.c @@ -402,7 +402,7 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) ret = -EPERM; goto out; } - vma->vm_flags &= ~calc_vm_may_flags(~asma->prot_mask); + vm_flags_clear(vma, calc_vm_may_flags(~asma->prot_mask)); if (!asma->file) { char *name = ASHMEM_NAME_DEF; @@ -528,6 +528,10 @@ ashmem_shrink_count(struct shrinker *shrink, struct shrink_control *sc) return lru_count; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) + +static struct shrinker *ashmem_shrinker; +#else static struct shrinker ashmem_shrinker = { .count_objects = ashmem_shrink_count, .scan_objects = ashmem_shrink_scan, @@ -537,6 +541,7 @@ static struct shrinker ashmem_shrinker = { */ .seeks = DEFAULT_SEEKS * 4, }; +#endif static int set_prot_mask(struct ashmem_area *asma, unsigned long prot) { @@ -857,8 +862,13 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) .gfp_mask = GFP_KERNEL, .nr_to_scan = LONG_MAX, }; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) + ret = ashmem_shrink_count(ashmem_shrinker, &sc); + ashmem_shrink_scan(ashmem_shrinker, &sc); +#else ret = ashmem_shrink_count(&ashmem_shrinker, &sc); ashmem_shrink_scan(&ashmem_shrinker, &sc); +#endif } break; } @@ -947,11 +957,25 @@ static int __init ashmem_init(void) goto out_free2; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) + ashmem_shrinker = shrinker_alloc(0, "android-ashmem"); + if (!ashmem_shrinker) { + pr_err("failed to allocate shrinker\n"); + goto out_demisc; + } + + ashmem_shrinker->count_objects = ashmem_shrink_count; + ashmem_shrinker->scan_objects = ashmem_shrink_scan; + ashmem_shrinker->seeks = DEFAULT_SEEKS * 4; + + shrinker_register(ashmem_shrinker); +#else ret = register_shrinker(&ashmem_shrinker); if (ret) { pr_err("failed to register shrinker!\n"); goto out_demisc; } +#endif pr_info("initialized\n"); @@ -970,12 +994,15 @@ static int __init ashmem_init(void) // HACKED static void __exit ashmem_exit(void) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) + shrinker_free(ashmem_shrinker); +#else unregister_shrinker(&ashmem_shrinker); - +#endif misc_deregister(&ashmem_misc); - kmem_cache_destroy(ashmem_range_cachep); kmem_cache_destroy(ashmem_area_cachep); + pr_info("unloaded\n"); } module_init(ashmem_init); diff --git a/ashmem/deps.c b/ashmem/deps.c index ef1fbe7..aac7d67 100644 --- a/ashmem/deps.c +++ b/ashmem/deps.c @@ -1,12 +1,64 @@ #include #include +#include +#include + +/* + * 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. + */ +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; +} + +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 +} + typedef int (*shmem_zero_setup_ptr_t)(struct vm_area_struct *); static shmem_zero_setup_ptr_t shmem_zero_setup_ptr = NULL; int shmem_zero_setup(struct vm_area_struct *vma) { if (!shmem_zero_setup_ptr) - shmem_zero_setup_ptr = (shmem_zero_setup_ptr_t) kallsyms_lookup_name("shmem_zero_setup"); + shmem_zero_setup_ptr = (shmem_zero_setup_ptr_t) kallsyms_lookup_name_wrapper("shmem_zero_setup"); return shmem_zero_setup_ptr(vma); } diff --git a/binder/binder.c b/binder/binder.c index 94c57ad..00952e8 100644 --- a/binder/binder.c +++ b/binder/binder.c @@ -76,6 +76,8 @@ #include "binder_internal.h" #include "binder_trace.h" +extern struct file *file_close_fd(unsigned int fd); + static HLIST_HEAD(binder_deferred_list); static DEFINE_MUTEX(binder_deferred_lock); @@ -470,6 +472,9 @@ struct binder_proc { struct list_head waiting_threads; int pid; struct task_struct *tsk; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 2) + const struct cred *cred; +#endif struct hlist_node deferred_work_node; int deferred_work; bool is_dead; @@ -2232,9 +2237,17 @@ static void binder_deferred_fd_close(int fd) if (!twcb) return; init_task_work(&twcb->twork, binder_do_fd_close); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + twcb->file = file_close_fd(fd); +#else __close_fd_get_file(fd, &twcb->file); +#endif if (twcb->file) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + task_work_add(current, &twcb->twork, TWA_RESUME); +#else task_work_add(current, &twcb->twork, true); +#endif else kfree(twcb); } @@ -2435,7 +2448,11 @@ static int binder_translate_binder(struct flat_binder_object *fp, ret = -EINVAL; goto done; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 2) + if (security_binder_transfer_binder(proc->cred, target_proc->cred)) { +#else if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { +#endif ret = -EPERM; goto done; } @@ -2481,7 +2498,11 @@ static int binder_translate_handle(struct flat_binder_object *fp, proc->pid, thread->pid, fp->handle); return -EINVAL; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 2) + if (security_binder_transfer_binder(proc->cred, target_proc->cred)) { +#else if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { +#endif ret = -EPERM; goto done; } @@ -2569,7 +2590,11 @@ static int binder_translate_fd(u32 fd, binder_size_t fd_offset, ret = -EBADF; goto err_fget; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 2) + ret = security_binder_transfer_file(proc->cred, target_proc->cred, file); +#else ret = security_binder_transfer_file(proc->tsk, target_proc->tsk, file); +#endif if (ret < 0) { ret = -EPERM; goto err_security; @@ -2842,7 +2867,11 @@ static void binder_transaction(struct binder_proc *proc, struct binder_context *context = proc->context; int t_debug_id = atomic_inc_return(&binder_last_id); char *secctx = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + struct lsmcontext lsmctx; +#else u32 secctx_sz = 0; +#endif e = binder_transaction_log_add(&binder_transaction_log); e->debug_id = t_debug_id; @@ -2968,8 +2997,13 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_invalid_target_handle; } - if (security_binder_transaction(proc->tsk, - target_proc->tsk) < 0) { + #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 2) + if (security_binder_transaction(proc->cred, + target_proc->cred) < 0) { +#else + if (security_binder_transaction(proc->tsk, + target_proc->tsk) < 0) { +#endif return_error = BR_FAILED_REPLY; return_error_param = -EPERM; return_error_line = __LINE__; @@ -3095,15 +3129,28 @@ static void binder_transaction(struct binder_proc *proc, u32 secid; size_t added_size; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + secid = 0; +#else security_task_getsecid(proc->tsk, &secid); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + ret = security_secid_to_secctx(secid, &lsmctx); + secctx = lsmctx.context; +#else ret = security_secid_to_secctx(secid, &secctx, &secctx_sz); +#endif if (ret) { return_error = BR_FAILED_REPLY; return_error_param = ret; return_error_line = __LINE__; goto err_get_secctx_failed; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + added_size = ALIGN(lsmctx.len, sizeof(u64)); +#else added_size = ALIGN(secctx_sz, sizeof(u64)); +#endif extra_buffers_size += added_size; if (extra_buffers_size < added_size) { /* integer overflow of extra_buffers_size */ @@ -3135,17 +3182,30 @@ static void binder_transaction(struct binder_proc *proc, size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) + ALIGN(tr->offsets_size, sizeof(void *)) + ALIGN(extra_buffers_size, sizeof(void *)) - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + ALIGN(lsmctx.len, sizeof(u64)); +#else ALIGN(secctx_sz, sizeof(u64)); +#endif t->security_ctx = (uintptr_t)t->buffer->user_data + buf_offset; err = binder_alloc_copy_to_buffer(&target_proc->alloc, t->buffer, buf_offset, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + secctx, lsmctx.len); +#else secctx, secctx_sz); +#endif if (err) { t->security_ctx = 0; WARN_ON(1); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + lsmctx.context = secctx; + security_release_secctx(&lsmctx); +#else security_release_secctx(secctx, secctx_sz); +#endif secctx = NULL; } t->buffer->debug_id = t->debug_id; @@ -3202,7 +3262,11 @@ static void binder_transaction(struct binder_proc *proc, off_end_offset = off_start_offset + tr->offsets_size; sg_buf_offset = ALIGN(off_end_offset, sizeof(void *)); sg_buf_end_offset = sg_buf_offset + extra_buffers_size - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + ALIGN(lsmctx.len, sizeof(u64)); +#else ALIGN(secctx_sz, sizeof(u64)); +#endif off_min = 0; for (buffer_offset = off_start_offset; buffer_offset < off_end_offset; buffer_offset += sizeof(binder_size_t)) { @@ -3478,8 +3542,14 @@ static void binder_transaction(struct binder_proc *proc, binder_alloc_free_buf(&target_proc->alloc, t->buffer); err_binder_alloc_buf_failed: err_bad_extra_size: - if (secctx) + if (secctx) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + lsmctx.context = secctx; + security_release_secctx(&lsmctx); +#else security_release_secctx(secctx, secctx_sz); +#endif + } err_get_secctx_failed: kfree(tcomplete); binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); @@ -4073,7 +4143,9 @@ static int binder_wait_for_work(struct binder_thread *thread, struct binder_proc *proc = thread->proc; int ret = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) freezer_do_not_count(); +#endif binder_inner_proc_lock(proc); for (;;) { prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE); @@ -4093,7 +4165,9 @@ static int binder_wait_for_work(struct binder_thread *thread, } finish_wait(&thread->wait, &wait); binder_inner_proc_unlock(proc); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) freezer_count(); +#endif return ret; } @@ -4696,6 +4770,9 @@ static void binder_free_proc(struct binder_proc *proc) } binder_alloc_deferred_release(&proc->alloc); put_task_struct(proc->tsk); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 2) + put_cred(proc->cred); +#endif binder_stats_deleted(BINDER_STAT_PROC); kfree(proc); } @@ -4906,7 +4983,11 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp, ret = -EBUSY; goto out; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 2) + ret = security_binder_set_context_mgr(proc->cred); +#else ret = security_binder_set_context_mgr(proc->tsk); +#endif if (ret < 0) goto out; if (uid_valid(context->binder_context_mgr_uid)) { @@ -5188,8 +5269,13 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) failure_string = "bad vm_flags"; goto err_bad_arg; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) + vm_flags_set(vma, VM_DONTCOPY | VM_MIXEDMAP); + vm_flags_clear(vma, VM_MAYWRITE); +#else vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP; vma->vm_flags &= ~VM_MAYWRITE; +#endif vma->vm_ops = &binder_vm_ops; vma->vm_private_data = proc; @@ -5223,6 +5309,9 @@ static int binder_open(struct inode *nodp, struct file *filp) spin_lock_init(&proc->outer_lock); get_task_struct(current->group_leader); proc->tsk = current->group_leader; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 2) + proc->cred = get_cred(filp->f_cred); +#endif INIT_LIST_HEAD(&proc->todo); proc->default_priority = task_nice(current); /* binderfs stashes devices in i_private */ diff --git a/binder/binder_alloc.c b/binder/binder_alloc.c index 4fddb6a..9bc48c2 100644 --- a/binder/binder_alloc.c +++ b/binder/binder_alloc.c @@ -213,7 +213,11 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, mm = alloc->vma_vm_mm; if (mm) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + mmap_read_lock(mm); +#else down_read(&mm->mmap_sem); +#endif vma = alloc->vma; } @@ -235,7 +239,11 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, if (page->page_ptr) { trace_binder_alloc_lru_start(alloc, index); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + on_lru = list_lru_del(&binder_alloc_lru, &page->lru, page_to_nid(page->page_ptr), NULL); +#else on_lru = list_lru_del(&binder_alloc_lru, &page->lru); +#endif WARN_ON(!on_lru); trace_binder_alloc_lru_end(alloc, index); @@ -272,7 +280,11 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, /* vm_insert_page does not seem to increment the refcount */ } if (mm) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + mmap_read_unlock(mm); +#else up_read(&mm->mmap_sem); +#endif mmput(mm); } return 0; @@ -287,7 +299,11 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, trace_binder_free_lru_start(alloc, index); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + ret = list_lru_add(&binder_alloc_lru, &page->lru, page_to_nid(page->page_ptr), NULL); +#else ret = list_lru_add(&binder_alloc_lru, &page->lru); +#endif WARN_ON(!ret); trace_binder_free_lru_end(alloc, index); @@ -305,7 +321,11 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, } err_no_vma: if (mm) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + mmap_read_unlock(mm); +#else up_read(&mm->mmap_sem); +#endif mmput(mm); } return vma ? -ENOMEM : -ESRCH; @@ -779,8 +799,15 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) if (!alloc->pages[i].page_ptr) continue; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + on_lru = list_lru_del(&binder_alloc_lru, + &alloc->pages[i].lru, + page_to_nid(alloc->pages[i].page_ptr), + NULL); +#else on_lru = list_lru_del(&binder_alloc_lru, &alloc->pages[i].lru); +#endif page_addr = alloc->buffer + i * PAGE_SIZE; binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, "%s: %d: page %d at %pK %s\n", @@ -934,7 +961,11 @@ enum lru_status binder_alloc_free_page(struct list_head *item, mm = alloc->vma_vm_mm; if (!mmget_not_zero(mm)) goto err_mmget; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + if (!mmap_read_trylock(mm)) +#else if (!down_read_trylock(&mm->mmap_sem)) +#endif goto err_down_read_mmap_sem_failed; vma = binder_alloc_get_vma(alloc); @@ -944,11 +975,19 @@ enum lru_status binder_alloc_free_page(struct list_head *item, if (vma) { trace_binder_unmap_user_start(alloc, index); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) + zap_page_range_single(vma, page_addr, PAGE_SIZE, NULL); +#else zap_page_range(vma, page_addr, PAGE_SIZE); +#endif trace_binder_unmap_user_end(alloc, index); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + mmap_read_unlock(mm); +#else up_read(&mm->mmap_sem); +#endif mmput_async(mm); trace_binder_unmap_kernel_start(alloc, index); @@ -988,11 +1027,15 @@ binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) return ret; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) +static struct shrinker *binder_shrinker; +#else static struct shrinker binder_shrinker = { .count_objects = binder_shrink_count, .scan_objects = binder_shrink_scan, .seeks = DEFAULT_SEEKS, }; +#endif /** * binder_alloc_init() - called by binder_open() for per-proc initialization @@ -1013,9 +1056,21 @@ int binder_alloc_shrinker_init(void) int ret = list_lru_init(&binder_alloc_lru); if (ret == 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + binder_shrinker = shrinker_alloc(0, "android-binder"); + if (!binder_shrinker) { + list_lru_destroy(&binder_alloc_lru); + return -ENOMEM; + } + binder_shrinker->count_objects = binder_shrink_count; + binder_shrinker->scan_objects = binder_shrink_scan; + binder_shrinker->seeks = DEFAULT_SEEKS; + shrinker_register(binder_shrinker); +#else ret = register_shrinker(&binder_shrinker); if (ret) list_lru_destroy(&binder_alloc_lru); +#endif } return ret; } @@ -1023,7 +1078,11 @@ int binder_alloc_shrinker_init(void) // HACKED void binder_alloc_shrinker_exit(void) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + shrinker_free(binder_shrinker); +#else unregister_shrinker(&binder_shrinker); +#endif list_lru_destroy(&binder_alloc_lru); } diff --git a/binder/binderfs.c b/binder/binderfs.c index a72d869..7a40c41 100644 --- a/binder/binderfs.c +++ b/binder/binderfs.c @@ -141,7 +141,11 @@ static int binderfs_binder_device_create(struct inode *ref_inode, goto err; inode->i_ino = minor + INODE_OFFSET; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) + simple_inode_init_ts(inode); +#else inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); +#endif init_special_inode(inode, S_IFCHR | 0600, MKDEV(MAJOR(binderfs_dev), minor)); inode->i_fop = &binder_fops; @@ -364,6 +368,18 @@ static inline bool is_binderfs_control_device(const struct dentry *dentry) return info->control_dentry == dentry; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) +static int binderfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + if (is_binderfs_control_device(old_dentry) || + is_binderfs_control_device(new_dentry)) + return -EPERM; + + return simple_rename(idmap, old_dir, old_dentry, new_dir, new_dentry, flags); +} +#else static int binderfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) @@ -374,6 +390,7 @@ static int binderfs_rename(struct inode *old_dir, struct dentry *old_dentry, return simple_rename(old_dir, old_dentry, new_dir, new_dentry, flags); } +#endif static int binderfs_unlink(struct inode *dir, struct dentry *dentry) { @@ -442,7 +459,11 @@ static int binderfs_binder_ctl_create(struct super_block *sb) } inode->i_ino = SECOND_INODE; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) + simple_inode_init_ts(inode); +#else inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); +#endif init_special_inode(inode, S_IFCHR | 0600, MKDEV(MAJOR(binderfs_dev), minor)); inode->i_fop = &binder_ctl_fops; @@ -484,7 +505,11 @@ static struct inode *binderfs_make_inode(struct super_block *sb, int mode) if (ret) { ret->i_ino = iunique(sb, BINDERFS_MAX_MINOR + INODE_OFFSET); ret->i_mode = mode; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) + simple_inode_init_ts(ret); +#else ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret); +#endif } return ret; } @@ -708,7 +733,11 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent) inode->i_ino = FIRST_INODE; inode->i_fop = &simple_dir_operations; inode->i_mode = S_IFDIR | 0755; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) + simple_inode_init_ts(inode); +#else inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); +#endif inode->i_op = &binderfs_dir_inode_operations; set_nlink(inode, 2); diff --git a/binder/deps.c b/binder/deps.c index c946e7a..1cc7d4c 100644 --- a/binder/deps.c +++ b/binder/deps.c @@ -9,13 +9,64 @@ #include #include #include +#include +#include + +/* + * 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. + */ +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; +} + +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 +} typedef void (*zap_page_range_ptr_t)(struct vm_area_struct *, unsigned long, unsigned long); static zap_page_range_ptr_t zap_page_range_ptr = NULL; void zap_page_range(struct vm_area_struct *vma, unsigned long address, unsigned long size) { if (!zap_page_range_ptr) - zap_page_range_ptr = (zap_page_range_ptr_t) kallsyms_lookup_name("zap_page_range"); + zap_page_range_ptr = (zap_page_range_ptr_t) kallsyms_lookup_name_wrapper("zap_page_range"); zap_page_range_ptr(vma, address, size); } @@ -24,43 +75,67 @@ static can_nice_ptr_t can_nice_ptr = NULL; int can_nice(const struct task_struct *p, const int nice) { if (!can_nice_ptr) - can_nice_ptr = (can_nice_ptr_t) kallsyms_lookup_name("can_nice"); + can_nice_ptr = (can_nice_ptr_t) kallsyms_lookup_name_wrapper("can_nice"); return can_nice_ptr(p, nice); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,2)) +typedef int (*security_binder_set_context_mgr_ptr_t)(const struct cred *mgr); +static security_binder_set_context_mgr_ptr_t security_binder_set_context_mgr_ptr = NULL; +int security_binder_set_context_mgr(const struct cred *mgr) +#else typedef int (*security_binder_set_context_mgr_ptr_t)(struct task_struct *mgr); -security_binder_set_context_mgr_ptr_t security_binder_set_context_mgr_ptr = NULL; +static security_binder_set_context_mgr_ptr_t security_binder_set_context_mgr_ptr = NULL; int security_binder_set_context_mgr(struct task_struct *mgr) +#endif { if (!security_binder_set_context_mgr_ptr) - security_binder_set_context_mgr_ptr = (security_binder_set_context_mgr_ptr_t) kallsyms_lookup_name("security_binder_set_context_mgr"); + security_binder_set_context_mgr_ptr = (security_binder_set_context_mgr_ptr_t) kallsyms_lookup_name_wrapper("security_binder_set_context_mgr"); return security_binder_set_context_mgr_ptr(mgr); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,2)) +typedef int (*security_binder_transaction_ptr_t)(const struct cred *from, const struct cred *to); +static security_binder_transaction_ptr_t security_binder_transaction_ptr = NULL; +int security_binder_transaction(const struct cred *from, const struct cred *to) +#else typedef int (*security_binder_transaction_ptr_t)(struct task_struct *from, struct task_struct *to); static security_binder_transaction_ptr_t security_binder_transaction_ptr = NULL; int security_binder_transaction(struct task_struct *from, struct task_struct *to) +#endif { if (!security_binder_transaction_ptr) - security_binder_transaction_ptr = (security_binder_transaction_ptr_t) kallsyms_lookup_name("security_binder_transaction"); + security_binder_transaction_ptr = (security_binder_transaction_ptr_t) kallsyms_lookup_name_wrapper("security_binder_transaction"); return security_binder_transaction_ptr(from, to); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,2)) +typedef int (*security_binder_transfer_binder_ptr_t)(const struct cred *from, const struct cred *to); +static security_binder_transfer_binder_ptr_t security_binder_transfer_binder_ptr = NULL; +int security_binder_transfer_binder(const struct cred *from, const struct cred *to) +#else typedef int (*security_binder_transfer_binder_ptr_t)(struct task_struct *from, struct task_struct *to); static security_binder_transfer_binder_ptr_t security_binder_transfer_binder_ptr = NULL; int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) +#endif { if (!security_binder_transfer_binder_ptr) - security_binder_transfer_binder_ptr = (security_binder_transfer_binder_ptr_t) kallsyms_lookup_name("security_binder_transfer_binder"); + security_binder_transfer_binder_ptr = (security_binder_transfer_binder_ptr_t) kallsyms_lookup_name_wrapper("security_binder_transfer_binder"); return security_binder_transfer_binder_ptr(from, to); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,2)) +typedef int (*security_binder_transfer_file_ptr_t)(const struct cred *from, const struct cred *to, const struct file *file); +static security_binder_transfer_file_ptr_t security_binder_transfer_file_ptr = NULL; +int security_binder_transfer_file(const struct cred *from, const struct cred *to, const struct file *file) +#else typedef int (*security_binder_transfer_file_ptr_t)(struct task_struct *from, struct task_struct *to, struct file *file); static security_binder_transfer_file_ptr_t security_binder_transfer_file_ptr = NULL; int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) +#endif { if (!security_binder_transfer_file_ptr) - security_binder_transfer_file_ptr = (security_binder_transfer_file_ptr_t) kallsyms_lookup_name("security_binder_transfer_file"); + security_binder_transfer_file_ptr = (security_binder_transfer_file_ptr_t) kallsyms_lookup_name_wrapper("security_binder_transfer_file"); return security_binder_transfer_file_ptr(from, to, file); } @@ -69,7 +144,7 @@ static put_ipc_ns_ptr_t put_ipc_ns_ptr = NULL; void put_ipc_ns(struct ipc_namespace *ns) { if (!put_ipc_ns_ptr) - put_ipc_ns_ptr = (put_ipc_ns_ptr_t) kallsyms_lookup_name("put_ipc_ns"); + put_ipc_ns_ptr = (put_ipc_ns_ptr_t) kallsyms_lookup_name_wrapper("put_ipc_ns"); put_ipc_ns_ptr(ns); } @@ -78,16 +153,22 @@ typedef struct ipc_namespace *init_ipc_ns_ptr_t; static init_ipc_ns_ptr_t init_ipc_ns_ptr = NULL; init_ipc_ns_ptr_t get_init_ipc_ns_ptr(void) { - if (!init_ipc_ns_ptr) init_ipc_ns_ptr = (init_ipc_ns_ptr_t) kallsyms_lookup_name("init_ipc_ns"); + if (!init_ipc_ns_ptr) init_ipc_ns_ptr = (init_ipc_ns_ptr_t) kallsyms_lookup_name_wrapper("init_ipc_ns"); return init_ipc_ns_ptr; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) +typedef int (*task_work_add_ptr_t)(struct task_struct *task, struct callback_head *twork, enum task_work_notify_mode notify); +static task_work_add_ptr_t task_work_add_ptr = NULL; +int task_work_add(struct task_struct *task, struct callback_head *twork, enum task_work_notify_mode notify) +#else typedef int (*task_work_add_ptr_t)(struct task_struct *task, struct callback_head *twork, bool notify); static task_work_add_ptr_t task_work_add_ptr = NULL; int task_work_add(struct task_struct *task, struct callback_head *twork, bool notify) +#endif { if (!task_work_add_ptr) - task_work_add_ptr = (task_work_add_ptr_t) kallsyms_lookup_name("task_work_add"); + task_work_add_ptr = (task_work_add_ptr_t) kallsyms_lookup_name_wrapper("task_work_add"); return task_work_add_ptr(task, twork, notify); } @@ -96,7 +177,7 @@ static mmput_async_ptr_t mmput_async_ptr = NULL; void mmput_async(struct mm_struct *mm) { if (!mmput_async_ptr) - mmput_async_ptr = (mmput_async_ptr_t) kallsyms_lookup_name("mmput_async"); + mmput_async_ptr = (mmput_async_ptr_t) kallsyms_lookup_name_wrapper("mmput_async"); mmput_async_ptr(mm); } @@ -146,10 +227,25 @@ static __close_fd_get_file_ptr_t __close_fd_get_file_ptr = NULL; int __close_fd_get_file(unsigned int fd, struct file **res) { if (!__close_fd_get_file_ptr) - __close_fd_get_file_ptr = (__close_fd_get_file_ptr_t) kallsyms_lookup_name("__close_fd_get_file"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + __close_fd_get_file_ptr = (__close_fd_get_file_ptr_t) kallsyms_lookup_name_wrapper("file_close_fd"); +#else + __close_fd_get_file_ptr = (__close_fd_get_file_ptr_t) kallsyms_lookup_name_wrapper("__close_fd_get_file"); +#endif return __close_fd_get_file_ptr(fd, res); } #endif // LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 1) + +typedef struct file *(*close_fd_get_file_t)(unsigned int fd); +static close_fd_get_file_t close_fd_get_file_ptr = NULL; +struct file *file_close_fd(unsigned int fd) +{ +if (!close_fd_get_file_ptr) +close_fd_get_file_ptr = (close_fd_get_file_t) kallsyms_lookup_name_wrapper("close_fd_get_file"); +if (close_fd_get_file_ptr) +return close_fd_get_file_ptr(fd); +return NULL; +} diff --git a/load_modules.sh b/load_modules.sh new file mode 100755 index 0000000..2a32b0f --- /dev/null +++ b/load_modules.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +echo "Loading ashmem_linux..." +sudo insmod ashmem/ashmem_linux.ko + +echo "Loading binder_linux..." +sudo insmod binder/binder_linux.ko + +echo "Modules loaded successfully!" +lsmod | grep -E "binder|ashmem" diff --git a/test/ashmem_test.c b/test/ashmem_test.c new file mode 100644 index 0000000..1581d62 --- /dev/null +++ b/test/ashmem_test.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASHMEM_NAME_LEN 256 +#define ASHMEM_NAME_DEF "dev/ashmem" +#define ASHMEM_NOT_PURGED 0 +#define ASHMEM_WAS_PURGED 1 +#define ASHMEM_IS_UNPINNED 0 +#define ASHMEM_IS_PINNED 1 + +#define __ASHMEMIOC 0x77 +#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN]) +#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN]) +#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t) +#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4) +#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long) +#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6) +#define ASHMEM_PIN _IOW(__ASHMEMIOC, 7, struct ashmem_pin) +#define ASHMEM_UNPIN _IOW(__ASHMEMIOC, 8, struct ashmem_pin) +#define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9) +#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10) + +struct ashmem_pin { + unsigned int offset; + unsigned int len; +}; + +int main() { + int fd; + const char *name = "my_ashmem_test"; + const size_t size = 4096; + void *ptr; + + printf("Opening /dev/ashmem...\n"); + fd = open("/dev/ashmem", O_RDWR); + if (fd < 0) { + perror("Failed to open /dev/ashmem"); + return 1; + } + printf("Successfully opened /dev/ashmem (fd=%d)\n", fd); + + if (ioctl(fd, ASHMEM_SET_NAME, name) < 0) { + perror("ASHMEM_SET_NAME failed"); + close(fd); + return 1; + } + printf("Set ashmem name to '%s'\n", name); + + if (ioctl(fd, ASHMEM_SET_SIZE, size) < 0) { + perror("ASHMEM_SET_SIZE failed"); + close(fd); + return 1; + } + printf("Set ashmem size to %zu bytes\n", size); + + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + perror("mmap failed"); + close(fd); + return 1; + } + printf("Successfully mmapped ashmem region at %p\n", ptr); + + sprintf((char*)ptr, "Hello Ashmem!"); + printf("Wrote to ashmem: '%s'\n", (char*)ptr); + + munmap(ptr, size); + close(fd); + printf("Test passed!\n"); + + return 0; +}