Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

arm64: io: Extract user memory type in ioremap_prot()

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>

+15 -2
+15 -2
arch/arm64/include/asm/io.h
··· 266 266 int arm64_ioremap_prot_hook_register(const ioremap_prot_hook_t hook); 267 267 void __iomem *__ioremap_prot(phys_addr_t phys, size_t size, pgprot_t prot); 268 268 269 - #define ioremap_prot __ioremap_prot 269 + static inline void __iomem *ioremap_prot(phys_addr_t phys, size_t size, 270 + pgprot_t user_prot) 271 + { 272 + pgprot_t prot; 273 + ptdesc_t user_prot_val = pgprot_val(user_prot); 270 274 271 - #define _PAGE_IOREMAP PROT_DEVICE_nGnRE 275 + if (WARN_ON_ONCE(!(user_prot_val & PTE_USER))) 276 + return NULL; 272 277 278 + prot = __pgprot_modify(PAGE_KERNEL, PTE_ATTRINDX_MASK, 279 + user_prot_val & PTE_ATTRINDX_MASK); 280 + return __ioremap_prot(phys, size, prot); 281 + } 282 + #define ioremap_prot ioremap_prot 283 + 284 + #define ioremap(addr, size) \ 285 + __ioremap_prot((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) 273 286 #define ioremap_wc(addr, size) \ 274 287 __ioremap_prot((addr), (size), __pgprot(PROT_NORMAL_NC)) 275 288 #define ioremap_np(addr, size) \