Page MenuHomeFreeBSD

riscv: Fix pmap_protect for superpages
ClosedPublic

Authored by jrtc27 on May 13 2020, 4:47 PM.
Tags
None
Referenced Files
Unknown Object (File)
Thu, Nov 21, 9:51 AM
Unknown Object (File)
Mon, Nov 18, 3:15 PM
Unknown Object (File)
Oct 10 2024, 3:54 PM
Unknown Object (File)
Oct 2 2024, 6:38 PM
Unknown Object (File)
Sep 29 2024, 1:43 PM
Unknown Object (File)
Sep 27 2024, 3:15 PM
Unknown Object (File)
Sep 26 2024, 10:49 PM
Unknown Object (File)
Sep 25 2024, 10:21 PM
Subscribers

Details

Summary

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.

Test Plan

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

Repository
rS FreeBSD src repository - subversion
Lint
Lint Not Applicable
Unit
Tests Not Applicable