]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
arm64: io: Extract user memory type in ioremap_prot()
authorWill Deacon <will@kernel.org>
Mon, 23 Feb 2026 22:10:11 +0000 (22:10 +0000)
committerWill Deacon <will@kernel.org>
Wed, 25 Feb 2026 19:49:52 +0000 (19:49 +0000)
The only caller of ioremap_prot() outside of the generic ioremap()
implementation is generic_access_phys(), which passes a 'pgprot_t' value
determined from the user mapping of the target 'pfn' being accessed by
the kernel. On arm64, the 'pgprot_t' contains all of the non-address
bits from the pte, including the permission controls, and so we end up
returning a new user mapping from ioremap_prot() which faults when
accessed from the kernel on systems with PAN:

  | Unable to handle kernel read from unreadable memory at virtual address ffff80008ea89000
  | ...
  | Call trace:
  |   __memcpy_fromio+0x80/0xf8
  |   generic_access_phys+0x20c/0x2b8
  |   __access_remote_vm+0x46c/0x5b8
  |   access_remote_vm+0x18/0x30
  |   environ_read+0x238/0x3e8
  |   vfs_read+0xe4/0x2b0
  |   ksys_read+0xcc/0x178
  |   __arm64_sys_read+0x4c/0x68

Extract only the memory type from the user 'pgprot_t' in ioremap_prot()
and assert that we're being passed a user mapping, to protect us against
any changes in future that may require additional handling. To avoid
falsely flagging users of ioremap(), provide our own ioremap() macro
which simply wraps __ioremap_prot().

Cc: Zeng Heng <zengheng4@huawei.com>
Cc: Jinjiang Tu <tujinjiang@huawei.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Fixes: 893dea9ccd08 ("arm64: Add HAVE_IOREMAP_PROT support")
Reported-by: Jinjiang Tu <tujinjiang@huawei.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
arch/arm64/include/asm/io.h

index cd2fddfe814ac3be57be4c26cc621a200ef39ec7..8cbd1e96fd50bfe2671b314254b58cb34a44dfa1 100644 (file)
@@ -266,10 +266,23 @@ typedef int (*ioremap_prot_hook_t)(phys_addr_t phys_addr, size_t size,
 int arm64_ioremap_prot_hook_register(const ioremap_prot_hook_t hook);
 void __iomem *__ioremap_prot(phys_addr_t phys, size_t size, pgprot_t prot);
 
-#define ioremap_prot __ioremap_prot
+static inline void __iomem *ioremap_prot(phys_addr_t phys, size_t size,
+                                        pgprot_t user_prot)
+{
+       pgprot_t prot;
+       ptdesc_t user_prot_val = pgprot_val(user_prot);
+
+       if (WARN_ON_ONCE(!(user_prot_val & PTE_USER)))
+               return NULL;
 
-#define _PAGE_IOREMAP PROT_DEVICE_nGnRE
+       prot = __pgprot_modify(PAGE_KERNEL, PTE_ATTRINDX_MASK,
+                              user_prot_val & PTE_ATTRINDX_MASK);
+       return __ioremap_prot(phys, size, prot);
+}
+#define ioremap_prot ioremap_prot
 
+#define ioremap(addr, size)    \
+       __ioremap_prot((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
 #define ioremap_wc(addr, size) \
        __ioremap_prot((addr), (size), __pgprot(PROT_NORMAL_NC))
 #define ioremap_np(addr, size) \