Page MenuHomeFreeBSD

riscv: Fix pmap_protect for superpages
ClosedPublic

Authored by jrtc27 on May 13 2020, 4:47 PM.
Tags
None
Referenced Files
F110349752: D24827.id71725.diff
Mon, Feb 17, 4:34 AM
F110341320: D24827.diff
Mon, Feb 17, 2:04 AM
Unknown Object (File)
Jan 11 2025, 8:25 PM
Unknown Object (File)
Jan 10 2025, 10:37 AM
Unknown Object (File)
Jan 10 2025, 10:34 AM
Unknown Object (File)
Jan 2 2025, 3:55 PM
Unknown Object (File)
Dec 26 2024, 2:57 PM
Unknown Object (File)
Dec 24 2024, 7:12 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 Passed
Unit
No Test Coverage
Build Status
Buildable 31064
Build 28763: arc lint + arc unit