diff options
author | Serban Constantinescu <serban.constantinescu@arm.com> | 2012-09-25 16:58:13 +0100 |
---|---|---|
committer | Zach Pfeffer <zach.pfeffer@linaro.org> | 2012-10-04 21:03:16 -0500 |
commit | 57952f2f3c171519877d0f37785cb8554577d4e5 (patch) | |
tree | 6443539b94dd504ed530000b8f2db46f6cefb4d2 | |
parent | 1819da661a46fd3c7f5919df64ff31f0c597236c (diff) | |
download | linux-aarch64-57952f2f3c171519877d0f37785cb8554577d4e5.tar.gz |
Android32: Add support for 32-bit Ashmem calls in a 64-bit kernel
Android's shared memory subsystem, Ashmem, does not support calls from a
32-bit userspace in a 64 bit kernel. This patch adds support for syscalls
from a 32-bit userspace in a 64-bit kernel.
Most of the changes are applied to types that change sizes between
32 and 64 bit world. The patch has been tested successfully on ARMv8
AEM.
NOTE: The patch should be considered development quality.
Signed-off-by: Serban Constantinescu <serban.constantinescu@arm.com>
-rw-r--r-- | drivers/staging/android/ashmem.c | 68 | ||||
-rw-r--r-- | drivers/staging/android/ashmem.h | 4 |
2 files changed, 51 insertions, 21 deletions
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 69cf2db1d69..77234bbef8e 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -586,6 +586,22 @@ static int ashmem_get_pin_status(struct ashmem_area *asma, size_t pgstart, return ret; } +/* checks pointers when casting back to 32-bit user-space usage + * it will check that the high 4bytes are 0; thus a 32-bit userspace pointer + */ +static unsigned int revert_ptr(unsigned long ptr) +{ + unsigned int ptr_compat = (unsigned int) ptr; + unsigned int ptr_assert = (unsigned int) (ptr >> 32); + + if(unlikely(ptr_assert != 0)) + { + printk("ERROR: compat_ptr failed in ashmem!\n"); + return 0; + } + return ptr_compat; +} + static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd, void __user *p) { @@ -595,13 +611,12 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd, if (unlikely(!asma->file)) return -EINVAL; - if (unlikely(copy_from_user(&pin, p, sizeof(pin)))) return -EFAULT; /* per custom, you can pass zero for len to mean "everything onward" */ if (!pin.len) - pin.len = PAGE_ALIGN(asma->size) - pin.offset; + pin.len = revert_ptr(PAGE_ALIGN(asma->size) - pin.offset); if (unlikely((pin.offset | pin.len) & ~PAGE_MASK)) return -EINVAL; @@ -639,37 +654,49 @@ 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); + switch (_IOC_NR(cmd)) { + + case _IOC_NR(ASHMEM_SET_NAME): + if(_IOC_SIZE(ASHMEM_SET_NAME) != sizeof(char[ASHMEM_NAME_LEN])) + printk(KERN_ERR "ashmem: ERROR: ioctl: SET_NAME: sizeof U/K structure differ\n"); + ret = set_name(asma, (void __user *) compat_ptr(arg)); break; - case ASHMEM_GET_NAME: - ret = get_name(asma, (void __user *) arg); + case _IOC_NR(ASHMEM_GET_NAME): + if(_IOC_SIZE(ASHMEM_GET_NAME) != sizeof(char[ASHMEM_NAME_LEN])) + printk(KERN_ERR "ashmem: ERROR: ioctl: GET_NAME: sizeof U/K structure differ\n"); + ret = get_name(asma, (void __user *) compat_ptr(arg)); break; - case ASHMEM_SET_SIZE: + case _IOC_NR(ASHMEM_SET_SIZE): + if(_IOC_SIZE(ASHMEM_SET_SIZE) != sizeof(uint32_t)) + printk(KERN_ERR "ashmem: ERROR: ioctl: SET_SIZE: sizeof U/K structure differ\n"); ret = -EINVAL; if (!asma->file) { ret = 0; asma->size = (size_t) arg; } break; - case ASHMEM_GET_SIZE: + case _IOC_NR(ASHMEM_GET_SIZE): ret = asma->size; - break; - case ASHMEM_SET_PROT_MASK: + break; + case _IOC_NR(ASHMEM_SET_PROT_MASK): + if(_IOC_SIZE(ASHMEM_SET_PROT_MASK) != sizeof(uint32_t)) + printk(KERN_ERR "ashmem: ERROR: ioctl: ASHMEM_SET_PROT_MASK: sizeof U/K structure differ\n"); 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); + case _IOC_NR(ASHMEM_GET_PROT_MASK): + ret = asma->prot_mask; + break; + case _IOC_NR(ASHMEM_PIN): + case _IOC_NR(ASHMEM_UNPIN): + case _IOC_NR(ASHMEM_GET_PIN_STATUS): + if(_IOC_SIZE(ASHMEM_PIN) != sizeof(struct ashmem_pin)) + printk(KERN_ERR "ashmem: ERROR: ioctl: ASHMEM_PIN: sizeof U/K structure differ\n"); + ret = ashmem_pin_unpin(asma, cmd, (void __user *) compat_ptr(arg)); break; - case ASHMEM_PURGE_ALL_CACHES: + case _IOC_NR(ASHMEM_PURGE_ALL_CACHES): ret = -EPERM; if (capable(CAP_SYS_ADMIN)) { + printk(KERN_ERR "ashmem: ioctl: ASHMEM_PURGE: user: sys-administrator\n"); struct shrink_control sc = { .gfp_mask = GFP_KERNEL, .nr_to_scan = 0, @@ -679,6 +706,9 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ashmem_shrink(&ashmem_shrinker, &sc); } break; + default: + printk(KERN_ERR "ashmem: ERROR: ioctl: IOCTL No. does not match\n"); + break; } return ret; diff --git a/drivers/staging/android/ashmem.h b/drivers/staging/android/ashmem.h index 1976b10ef93..f19d8908eac 100644 --- a/drivers/staging/android/ashmem.h +++ b/drivers/staging/android/ashmem.h @@ -36,9 +36,9 @@ struct ashmem_pin { #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_SET_SIZE _IOW(__ASHMEMIOC, 3, uint32_t) #define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4) -#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long) +#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, uint32_t) #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) |