From b4fef39187504f7a0b62e0d92d3d5b3e77d68339 Mon Sep 17 00:00:00 2001 From: "Isaac J. Manjarres" Date: Tue, 18 Feb 2025 11:34:06 -0800 Subject: [PATCH] ANDROID: ashmem: Add toggle to ignore requests to deny PROT_EXEC mappings Memfd does not support preventing a file from being mapped with PROT_EXEC, as ashmem does. It would be useful to expose a knob to userspace to change ashmem's behavior to match memfd to see if any issues arise during tests. Therefore, expose a tunable that userspace can use to cause ashmem to ignore requests to deny PROT_EXEC mappings. Bug: 111903542 Change-Id: I3da63d899c4753aa704092bf8e8a2568500fa833 Signed-off-by: Isaac J. Manjarres --- drivers/staging/android/ashmem.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 948baaf2c1b2..38871d593cba 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -108,13 +108,15 @@ static struct lock_class_key backing_shmem_inode_class; static bool unpinning_enable = true; /* - * memfd does not allow removing permissions to map a buffer with PROT_READ. This variable - * is exposed as a tunable so that it can be used to make ashmem behave more like memfd for - * test purposes. + * memfd does not allow removing permissions to map a buffer with PROT_READ or PROT_EXEC. These + * variables are exposed as tunables so that they can be used to make ashmem behave more like memfd + * for test purposes. * - * It is set to false by default to retain compatibility with the original behavior of the driver. + * They are set to false by default to retain compatibility with the original behavior of the + * driver. */ static bool ignore_unset_prot_read; +static bool ignore_unset_prot_exec; static inline unsigned long range_size(struct ashmem_range *range) { @@ -562,6 +564,10 @@ static int set_prot_mask(struct ashmem_area *asma, unsigned long prot) if (ignore_unset_prot_read) prot |= asma->prot_mask & PROT_READ; + /* Ensure the buffer can only be mapped with PROT_EXEC iff it has that permission. */ + if (ignore_unset_prot_exec) + prot |= asma->prot_mask & PROT_EXEC; + /* the user can only remove, not add, protection bits */ if ((asma->prot_mask & prot) != prot) { ret = -EINVAL; @@ -953,6 +959,12 @@ static int ignore_unset_prot_read_open(struct inode *inode, struct file *file) return 0; } +static int ignore_unset_prot_exec_open(struct inode *inode, struct file *file) +{ + file->private_data = &ignore_unset_prot_exec; + return 0; +} + static ssize_t attr_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { bool *attrp = file->private_data; @@ -1019,6 +1031,13 @@ static const struct file_operations ignore_unset_prot_read_fops = { .write = attr_write, }; +static const struct file_operations ignore_unset_prot_exec_fops = { + .owner = THIS_MODULE, + .open = ignore_unset_prot_exec_open, + .read= attr_read, + .write = attr_write, +}; + static struct miscdevice ashmem_miscs[] = { { .minor = MISC_DYNAMIC_MINOR, @@ -1035,6 +1054,11 @@ static struct miscdevice ashmem_miscs[] = { .name = "ashmem_ignore_unset_prot_read", .fops = &ignore_unset_prot_read_fops, }, + { + .minor = MISC_DYNAMIC_MINOR, + .name = "ashmem_ignore_unset_prot_exec", + .fops = &ignore_unset_prot_exec_fops, + }, }; static int __init ashmem_init(void)