| From cb22f247f371bd206a88cf0e0c05d80b8b62fb26 Mon Sep 17 00:00:00 2001 |
| From: John David Anglin <dave.anglin@bell.net> |
| Date: Mon, 21 Jul 2025 15:13:42 -0400 |
| Subject: parisc: Update comments in make_insert_tlb |
| |
| From: John David Anglin <dave.anglin@bell.net> |
| |
| commit cb22f247f371bd206a88cf0e0c05d80b8b62fb26 upstream. |
| |
| The following testcase exposed a problem with our read access checks |
| in get_user() and raw_copy_from_user(): |
| |
| #include <stdint.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| |
| int main(int argc, char **argv) |
| { |
| unsigned long page_size = sysconf(_SC_PAGESIZE); |
| char *p = malloc(3 * page_size); |
| char *p_aligned; |
| |
| /* initialize memory region. If not initialized, write syscall below will correctly return EFAULT. */ |
| if (1) |
| memset(p, 'X', 3 * page_size); |
| |
| p_aligned = (char *) ((((uintptr_t) p) + (2*page_size - 1)) & ~(page_size - 1)); |
| /* Drop PROT_READ protection. Kernel and userspace should fault when accessing that memory region */ |
| mprotect(p_aligned, page_size, PROT_NONE); |
| |
| /* the following write() should return EFAULT, since PROT_READ was dropped by previous mprotect() */ |
| int ret = write(2, p_aligned, 1); |
| if (!ret || errno != EFAULT) |
| printf("\n FAILURE: write() did not returned expected EFAULT value\n"); |
| |
| return 0; |
| } |
| |
| Because of the way _PAGE_READ is handled, kernel code never generates |
| a read access fault when it access a page as the kernel privilege level |
| is always less than PL1 in the PTE. |
| |
| This patch reworks the comments in the make_insert_tlb macro to try |
| to make this clearer. |
| |
| Signed-off-by: John David Anglin <dave.anglin@bell.net> |
| Signed-off-by: Helge Deller <deller@gmx.de> |
| Cc: stable@vger.kernel.org # v5.12+ |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/parisc/kernel/entry.S | 17 ++++++++++++----- |
| 1 file changed, 12 insertions(+), 5 deletions(-) |
| |
| --- a/arch/parisc/kernel/entry.S |
| +++ b/arch/parisc/kernel/entry.S |
| @@ -499,6 +499,12 @@ |
| * this happens is quite subtle, read below */ |
| .macro make_insert_tlb spc,pte,prot,tmp |
| space_to_prot \spc \prot /* create prot id from space */ |
| + |
| +#if _PAGE_SPECIAL_BIT == _PAGE_DMB_BIT |
| + /* need to drop DMB bit, as it's used as SPECIAL flag */ |
| + depi 0,_PAGE_SPECIAL_BIT,1,\pte |
| +#endif |
| + |
| /* The following is the real subtlety. This is depositing |
| * T <-> _PAGE_REFTRAP |
| * D <-> _PAGE_DIRTY |
| @@ -511,17 +517,18 @@ |
| * Finally, _PAGE_READ goes in the top bit of PL1 (so we |
| * trigger an access rights trap in user space if the user |
| * tries to read an unreadable page */ |
| -#if _PAGE_SPECIAL_BIT == _PAGE_DMB_BIT |
| - /* need to drop DMB bit, as it's used as SPECIAL flag */ |
| - depi 0,_PAGE_SPECIAL_BIT,1,\pte |
| -#endif |
| depd \pte,8,7,\prot |
| |
| /* PAGE_USER indicates the page can be read with user privileges, |
| * so deposit X1|11 to PL1|PL2 (remember the upper bit of PL1 |
| - * contains _PAGE_READ) */ |
| + * contains _PAGE_READ). While the kernel can't directly write |
| + * user pages which have _PAGE_WRITE zero, it can read pages |
| + * which have _PAGE_READ zero (PL <= PL1). Thus, the kernel |
| + * exception fault handler doesn't trigger when reading pages |
| + * that aren't user read accessible */ |
| extrd,u,*= \pte,_PAGE_USER_BIT+32,1,%r0 |
| depdi 7,11,3,\prot |
| + |
| /* If we're a gateway page, drop PL2 back to zero for promotion |
| * to kernel privilege (so we can execute the page as kernel). |
| * Any privilege promotion page always denys read and write */ |