您的位置:首页 > 移动开发 > Android开发

Android ashmem hacking

2016-01-12 10:48 344 查看
/**********************************************************************
*                     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),
0, 0, 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),
0, 0, 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 0;                                                         |
}                                                                     |
|
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 0;     +-----------------------------------------------------------+ |
}                 |                                                             |
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 = 0;                                           |     |          |
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 = 0,                               |     |          |
};                                                 |     |          |
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 = 0;                                                     |          |
|          |
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-1] = '\0';                       |          |
|          |
out:                                                                 |          |
mutex_unlock(&ashmem_mutex);                                     |          |
|          |
return ret;                                                      |          |
}             +------------------------------------------------------+          |
V                                                                 |
static int get_name(struct ashmem_area *asma, void __user *name)                |
{                                                                               |
int ret = 0;                                                                |
|
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) + 1;                  |
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 = 0;

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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: