Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 11 additions & 55 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
33 changes: 30 additions & 3 deletions ashmem/ashmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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");

Expand All @@ -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);
Expand Down
54 changes: 53 additions & 1 deletion ashmem/deps.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,64 @@
#include <linux/mm.h>
#include <linux/kallsyms.h>

#include <linux/kprobes.h>
#include <linux/version.h>

/*
* 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);
}

Loading