Skip to content

Commit

Permalink
[metal] Allow more fine-grained control over page permissions (#663)
Browse files Browse the repository at this point in the history
- use PAGE_RSRV bit (originally only for blinkenlights),
  rather than PAGE_V bit, to indicate that a virtual address
  page has been reserved — this should allow a program to
  create & reserve inaccessible "guard pages"
- mark page table entries for non-code pages with PAGE_XD bit,
  which should be supported on (circa) post-2004 x86-64 CPUs
  • Loading branch information
tkchia authored Oct 12, 2022
1 parent 0f89140 commit d387006
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 14 deletions.
8 changes: 4 additions & 4 deletions ape/ape.S
Original file line number Diff line number Diff line change
Expand Up @@ -1285,8 +1285,8 @@ lcheck: pushf # check for i8086 / i8088 / i80186
jl 10f
mov %edi,%eax
cpuid
mov $1<<29,%edi # need nexgen32e long mode support
and %edi,%edx
mov $1<<29|1<<20,%edi # need nexgen32e long mode support
and %edi,%edx # & nx support
cmp %edi,%edx
jne 10f
xor %ax,%ax
Expand Down Expand Up @@ -1415,7 +1415,7 @@ pinit: push %ds
movl $0x79000+PAGE_V+PAGE_RW,0x7b000-SEG # PD←PDT (+)
movl $0x79000+PAGE_V+PAGE_RW,0x7a000-SEG # PD←PDT (-)
mov $512,%cx # PD±2MB
mov $PAGE_V+PAGE_RW,%eax
mov $PAGE_V+PAGE_RSRV+PAGE_RW,%eax
xor %di,%di
0: stosl
add $0x1000,%eax
Expand All @@ -1438,7 +1438,7 @@ golong: cli
mov %eax,%cr4
movl $EFER,%ecx
rdmsr
or $EFER_LME|EFER_SCE,%eax
or $EFER_LME|EFER_SCE|EFER_NXE,%eax
wrmsr
lgdt REAL(_gdtrphy)
mov %cr0,%eax
Expand Down
10 changes: 8 additions & 2 deletions libc/intrin/directmap-metal.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ noasan struct DirectMap sys_mmap_metal(void *paddr, size_t size, int prot,
addr = 4096;
for (i = 0; i < size; i += 4096) {
pte = __get_virtual(mm, pml4t, addr + i, false);
if (pte && (*pte & PAGE_V)) {
if (pte && (*pte & (PAGE_V | PAGE_RSRV))) {
addr = MAX(addr, sys_mmap_metal_break) + i + 4096;
i = 0;
}
Expand All @@ -55,7 +55,13 @@ noasan struct DirectMap sys_mmap_metal(void *paddr, size_t size, int prot,
pte = __get_virtual(mm, pml4t, addr + i, true);
if (pte && page) {
__clear_page(BANE + page);
*pte = page | ((prot & PROT_WRITE) ? PAGE_RW : 0) | PAGE_U | PAGE_V;
page |= PAGE_RSRV | PAGE_U;
if ((prot & PROT_WRITE))
page |= PAGE_V | PAGE_RW;
else if ((prot & (PROT_READ | PROT_EXEC)))
page |= PAGE_V;
if (!(prot & PROT_EXEC)) page |= PAGE_XD;
*pte = page;
} else {
addr = -1;
break;
Expand Down
16 changes: 10 additions & 6 deletions libc/intrin/mman.greg.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ noasan textreal uint64_t *__get_virtual(struct mman *mm, uint64_t *t,
for (h = 39;; h -= 9) {
e = t + ((vaddr >> h) & 511);
if (h == 12) return e;
if (!(*e & PAGE_V)) {
if (!(*e & (PAGE_V | PAGE_RSRV))) {
if (!maketables) return NULL;
if (!(p = __new_page(mm))) return NULL;
__clear_page(BANE + p);
Expand Down Expand Up @@ -123,8 +123,8 @@ noasan textreal void __invert_memory_area(struct mman *mm, uint64_t *pml4t,
pe = ROUNDUP(pe, 4096);
for (p = ps; p != pe; p += 4096) {
m = __get_virtual(mm, pml4t, BANE + p, true);
if (m && !(*m & PAGE_V)) {
*m = p | PAGE_V | pte_flags;
if (m && !(*m & (PAGE_V | PAGE_RSRV))) {
*m = p | PAGE_V | PAGE_RSRV | pte_flags;
}
}
}
Expand All @@ -139,7 +139,7 @@ static noasan textreal void __invert_memory(struct mman *mm, uint64_t *pml4t) {
/* ape/ape.S has already mapped the first 2 MiB of physical memory. */
if (ps < 0x200000 && ps + size <= 0x200000)
continue;
__invert_memory_area(mm, pml4t, ps, size, PAGE_RW);
__invert_memory_area(mm, pml4t, ps, size, PAGE_RW | PAGE_XD);
}
}

Expand Down Expand Up @@ -187,8 +187,12 @@ noasan textreal void __map_phdrs(struct mman *mm, uint64_t *pml4t, uint64_t b) {
for (p = (struct Elf64_Phdr *)REAL(ape_phdrs), m = 0;
p < (struct Elf64_Phdr *)REAL(ape_phdrs_end); ++p) {
if (p->p_type == PT_LOAD || p->p_type == PT_GNU_STACK) {
f = PAGE_V | PAGE_U;
if (p->p_flags & PF_W) f |= PAGE_RW;
f = PAGE_RSRV | PAGE_U;
if (p->p_flags & PF_W)
f |= PAGE_V | PAGE_RW;
else if (p->p_flags & (PF_R | PF_X))
f |= PAGE_V;
if (!(p->p_flags & PF_X)) f |= PAGE_XD;
for (i = 0; i < p->p_memsz; i += 4096) {
if (i < p->p_filesz) {
v = b + p->p_offset + i;
Expand Down
2 changes: 1 addition & 1 deletion libc/runtime/efimain.greg.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ __msabi noasan EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
pdpt2 = (uint64_t *)0x7c000;
pml4t = (uint64_t *)0x7e000;
for (i = 0; i < 512; ++i) {
pd[i] = 0x1000 * i + PAGE_V + PAGE_RW;
pd[i] = 0x1000 * i + PAGE_V + PAGE_RSRV + PAGE_RW;
}
pdt1[0] = (intptr_t)pd + PAGE_V + PAGE_RW;
pdt2[0] = (intptr_t)pd + PAGE_V + PAGE_RW;
Expand Down
3 changes: 2 additions & 1 deletion libc/vga/vga-init.greg.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ void _vga_reinit(struct Tty *tty, unsigned short starty, unsigned short startx,
chr_ht = VGA_ASSUME_CHAR_HEIGHT_PX;
chr_wid = VGA_ASSUME_CHAR_WIDTH_PX;
/* Make sure the video buffer is mapped into virtual memory. */
__invert_memory_area(mm, __get_pml4t(), vid_buf_phy, vid_buf_sz, PAGE_RW);
__invert_memory_area(mm, __get_pml4t(), vid_buf_phy, vid_buf_sz,
PAGE_RW | PAGE_XD);
/*
* Initialize our tty structure from the current screen geometry, screen
* contents, cursor position, & character dimensions.
Expand Down

0 comments on commit d387006

Please sign in to comment.