When protecting a superpage, we would previously fall through to the
non-superpage case and read the contents of the superpage as PTEs, potentially
modifying them and trying to look up underlying VM pages that don't exist if
they happen to look like PTEs we would care about. This led to nginx causing an
unexpected page fault in pmap_protect that panic'ed the kernel. Instead, if we
see a superpage, we are done for this range and should continue to the next.
Details
Details
- Reviewers
markj jhb br - Commits
- rS361010: riscv: Fix pmap_protect for superpages
The following test case previously panicked the kernel in the same way and no
longer does (courtesy of jhb):
#include <sys/types.h> #include <sys/mman.h> #include <machine/pte.h> #include <err.h> #include <stdio.h> #include <unistd.h> #define MAP_LENGTH (16 * 1024 * 1024) /* * We need to dirty all the pages to get them promoted to superpages. * The first page in each superpage also needs to "look" like a page * of dirty PTEs (PTE_V | PTE_SW_MANAGED | PTE_D) but with physical * addresses that are invalid so that PHYS_TO_VM_PAGE returns NULL. */ static void touch_pages(char *p, size_t len) { pt_entry_t *l3; void *end; l3 = (pt_entry_t *)p; end = p + len; while (l3 < end) { *l3 = 0xfffffffffffffe00 | PTE_SW_MANAGED | PTE_D | PTE_V; l3++; } } int main(int ac, char **av) { char *p; pid_t pid; int i; p = mmap(NULL, MAP_LENGTH, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0); if (p == MAP_FAILED) err(1, "mmap"); printf("mapping: %p\n", p); touch_pages(p, MAP_LENGTH); pid = fork(); if (pid == -1) err(1, "fork"); if (pid == 0) { printf("child %d\n", (int)getpid()); return (0); } return (0); }
Diff Detail
Diff Detail
- Repository
- rS FreeBSD src repository - subversion
- Lint
Lint Not Applicable - Unit
Tests Not Applicable