mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] riscv: refuse PMLEN=16 when its tag bits overlap the canonical VA sign bit
@ 2026-10-07 14:15 Ziyi Guo
  0 siblings, 0 replies; only message in thread
From: Ziyi Guo @ 2026-10-07 14:15 UTC (permalink / raw)
  To: palmer, pjw, aou, alex, samuel.holland, thecharlesjenkins
  Cc: debug, zong.li, vulab, linux-riscv, linux-kernel, Ziyi Guo

The tagged address ABI lets userspace enable pointer masking (Supm) and
request a PMLEN of 7 or 16.  access_ok() validates untagged_addr(ptr),
which strips the top PMLEN bits and sign-extends from bit 63 - PMLEN.
Supervisor-mode accesses are not subject to the U-mode pointer masking,
so any site that dereferences a user pointer without re-applying
untagged_addr() -- e.g. the futex atomics and the unsafe_*() accessors,
operates on the raw, still-tagged address.

When the canonical VA sign bit (bit VA_BITS - 1) falls inside the masked
tag field, i.e. VA_BITS > 64 - PMLEN, an unprivileged task can craft a
pointer whose untagged form is a valid user address (so access_ok()
passes) but whose raw form is a canonical *kernel* VA.  Among the
supported configurations this is only Sv57 + PMLEN=16 (48 < 57): the tag
field [63:48] covers the Sv57 sign bit (56), so the pointer can name any
address in the linear map and a raw dereference becomes an arbitrary
kernel read/write.  Sv39/Sv48 are not reachable (the raw address is
non-canonical and faults) and Sv57 + PMLEN=7 is fine (the tag stays
above the sign bit).

It is triggerable and can be reproduced today under QEMU, which emulates
Supm and Sv57 (tested with qemu-system-riscv64 11.1.0, -cpu
rv64,sv57=on,supm=on): on an otherwise unmodified kernel an unprivileged
prctl(PR_SET_TAGGED_ADDR_CTRL, PMLEN=16) succeeds, after which a futex on
a tagged linear-map pointer, whose untagged form passes access_ok(), 
performs the atomic on the kernel address.

Only advertise PMLEN=16 when its tag bits sit entirely above the
canonical VA sign bit (PMLEN <= 64 - VA_BITS).  have_user_pmlen_16 is the
single gate used by both the prctl() and ptrace() paths, so this closes
the reachability for every such accessor at once.

This bounds reachability rather than fixing the individual raw
dereferences; the futex / unsafe_*() paths should additionally untag the
user pointer after access_ok() so that tagged pointers work there as the
ABI intends.

Link: https://lore.kernel.org/all/a25d01cd-e21d-4e51-9d24-6cc41589c041@sifive.com/
Fixes: 09d6775f503b ("riscv: Add support for userspace pointer masking")
Signed-off-by: Ziyi Guo <guoziyi114@gmail.com>
---
 arch/riscv/kernel/process.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
index 7cc5a6a5c020..afaeaaa247c2 100644
--- a/arch/riscv/kernel/process.c
+++ b/arch/riscv/kernel/process.c
@@ -433,7 +433,16 @@ static int __init tagged_addr_init(void)
 	 */
 	csr_clear(CSR_ENVCFG, ENVCFG_PMM);
 	have_user_pmlen_7 = try_to_set_pmm(ENVCFG_PMM_PMLEN_7);
-	have_user_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16);
+	/*
+	 * PMLEN=16 masks bits [63:48].  On Sv57 that overlaps the canonical VA
+	 * sign bit (bit 56), so a tagged user pointer whose untagged form
+	 * passes access_ok() can still name a canonical kernel VA when
+	 * dereferenced raw (the futex and unsafe_*() accessors do exactly
+	 * that).  Only offer a PMLEN whose tag bits stay above the sign bit,
+	 * i.e. PMLEN <= 64 - VA_BITS; this refuses only Sv57 + PMLEN=16.
+	 */
+	have_user_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16) &&
+			     VA_BITS <= 64 - PMLEN_16;
 
 	if (!register_sysctl("abi", tagged_addr_sysctl_table))
 		return -EINVAL;
-- 
2.34.1


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-10-07 14:16 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-07 14:15 [PATCH] riscv: refuse PMLEN=16 when its tag bits overlap the canonical VA sign bit Ziyi Guo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®