Merge android13-5.15 into android13-5.15-lts
This merges the android13-5.15 branch into the -lts branch, catching
it up with the latest changes in there.
It contains the following commits:
* f7dbe96d1c39 Merge tag 'android13-5.15.182_r00' into android13-5.15
* 70ff3b45ba88 ANDROID: binder: fix minimum node priority comparison
* c128f36c63bd UPSTREAM: PCI/ASPM: Fix L1SS saving
* bfdd4619d735 UPSTREAM: PCI/ASPM: Save parent L1SS config in pci_save_aspm_l1ss_state()
* daab5d890e9f ANDROID: 16K: Remove ELF padding entry from map_file ranges
* 0caacc39200e ANDROID: ABI: Add to QCOM symbols list
* edfaa2dd3fcb UPSTREAM: binder: fix potential UAF of target_{proc,thread}
* b9a389cabebd ANDROID: GKI: Enable CONFIG_MEMFD_ASHMEM_SHIM
* 74fde7206b97 ANDROID: mm: shmem: Use memfd-ashmem-shim ioctl handler
* 46cd1ff96ae1 ANDROID: mm/memfd-ashmem-shim: Introduce shim layer
Change-Id: I8b4c8f7562a07af98936e80ea88ef8a4a7216a77
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
diff --git a/android/abi_gki_aarch64_qcom b/android/abi_gki_aarch64_qcom
index 9af3d1d..ddadbb3 100644
--- a/android/abi_gki_aarch64_qcom
+++ b/android/abi_gki_aarch64_qcom
@@ -116,6 +116,7 @@
blk_mq_init_queue
blk_mq_map_queues
blk_mq_pci_map_queues
+ blk_mq_quiesce_queue_nowait
blk_mq_requeue_request
blk_mq_rq_cpu
blk_mq_tagset_busy_iter
diff --git a/arch/arm64/configs/gki_defconfig b/arch/arm64/configs/gki_defconfig
index ea8e961..518fa258 100644
--- a/arch/arm64/configs/gki_defconfig
+++ b/arch/arm64/configs/gki_defconfig
@@ -127,6 +127,7 @@
# CONFIG_ZONE_DMA is not set
CONFIG_ANON_VMA_NAME=y
CONFIG_LRU_GEN=y
+CONFIG_MEMFD_ASHMEM_SHIM=y
CONFIG_DAMON=y
CONFIG_DAMON_PADDR=y
CONFIG_DAMON_RECLAIM=y
diff --git a/arch/x86/configs/gki_defconfig b/arch/x86/configs/gki_defconfig
index 7601911..9201883 100644
--- a/arch/x86/configs/gki_defconfig
+++ b/arch/x86/configs/gki_defconfig
@@ -116,6 +116,7 @@
# CONFIG_ZONE_DMA is not set
CONFIG_ANON_VMA_NAME=y
CONFIG_LRU_GEN=y
+CONFIG_MEMFD_ASHMEM_SHIM=y
CONFIG_DAMON=y
CONFIG_DAMON_PADDR=y
CONFIG_DAMON_RECLAIM=y
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index b06c14a..ccaa9f2 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -815,8 +815,8 @@ static void binder_transaction_priority(struct binder_thread *thread,
desired.sched_policy = SCHED_NORMAL;
}
- if (node_prio.prio < t->priority.prio ||
- (node_prio.prio == t->priority.prio &&
+ if (node_prio.prio < desired.prio ||
+ (node_prio.prio == desired.prio &&
node_prio.sched_policy == SCHED_FIFO)) {
/*
* In case the minimum priority on the node is
@@ -3842,10 +3842,6 @@ static void binder_transaction(struct binder_proc *proc,
err_empty_call_stack:
err_dead_binder:
err_invalid_target_handle:
- if (target_thread)
- binder_thread_dec_tmpref(target_thread);
- if (target_proc)
- binder_proc_dec_tmpref(target_proc);
if (target_node) {
binder_dec_node(target_node, 1, 0);
binder_dec_node_tmpref(target_node);
@@ -3861,6 +3857,11 @@ static void binder_transaction(struct binder_proc *proc,
tr->code, (u64)tr->data_size, (u64)tr->offsets_size,
return_error_line);
+ if (target_thread)
+ binder_thread_dec_tmpref(target_thread);
+ if (target_proc)
+ binder_proc_dec_tmpref(target_proc);
+
{
struct binder_transaction_log_entry *fe;
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 949cc54..798a71f 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -75,24 +75,44 @@ void pci_configure_aspm_l1ss(struct pci_dev *pdev)
void pci_save_aspm_l1ss_state(struct pci_dev *pdev)
{
+ struct pci_dev *parent = pdev->bus->self;
struct pci_cap_saved_state *save_state;
- u16 l1ss = pdev->l1ss;
u32 *cap;
/*
+ * If this is a Downstream Port, we never restore the L1SS state
+ * directly; we only restore it when we restore the state of the
+ * Upstream Port below it.
+ */
+ if (pcie_downstream_port(pdev) || !parent)
+ return;
+
+ if (!pdev->l1ss || !parent->l1ss)
+ return;
+
+ /*
* Save L1 substate configuration. The ASPM L0s/L1 configuration
* in PCI_EXP_LNKCTL_ASPMC is saved by pci_save_pcie_state().
*/
- if (!l1ss)
- return;
-
save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_L1SS);
if (!save_state)
return;
cap = &save_state->cap.data[0];
- pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL2, cap++);
- pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, cap++);
+ pci_read_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL2, cap++);
+ pci_read_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, cap++);
+
+ /*
+ * Save parent's L1 substate configuration so we have it for
+ * pci_restore_aspm_l1ss_state(pdev) to restore.
+ */
+ save_state = pci_find_saved_ext_cap(parent, PCI_EXT_CAP_ID_L1SS);
+ if (!save_state)
+ return;
+
+ cap = &save_state->cap.data[0];
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, cap++);
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, cap++);
}
static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 211e330..7624ae2 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -65,6 +65,7 @@
#include <linux/namei.h>
#include <linux/mnt_namespace.h>
#include <linux/mm.h>
+#include <linux/pgsize_migration.h>
#include <linux/swap.h>
#include <linux/rcupdate.h>
#include <linux/kallsyms.h>
@@ -2462,7 +2463,7 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
}
p->start = vma->vm_start;
- p->end = vma->vm_end;
+ p->end = VMA_PAD_START(vma);
p->mode = vma->vm_file->f_mode;
}
mmap_read_unlock(mm);
diff --git a/mm/Kconfig b/mm/Kconfig
index ae181c7..9d982e5 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -942,6 +942,17 @@
This option has a per-memcg and per-node memory overhead.
# }
+config MEMFD_ASHMEM_SHIM
+ bool "Memfd ashmem ioctl compatibility support"
+ depends on MEMFD_CREATE
+ help
+ This provides compatibility support for ashmem ioctl commands against
+ memfd file descriptors. This is useful for compatibility on Android
+ for older applications that may use ashmem's ioctl commands on the
+ now memfds passed to them.
+
+ Unless you are running Android, say N.
+
source "mm/damon/Kconfig"
config ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT
diff --git a/mm/Makefile b/mm/Makefile
index a91fd8a..0d74b5a 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -125,6 +125,7 @@
obj-$(CONFIG_ZONE_DEVICE) += memremap.o
obj-$(CONFIG_HMM_MIRROR) += hmm.o
obj-$(CONFIG_MEMFD_CREATE) += memfd.o
+obj-$(CONFIG_MEMFD_ASHMEM_SHIM) += memfd-ashmem-shim.o
obj-$(CONFIG_MAPPING_DIRTY_HELPERS) += mapping_dirty_helpers.o
obj-$(CONFIG_PTDUMP_CORE) += ptdump.o
obj-$(CONFIG_PAGE_REPORTING) += page_reporting.o
diff --git a/mm/memfd-ashmem-shim-internal.h b/mm/memfd-ashmem-shim-internal.h
new file mode 100644
index 0000000..b499434
--- /dev/null
+++ b/mm/memfd-ashmem-shim-internal.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Ashmem compatability for memfd
+ *
+ * Copyright (c) 2025, Google LLC.
+ * Author: Isaac J. Manjarres <isaacmanjarres@google.com>
+ */
+
+#ifndef _MM_MEMFD_ASHMEM_SHIM_INTERNAL_H
+#define _MM_MEMFD_ASHMEM_SHIM_INTERNAL_H
+
+#include <linux/compat.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ASHMEM_NAME_LEN 256
+
+/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
+#define ASHMEM_NOT_PURGED 0
+#define ASHMEM_WAS_PURGED 1
+
+/* Return values from ASHMEM_GET_PIN_STATUS: Is the mapping pinned? */
+#define ASHMEM_IS_UNPINNED 0
+#define ASHMEM_IS_PINNED 1
+
+struct ashmem_pin {
+ __u32 offset; /* offset into region, in bytes, page-aligned */
+ __u32 len; /* length forward from offset, in bytes, page-aligned */
+};
+
+#define __ASHMEMIOC 0x77
+
+#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_GET_SIZE _IO(__ASHMEMIOC, 4)
+#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long)
+#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)
+#define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9)
+#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)
+#define ASHMEM_GET_FILE_ID _IOR(__ASHMEMIOC, 11, unsigned long)
+
+/* support of 32bit userspace on 64bit platforms */
+#ifdef CONFIG_COMPAT
+#define COMPAT_ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, compat_size_t)
+#define COMPAT_ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned int)
+#endif
+
+#endif /* _MM_MEMFD_ASHMEM_SHIM_INTERNAL_H */
diff --git a/mm/memfd-ashmem-shim.c b/mm/memfd-ashmem-shim.c
new file mode 100644
index 0000000..258498cc
--- /dev/null
+++ b/mm/memfd-ashmem-shim.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Ashmem compatability for memfd
+ *
+ * Copyright (c) 2025, Google LLC.
+ * Author: Isaac J. Manjarres <isaacmanjarres@google.com>
+ */
+
+#include <asm-generic/mman-common.h>
+#include <linux/capability.h>
+#include <linux/fs.h>
+#include <linux/memfd.h>
+#include <linux/uaccess.h>
+
+#include "memfd-ashmem-shim.h"
+#include "memfd-ashmem-shim-internal.h"
+
+/* memfd file names all start with memfd: */
+#define MEMFD_PREFIX "memfd:"
+#define MEMFD_PREFIX_LEN (sizeof(MEMFD_PREFIX) - 1)
+
+static const char *get_memfd_name(struct file *file)
+{
+ /* This pointer is always valid, so no need to check if it's NULL. */
+ const char *file_name = file->f_path.dentry->d_name.name;
+
+ if (file_name != strstr(file_name, MEMFD_PREFIX))
+ return NULL;
+
+ return file_name;
+}
+
+static long get_name(struct file *file, void __user *name)
+{
+ const char *file_name = get_memfd_name(file);
+ size_t len;
+
+ if (!file_name)
+ return -EINVAL;
+
+ /* Strip MEMFD_PREFIX to retain compatibility with ashmem driver. */
+ file_name = &file_name[MEMFD_PREFIX_LEN];
+
+ /*
+ * The expectation is that the user provided buffer is ASHMEM_NAME_LEN in size, which is
+ * larger than the maximum size of a name for a memfd buffer, so the name should always fit
+ * within the given buffer.
+ *
+ * However, we should ensure that the string will indeed fit in the user provided buffer.
+ *
+ * Add 1 to the copy size to account for the NUL terminator
+ */
+ len = strlen(file_name) + 1;
+ if (len > ASHMEM_NAME_LEN)
+ return -EINVAL;
+
+ return copy_to_user(name, file_name, len) ? -EFAULT : 0;
+}
+
+static long get_prot_mask(struct file *file)
+{
+ long prot_mask = PROT_READ | PROT_EXEC;
+ long seals = memfd_fcntl(file, F_GET_SEALS, 0);
+
+ if (seals < 0)
+ return seals;
+
+ /* memfds are readable and executable by default. Only writability can be changed. */
+ if (!(seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE)))
+ prot_mask |= PROT_WRITE;
+
+ return prot_mask;
+}
+
+static long set_prot_mask(struct file *file, unsigned long prot)
+{
+ long curr_prot = get_prot_mask(file);
+ long ret = 0;
+
+ if (curr_prot < 0)
+ return curr_prot;
+
+ /*
+ * memfds are always readable and executable; there is no way to remove either mapping
+ * permission, nor is there a known usecase that requires it.
+ *
+ * Attempting to remove either of these mapping permissions will return successfully, but
+ * will be a nop, as the buffer will still be mappable with these permissions.
+ */
+ prot |= PROT_READ | PROT_EXEC;
+
+ /* Only allow permissions to be removed. */
+ if ((curr_prot & prot) != prot)
+ return -EINVAL;
+
+ /*
+ * Removing PROT_WRITE:
+ *
+ * We could prevent any other mappings from having write permissions by adding the
+ * F_SEAL_WRITE mapping. However, that would conflict with known usecases where it is
+ * desirable to maintain an existing writable mapping, but forbid future writable mappings.
+ *
+ * To support those usecases, we use F_SEAL_FUTURE_WRITE.
+ */
+ if (!(prot & PROT_WRITE))
+ ret = memfd_fcntl(file, F_ADD_SEALS, F_SEAL_FUTURE_WRITE);
+
+ return ret;
+}
+
+/*
+ * memfd_ashmem_shim_ioctl - ioctl handler for ashmem commands
+ * @file: The shmem file.
+ * @cmd: The ioctl command.
+ * @arg: The argument for the ioctl command.
+ *
+ * The purpose of this handler is to allow old applications to continue working
+ * on newer kernels by allowing them to invoke ashmem ioctl commands on memfds.
+ *
+ * The ioctl handler attempts to retain as much compatibility with the ashmem
+ * driver as possible.
+ */
+long memfd_ashmem_shim_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long ret = -ENOTTY;
+ unsigned long inode_nr;
+
+ switch (cmd) {
+ /*
+ * Older applications won't create memfds and try to use ASHMEM_SET_NAME/ASHMEM_SET_SIZE on
+ * them intentionally.
+ *
+ * Instead, we can end up in this scenario if an old application receives a memfd that was
+ * created by another process.
+ *
+ * However, the current process shouldn't expect to be able to reliably [re]name/size a
+ * buffer that was shared with it, since the process that shared that buffer with it, or
+ * any other process that references the buffer could have already mapped it.
+ *
+ * Additionally in the case of ASHMEM_SET_SIZE, when processes create memfds that are going
+ * to be shared with other processes in Android, they also specify the size of the memory
+ * region and seal the file against any size changes. Therefore, ASHMEM_SET_SIZE should not
+ * be supported anyway.
+ *
+ * Therefore, it is reasonable to return -EINVAL here, as if the buffer was already mapped.
+ */
+ case ASHMEM_SET_NAME:
+ case ASHMEM_SET_SIZE:
+ ret = -EINVAL;
+ break;
+ case ASHMEM_GET_NAME:
+ ret = get_name(file, (void __user *)arg);
+ break;
+ case ASHMEM_GET_SIZE:
+ ret = i_size_read(file_inode(file));
+ break;
+ case ASHMEM_SET_PROT_MASK:
+ ret = set_prot_mask(file, arg);
+ break;
+ case ASHMEM_GET_PROT_MASK:
+ ret = get_prot_mask(file);
+ break;
+ /*
+ * Unpinning ashmem buffers was deprecated with the release of Android 10,
+ * as it did not yield any remarkable benefits. Therefore, ignore pinning
+ * related requests.
+ *
+ * This makes it so that memory is always "pinned" or never entirely freed
+ * until all references to the ashmem buffer are dropped. The memory occupied
+ * by the buffer is still subject to being reclaimed (swapped out) under memory
+ * pressure, but that is not the same as being freed.
+ *
+ * This makes it so that:
+ *
+ * 1. Memory is always pinned and therefore never purged.
+ * 2. Requests to unpin memory (make it a candidate for being freed) are ignored.
+ */
+ case ASHMEM_PIN:
+ ret = ASHMEM_NOT_PURGED;
+ break;
+ case ASHMEM_UNPIN:
+ ret = 0;
+ break;
+ case ASHMEM_GET_PIN_STATUS:
+ ret = ASHMEM_IS_PINNED;
+ break;
+ case ASHMEM_PURGE_ALL_CACHES:
+ ret = capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
+ break;
+ case ASHMEM_GET_FILE_ID:
+ inode_nr = file_inode(file)->i_ino;
+ if (copy_to_user((void __user *)arg, &inode_nr, sizeof(inode_nr)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+long memfd_ashmem_shim_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ if (cmd == COMPAT_ASHMEM_SET_SIZE)
+ cmd = ASHMEM_SET_SIZE;
+ else if (cmd == COMPAT_ASHMEM_SET_PROT_MASK)
+ cmd = ASHMEM_SET_PROT_MASK;
+
+ return memfd_ashmem_shim_ioctl(file, cmd, arg);
+}
+#endif
diff --git a/mm/memfd-ashmem-shim.h b/mm/memfd-ashmem-shim.h
new file mode 100644
index 0000000..026789b0
--- /dev/null
+++ b/mm/memfd-ashmem-shim.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __MM_MEMFD_ASHMEM_SHIM_H
+#define __MM_MEMFD_ASHMEM_SHIM_H
+
+/*
+ * mm/memfd-ashmem-shim.h
+ *
+ * Ashmem compatability for memfd
+ *
+ * Copyright (c) 2025, Google LLC.
+ * Author: Isaac J. Manjarres <isaacmanjarres@google.com>
+ *
+ */
+
+#include <linux/fs.h>
+
+long memfd_ashmem_shim_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_COMPAT
+long memfd_ashmem_shim_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+#endif
+#endif /* __MM_MEMFD_ASHMEM_SHIM_H */
diff --git a/mm/shmem.c b/mm/shmem.c
index 7eada4d..23fc50a 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -85,6 +85,10 @@ static struct vfsmount *shm_mnt;
#include "internal.h"
+#ifdef CONFIG_MEMFD_ASHMEM_SHIM
+#include "memfd-ashmem-shim.h"
+#endif
+
#define BLOCKS_PER_PAGE (PAGE_SIZE/512)
#define VM_ACCT(size) (PAGE_ALIGN(size) >> PAGE_SHIFT)
@@ -3858,6 +3862,12 @@ static const struct file_operations shmem_file_operations = {
.splice_write = iter_file_splice_write,
.fallocate = shmem_fallocate,
#endif
+#ifdef CONFIG_MEMFD_ASHMEM_SHIM
+ .unlocked_ioctl = memfd_ashmem_shim_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = memfd_ashmem_shim_compat_ioctl,
+#endif
+#endif
};
static const struct inode_operations shmem_inode_operations = {