Android ashmem hacking

时间:2023-03-08 22:44:57
Android ashmem hacking
/**********************************************************************
* Android ashmem hacking
* 声明:
* 最近有些东西涉及到binder,而binder又涉及到ashmem,于是先跟一下这
* 部分的内容。
*
* 2016-1-12 深圳 南山平山村 曾剑锋
*********************************************************************/ /**
* 参考文章:
* Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析
* http://blog.csdn.net/luoshengyang/article/details/6664554
*/ /*
* ashmem_area - anonymous shared memory area
* Lifecycle: From our parent file's open() until its release()
* Locking: Protected by `ashmem_mutex'
* Big Note: Mappings do NOT pin this structure; it dies on close()
*/
struct ashmem_area {
char name[ASHMEM_FULL_NAME_LEN];/* optional name for /proc/pid/maps */
struct list_head unpinned_list; /* list of all ashmem areas */
struct file *file; /* the shmem-based backing file */
size_t size; /* size of the mapping, in bytes */
unsigned long prot_mask; /* allowed prot bits, as vm_flags */
}; /*
* ashmem_range - represents an interval of unpinned (evictable) pages
* Lifecycle: From unpin to pin
* Locking: Protected by `ashmem_mutex'
*/
struct ashmem_range {
struct list_head lru; /* entry in LRU list */
struct list_head unpinned; /* entry in its area's unpinned list */
struct ashmem_area *asma; /* associated area */
size_t pgstart; /* starting page, inclusive */
size_t pgend; /* ending page, inclusive */
unsigned int purged; /* ASHMEM_NOT or ASHMEM_WAS_PURGED */
}; module_init(ashmem_init); ----------+
module_exit(ashmem_exit); |
|
static int __init ashmem_init(void) <---------+
{
int ret; // static struct kmem_cache *ashmem_area_cachep __read_mostly;
ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",
sizeof(struct ashmem_area),
, , NULL);
if (unlikely(!ashmem_area_cachep)) {
printk(KERN_ERR "ashmem: failed to create slab cache\n");
return -ENOMEM;
} ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",
sizeof(struct ashmem_range),
, , NULL);
if (unlikely(!ashmem_range_cachep)) {
printk(KERN_ERR "ashmem: failed to create slab cache\n");
return -ENOMEM;
} ret = misc_register(&ashmem_misc); -------------------+
if (unlikely(ret)) { |
printk(KERN_ERR "ashmem: failed to register misc device!\n"); |
return ret; |
} |
|
register_shrinker(&ashmem_shrinker); |
|
printk(KERN_INFO "ashmem: initialized\n"); |
|
return ; |
} |
|
static struct file_operations ashmem_fops = { <-------+ |
.owner = THIS_MODULE, | |
.open = ashmem_open, -------*-+ |
.release = ashmem_release, | | |
.read = ashmem_read, | | |
.llseek = ashmem_llseek, | | |
.mmap = ashmem_mmap, ---------------*-*--------*---------+
.unlocked_ioctl = ashmem_ioctl, ---------------*-*--------*-------+ |
.compat_ioctl = ashmem_ioctl, | | | | |
}; | | | | |
| | | | |
static struct miscdevice ashmem_misc = { <-------*-*--------+ | |
.minor = MISC_DYNAMIC_MINOR, | | | |
.name = "ashmem", | | | |
.fops = &ashmem_fops, --------+ | | |
}; +-------------------------------------------+ | |
V | |
static int ashmem_open(struct inode *inode, struct file *file) | |
{ | |
struct ashmem_area *asma; | |
int ret; | |
| |
ret = generic_file_open(inode, file); | |
if (unlikely(ret)) | |
return ret; | |
| |
// static struct kmem_cache *ashmem_area_cachep __read_mostly; | |
// ashmem_area_cachep = kmem_cache_create("ashmem_area_cache", | |
// sizeof(struct ashmem_area), | |
// 0, 0, NULL); | |
asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL); | |
if (unlikely(!asma)) | |
return -ENOMEM; | |
| |
INIT_LIST_HEAD(&asma->unpinned_list); | |
// #define ASHMEM_NAME_PREFIX "dev/ashmem/" | |
// #define ASHMEM_NAME_PREFIX_LEN (sizeof(ASHMEM_NAME_PREFIX) - 1) | |
// #define ASHMEM_FULL_NAME_LEN (ASHMEM_NAME_LEN + ASHMEM_NAME_PREFIX_LEN)| |
memcpy(asma->name, ASHMEM_NAME_PREFIX, ASHMEM_NAME_PREFIX_LEN); | |
// #define PROT_MASK (PROT_EXEC | PROT_READ | PROT_WRITE) | |
asma->prot_mask = PROT_MASK; | |
// can get this asma struct in other function | |
file->private_data = asma; | |
| |
return ; +-----------------------------------------------------------+ |
} | |
V |
static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)|
{ |
struct ashmem_area *asma = file->private_data; |
long ret = -ENOTTY; |
|
switch (cmd) { |
case ASHMEM_SET_NAME: |
ret = set_name(asma, (void __user *) arg); -------+ |
break; | |
case ASHMEM_GET_NAME: | |
ret = get_name(asma, (void __user *) arg); -------*-----+ |
break; | | |
case ASHMEM_SET_SIZE: | | |
ret = -EINVAL; | | |
if (!asma->file) { | | |
ret = ; | | |
asma->size = (size_t) arg; | | |
} | | |
break; | | |
case ASHMEM_GET_SIZE: | | |
ret = asma->size; | | |
break; | | |
case ASHMEM_SET_PROT_MASK: | | |
ret = set_prot_mask(asma, arg); | | |
break; | | |
case ASHMEM_GET_PROT_MASK: | | |
ret = asma->prot_mask; | | |
break; | | |
case ASHMEM_PIN: | | |
case ASHMEM_UNPIN: | | |
case ASHMEM_GET_PIN_STATUS: | | |
ret = ashmem_pin_unpin(asma, cmd, (void __user *) arg);| | |
break; | | |
case ASHMEM_PURGE_ALL_CACHES: | | |
ret = -EPERM; | | |
if (capable(CAP_SYS_ADMIN)) { | | |
struct shrink_control sc = { | | |
.gfp_mask = GFP_KERNEL, | | |
.nr_to_scan = , | | |
}; | | |
ret = ashmem_shrink(&ashmem_shrinker, &sc); | | |
sc.nr_to_scan = ret; | | |
ashmem_shrink(&ashmem_shrinker, &sc); | | |
} | | |
break; | | |
} | | |
| | |
return ret; | | |
} +----------------------------------------------+ | |
V | |
static int set_name(struct ashmem_area *asma, void __user *name) | |
{ | |
int ret = ; | |
| |
mutex_lock(&ashmem_mutex); | |
| |
/* cannot change an existing mapping's name */ | |
if (unlikely(asma->file)) { | |
ret = -EINVAL; | |
goto out; | |
} | |
| |
if (unlikely(copy_from_user(asma->name + ASHMEM_NAME_PREFIX_LEN, | |
name, ASHMEM_NAME_LEN))) | |
ret = -EFAULT; | |
asma->name[ASHMEM_FULL_NAME_LEN-] = '\0'; | |
| |
out: | |
mutex_unlock(&ashmem_mutex); | |
| |
return ret; | |
} +------------------------------------------------------+ |
V |
static int get_name(struct ashmem_area *asma, void __user *name) |
{ |
int ret = ; |
|
mutex_lock(&ashmem_mutex); |
if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0') { |
size_t len; |
|
/* |
* Copying only `len', instead of ASHMEM_NAME_LEN, bytes |
* prevents us from revealing one user's stack to another. |
*/ |
len = strlen(asma->name + ASHMEM_NAME_PREFIX_LEN) + ; |
if (unlikely(copy_to_user(name, |
asma->name + ASHMEM_NAME_PREFIX_LEN, len))) |
ret = -EFAULT; |
} else { |
if (unlikely(copy_to_user(name, ASHMEM_NAME_DEF, |
sizeof(ASHMEM_NAME_DEF)))) |
ret = -EFAULT; |
} |
mutex_unlock(&ashmem_mutex); |
|
return ret; |
} |
|
static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) <--------+
{
struct ashmem_area *asma = file->private_data;
int ret = ; mutex_lock(&ashmem_mutex); /* user needs to SET_SIZE before mapping */
if (unlikely(!asma->size)) {
ret = -EINVAL;
goto out;
} /* requested protection bits must match our allowed protection mask */
if (unlikely((vma->vm_flags & ~calc_vm_prot_bits(asma->prot_mask)) &
calc_vm_prot_bits(PROT_MASK))) {
ret = -EPERM;
goto out;
}
vma->vm_flags &= ~calc_vm_may_flags(~asma->prot_mask); if (!asma->file) {
char *name = ASHMEM_NAME_DEF;
struct file *vmfile; if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0')
name = asma->name; /* ... and allocate the backing shmem file */
vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);
if (unlikely(IS_ERR(vmfile))) {
ret = PTR_ERR(vmfile);
goto out;
}
asma->file = vmfile;
}
// #define get_file(x) atomic_long_inc(&(x)->f_count)
get_file(asma->file); if (vma->vm_flags & VM_SHARED)
shmem_set_file(vma, asma->file); --------------+
else { |
if (vma->vm_file) |
fput(vma->vm_file); |
vma->vm_file = asma->file; |
} |
vma->vm_flags |= VM_CAN_NONLINEAR; |
|
out: |
mutex_unlock(&ashmem_mutex); |
return ret; |
} +------------------------------------------+
v
void shmem_set_file(struct vm_area_struct *vma, struct file *file)
{
if (vma->vm_file)
fput(vma->vm_file); -------------+
vma->vm_file = file; |
vma->vm_ops = &shmem_vm_ops; |
vma->vm_flags |= VM_CAN_NONLINEAR; |
} |
|
void fput(struct file *file) <------------+
{
if (atomic_long_dec_and_test(&file->f_count))
__fput(file);
}