2024-06-21 03:46:42 +00:00
|
|
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
|
|
│ │
|
|
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
|
|
│ │
|
|
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
|
|
#include "libc/calls/struct/sigset.internal.h"
|
|
|
|
#include "libc/calls/syscall-sysv.internal.h"
|
|
|
|
#include "libc/dce.h"
|
|
|
|
#include "libc/intrin/describeflags.internal.h"
|
|
|
|
#include "libc/intrin/directmap.internal.h"
|
|
|
|
#include "libc/intrin/dll.h"
|
|
|
|
#include "libc/intrin/kprintf.h"
|
|
|
|
#include "libc/intrin/maps.h"
|
|
|
|
#include "libc/intrin/strace.internal.h"
|
|
|
|
#include "libc/nt/memory.h"
|
|
|
|
#include "libc/runtime/internal.h"
|
|
|
|
#include "libc/runtime/runtime.h"
|
|
|
|
#include "libc/stdio/sysparam.h"
|
|
|
|
#include "libc/sysv/consts/auxv.h"
|
|
|
|
#include "libc/sysv/consts/prot.h"
|
|
|
|
#include "libc/sysv/errfuns.h"
|
|
|
|
|
|
|
|
#define PGUP(x) (((x) + pagesz - 1) & -pagesz)
|
|
|
|
|
|
|
|
static int __mprotect_chunk(char *addr, size_t size, int prot, bool iscow) {
|
|
|
|
|
|
|
|
if (!IsWindows())
|
|
|
|
return sys_mprotect(addr, size, prot);
|
|
|
|
|
|
|
|
uint32_t op;
|
|
|
|
if (!VirtualProtect(addr, size, __prot2nt(prot, iscow), &op))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __mprotect(char *addr, size_t size, int prot) {
|
|
|
|
|
|
|
|
// unix checks prot before checking size
|
|
|
|
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
|
|
return einval();
|
|
|
|
|
|
|
|
// make new technology consistent with unix
|
|
|
|
if (!size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// unix checks prot before checking size
|
|
|
|
int pagesz = getauxval(AT_PAGESZ);
|
|
|
|
if ((intptr_t)addr & (pagesz - 1))
|
|
|
|
return einval();
|
|
|
|
|
|
|
|
// change mappings
|
|
|
|
int rc = 0;
|
|
|
|
__maps_lock();
|
|
|
|
bool found = false;
|
|
|
|
struct Map *map = __maps.maps;
|
|
|
|
_Atomic(struct Map *) *prev = &__maps.maps;
|
|
|
|
while (map) {
|
|
|
|
char *map_addr = map->addr;
|
|
|
|
size_t map_size = map->size;
|
|
|
|
struct Map *next = map->next;
|
|
|
|
char *beg = MAX(addr, map_addr);
|
|
|
|
char *end = MIN(addr + PGUP(size), map_addr + PGUP(map_size));
|
|
|
|
if (beg < end) {
|
|
|
|
found = true;
|
|
|
|
if (addr <= map_addr && addr + PGUP(size) >= map_addr + PGUP(map_size)) {
|
|
|
|
// change protection of entire mapping
|
|
|
|
if (!__mprotect_chunk(map_addr, map_size, prot, map->iscow)) {
|
|
|
|
map->prot = prot;
|
|
|
|
} else {
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else if (IsWindows()) {
|
|
|
|
// windows does allow changing protection at 4096 byte chunks
|
|
|
|
// however we currently don't have data structures that track
|
|
|
|
// this within the 64 kb map granules that can't be broken up
|
|
|
|
if (__mprotect_chunk(beg, end - beg, prot, map->iscow) == -1)
|
|
|
|
rc = -1;
|
|
|
|
} else if (addr <= map_addr) {
|
|
|
|
// cleave lefthand side of mapping
|
2024-06-24 13:53:49 +00:00
|
|
|
size_t left = PGUP(addr + size - map_addr);
|
|
|
|
size_t right = map_size - left;
|
2024-06-21 03:46:42 +00:00
|
|
|
struct Map *leftmap;
|
|
|
|
if ((leftmap = __maps_alloc())) {
|
|
|
|
if (!__mprotect_chunk(map_addr, left, prot, false)) {
|
|
|
|
leftmap->next = map;
|
|
|
|
leftmap->addr = map_addr;
|
|
|
|
leftmap->size = left;
|
|
|
|
leftmap->prot = prot;
|
|
|
|
leftmap->off = map->off;
|
|
|
|
leftmap->flags = map->flags;
|
|
|
|
map->addr += left;
|
|
|
|
map->size = right;
|
|
|
|
if (map->off != -1)
|
|
|
|
map->off += left;
|
|
|
|
dll_make_first(&__maps.used, &leftmap->elem);
|
|
|
|
*prev = leftmap;
|
2024-06-24 13:53:49 +00:00
|
|
|
__maps.count += 1;
|
|
|
|
__maps_check();
|
2024-06-21 03:46:42 +00:00
|
|
|
} else {
|
|
|
|
__maps_free(leftmap);
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else if (addr + PGUP(size) >= map_addr + PGUP(map_size)) {
|
|
|
|
// cleave righthand side of mapping
|
|
|
|
size_t left = addr - map_addr;
|
|
|
|
size_t right = map_addr + map_size - addr;
|
|
|
|
struct Map *leftmap;
|
|
|
|
if ((leftmap = __maps_alloc())) {
|
|
|
|
if (!__mprotect_chunk(map_addr + left, right, prot, false)) {
|
|
|
|
leftmap->next = map;
|
|
|
|
leftmap->addr = map_addr;
|
|
|
|
leftmap->size = left;
|
|
|
|
leftmap->off = map->off;
|
|
|
|
leftmap->prot = map->prot;
|
|
|
|
leftmap->flags = map->flags;
|
|
|
|
map->addr += left;
|
|
|
|
map->size = right;
|
|
|
|
map->prot = prot;
|
|
|
|
if (map->off != -1)
|
|
|
|
map->off += left;
|
|
|
|
dll_make_first(&__maps.used, &leftmap->elem);
|
|
|
|
*prev = leftmap;
|
2024-06-24 13:53:49 +00:00
|
|
|
__maps.count += 1;
|
|
|
|
__maps_check();
|
2024-06-21 03:46:42 +00:00
|
|
|
} else {
|
|
|
|
__maps_free(leftmap);
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// punch hole in mapping
|
|
|
|
size_t left = addr - map_addr;
|
|
|
|
size_t middle = PGUP(size);
|
|
|
|
size_t right = map_size - middle - left;
|
|
|
|
struct Map *leftmap;
|
|
|
|
if ((leftmap = __maps_alloc())) {
|
|
|
|
struct Map *midlmap;
|
|
|
|
if ((midlmap = __maps_alloc())) {
|
|
|
|
if (!__mprotect_chunk(map_addr + left, middle, prot, false)) {
|
|
|
|
leftmap->next = midlmap;
|
|
|
|
leftmap->addr = map_addr;
|
|
|
|
leftmap->size = left;
|
|
|
|
leftmap->off = map->off;
|
|
|
|
leftmap->prot = map->prot;
|
|
|
|
leftmap->flags = map->flags;
|
|
|
|
midlmap->next = map;
|
|
|
|
midlmap->addr = map_addr + left;
|
|
|
|
midlmap->size = middle;
|
|
|
|
midlmap->off = map->off == -1 ? -1 : map->off + left;
|
|
|
|
midlmap->prot = prot;
|
|
|
|
midlmap->flags = map->flags;
|
|
|
|
map->addr += left + middle;
|
|
|
|
map->size = right;
|
|
|
|
if (map->off != -1)
|
|
|
|
map->off += left + middle;
|
|
|
|
dll_make_first(&__maps.used, &leftmap->elem);
|
|
|
|
dll_make_first(&__maps.used, &midlmap->elem);
|
|
|
|
*prev = leftmap;
|
2024-06-24 13:53:49 +00:00
|
|
|
__maps.count += 2;
|
|
|
|
__maps_check();
|
2024-06-21 03:46:42 +00:00
|
|
|
} else {
|
|
|
|
__maps_free(midlmap);
|
|
|
|
__maps_free(leftmap);
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
__maps_free(leftmap);
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prev = &map->next;
|
|
|
|
map = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// allow user to change mappings unknown to cosmo runtime
|
|
|
|
if (!found)
|
|
|
|
rc = __mprotect_chunk(addr, size, prot, false);
|
|
|
|
|
|
|
|
__maps_unlock();
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modifies restrictions on virtual memory address range.
|
|
|
|
*
|
|
|
|
* @param addr needs to be 4kb aligned
|
|
|
|
* @param prot can have PROT_{NONE,READ,WRITE,EXEC}
|
|
|
|
* @return 0 on success, or -1 w/ errno
|
|
|
|
* @raise ENOMEM on tracking memory oom
|
|
|
|
* @see mmap()
|
|
|
|
*/
|
|
|
|
int mprotect(void *addr, size_t size, int prot) {
|
|
|
|
int rc;
|
|
|
|
rc = __mprotect(addr, size, prot);
|
|
|
|
STRACE("mprotect(%p, %'zu, %s) → %d% m", addr, size, DescribeProtFlags(prot),
|
|
|
|
rc);
|
|
|
|
return rc;
|
|
|
|
}
|