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.h"
|
|
|
|
#include "libc/intrin/directmap.h"
|
|
|
|
#include "libc/intrin/dll.h"
|
|
|
|
#include "libc/intrin/maps.h"
|
|
|
|
#include "libc/intrin/strace.h"
|
2024-07-06 06:13:20 +00:00
|
|
|
#include "libc/intrin/tree.h"
|
2024-06-21 03:46:42 +00:00
|
|
|
#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"
|
2024-06-29 12:10:15 +00:00
|
|
|
#include "libc/sysv/consts/map.h"
|
2024-06-21 03:46:42 +00:00
|
|
|
#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
|
2024-07-06 06:13:20 +00:00
|
|
|
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_GUARD))
|
2024-06-21 03:46:42 +00:00
|
|
|
return einval();
|
|
|
|
|
|
|
|
// make new technology consistent with unix
|
|
|
|
if (!size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// unix checks prot before checking size
|
2024-07-19 04:02:59 +00:00
|
|
|
int pagesz = __pagesize;
|
2024-07-04 17:52:16 +00:00
|
|
|
if (((intptr_t)addr & (pagesz - 1)) || (uintptr_t)addr + size < size)
|
2024-06-21 03:46:42 +00:00
|
|
|
return einval();
|
|
|
|
|
2024-07-04 17:52:16 +00:00
|
|
|
// normalize size
|
|
|
|
size = (size + pagesz - 1) & -pagesz;
|
|
|
|
|
2024-12-29 01:08:18 +00:00
|
|
|
// test for signal handler reentry
|
2025-01-02 06:25:22 +00:00
|
|
|
if (__maps_reentrant())
|
2024-12-29 01:08:18 +00:00
|
|
|
return edeadlk();
|
|
|
|
|
2024-06-21 03:46:42 +00:00
|
|
|
// change mappings
|
|
|
|
int rc = 0;
|
|
|
|
bool found = false;
|
2024-12-29 01:08:18 +00:00
|
|
|
__maps_lock();
|
2025-01-02 06:25:22 +00:00
|
|
|
struct Map *map;
|
|
|
|
if (!(map = __maps_floor(addr)))
|
|
|
|
map = __maps_first();
|
|
|
|
for (; map && map->addr <= addr + size; map = __maps_next(map)) {
|
2024-06-21 03:46:42 +00:00
|
|
|
char *map_addr = map->addr;
|
|
|
|
size_t map_size = map->size;
|
|
|
|
char *beg = MAX(addr, map_addr);
|
2024-07-04 17:52:16 +00:00
|
|
|
char *end = MIN(addr + size, map_addr + PGUP(map_size));
|
2024-07-06 06:13:20 +00:00
|
|
|
if (beg >= end)
|
2024-07-07 19:24:25 +00:00
|
|
|
continue;
|
2024-07-06 06:13:20 +00:00
|
|
|
found = true;
|
|
|
|
if (addr <= map_addr && addr + size >= map_addr + PGUP(map_size)) {
|
2025-01-02 06:25:22 +00:00
|
|
|
// change protection status of pages
|
2024-07-06 06:13:20 +00:00
|
|
|
if (!__mprotect_chunk(map_addr, map_size, prot, map->iscow)) {
|
|
|
|
map->prot = prot;
|
|
|
|
} else {
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else if (addr <= map_addr) {
|
|
|
|
// change lefthand side of mapping
|
2024-07-24 08:05:00 +00:00
|
|
|
size_t left = addr + size - map_addr;
|
2024-07-06 06:13:20 +00:00
|
|
|
size_t right = map_size - left;
|
|
|
|
struct Map *leftmap;
|
|
|
|
if ((leftmap = __maps_alloc())) {
|
|
|
|
if (!__mprotect_chunk(map_addr, left, prot, false)) {
|
|
|
|
leftmap->addr = map_addr;
|
|
|
|
leftmap->size = left;
|
|
|
|
leftmap->prot = prot;
|
|
|
|
leftmap->off = map->off;
|
|
|
|
leftmap->flags = map->flags;
|
|
|
|
leftmap->iscow = map->iscow;
|
|
|
|
leftmap->readonlyfile = map->readonlyfile;
|
|
|
|
leftmap->hand = map->hand;
|
|
|
|
map->addr += left;
|
|
|
|
map->size = right;
|
2025-01-01 12:59:38 +00:00
|
|
|
map->hand = MAPS_SUBREGION;
|
2024-07-06 06:13:20 +00:00
|
|
|
if (!(map->flags & MAP_ANONYMOUS))
|
|
|
|
map->off += left;
|
|
|
|
tree_insert(&__maps.maps, &leftmap->tree, __maps_compare);
|
|
|
|
__maps.count += 1;
|
|
|
|
__maps_check();
|
2024-06-21 03:46:42 +00:00
|
|
|
} else {
|
2024-07-06 06:13:20 +00:00
|
|
|
__maps_free(leftmap);
|
2024-06-21 03:46:42 +00:00
|
|
|
rc = -1;
|
|
|
|
}
|
2024-07-06 06:13:20 +00:00
|
|
|
} else {
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else if (addr + size >= map_addr + PGUP(map_size)) {
|
|
|
|
// change 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->addr = map_addr;
|
|
|
|
leftmap->size = left;
|
|
|
|
leftmap->off = map->off;
|
|
|
|
leftmap->prot = map->prot;
|
|
|
|
leftmap->flags = map->flags;
|
|
|
|
leftmap->iscow = map->iscow;
|
|
|
|
leftmap->readonlyfile = map->readonlyfile;
|
|
|
|
leftmap->hand = map->hand;
|
|
|
|
map->addr += left;
|
|
|
|
map->size = right;
|
|
|
|
map->prot = prot;
|
2025-01-01 12:59:38 +00:00
|
|
|
map->hand = MAPS_SUBREGION;
|
2024-07-06 06:13:20 +00:00
|
|
|
if (!(map->flags & MAP_ANONYMOUS))
|
|
|
|
map->off += left;
|
|
|
|
tree_insert(&__maps.maps, &leftmap->tree, __maps_compare);
|
|
|
|
__maps.count += 1;
|
|
|
|
__maps_check();
|
2024-06-21 03:46:42 +00:00
|
|
|
} else {
|
2024-07-06 06:13:20 +00:00
|
|
|
__maps_free(leftmap);
|
2024-06-21 03:46:42 +00:00
|
|
|
rc = -1;
|
|
|
|
}
|
2024-07-06 06:13:20 +00:00
|
|
|
} else {
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// change middle of mapping
|
|
|
|
size_t left = addr - map_addr;
|
|
|
|
size_t middle = 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)) {
|
2024-06-21 03:46:42 +00:00
|
|
|
leftmap->addr = map_addr;
|
|
|
|
leftmap->size = left;
|
|
|
|
leftmap->off = map->off;
|
|
|
|
leftmap->prot = map->prot;
|
|
|
|
leftmap->flags = map->flags;
|
2024-07-04 17:52:16 +00:00
|
|
|
leftmap->iscow = map->iscow;
|
|
|
|
leftmap->readonlyfile = map->readonlyfile;
|
|
|
|
leftmap->hand = map->hand;
|
2024-07-06 06:13:20 +00:00
|
|
|
midlmap->addr = map_addr + left;
|
|
|
|
midlmap->size = middle;
|
|
|
|
midlmap->off = (map->flags & MAP_ANONYMOUS) ? 0 : map->off + left;
|
|
|
|
midlmap->prot = prot;
|
|
|
|
midlmap->flags = map->flags;
|
2025-01-01 12:59:38 +00:00
|
|
|
midlmap->hand = MAPS_SUBREGION;
|
2024-07-06 06:13:20 +00:00
|
|
|
map->addr += left + middle;
|
2024-06-21 03:46:42 +00:00
|
|
|
map->size = right;
|
2025-01-01 12:59:38 +00:00
|
|
|
map->hand = MAPS_SUBREGION;
|
2024-06-29 12:10:15 +00:00
|
|
|
if (!(map->flags & MAP_ANONYMOUS))
|
2024-07-06 06:13:20 +00:00
|
|
|
map->off += left + middle;
|
|
|
|
tree_insert(&__maps.maps, &leftmap->tree, __maps_compare);
|
|
|
|
tree_insert(&__maps.maps, &midlmap->tree, __maps_compare);
|
|
|
|
__maps.count += 2;
|
2024-06-24 13:53:49 +00:00
|
|
|
__maps_check();
|
2024-06-21 03:46:42 +00:00
|
|
|
} else {
|
2024-07-06 06:13:20 +00:00
|
|
|
__maps_free(midlmap);
|
2024-06-21 03:46:42 +00:00
|
|
|
__maps_free(leftmap);
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else {
|
2024-07-06 06:13:20 +00:00
|
|
|
__maps_free(leftmap);
|
2024-06-21 03:46:42 +00:00
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
} else {
|
2024-07-06 06:13:20 +00:00
|
|
|
rc = -1;
|
2024-06-21 03:46:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
*
|
2024-12-29 01:08:18 +00:00
|
|
|
* POSIX doesn't require mprotect() to be async signal safe. However you
|
|
|
|
* should be able to call this from a signal handler safely, if you know
|
|
|
|
* that your signal will never interrupt the cosmopolitan memory manager
|
|
|
|
* and the only way you can ensure that, is by blocking signals whenever
|
|
|
|
* you call mmap(), munmap(), mprotect(), etc.
|
|
|
|
*
|
|
|
|
* @param addr needs to be page size aligned
|
|
|
|
* @param size is rounded up to the page size
|
|
|
|
* @param prot can be PROT_NONE or a combination of PROT_READ,
|
|
|
|
* PROT_WRITE, and PROT_EXEC
|
2024-06-21 03:46:42 +00:00
|
|
|
* @return 0 on success, or -1 w/ errno
|
2024-12-29 01:08:18 +00:00
|
|
|
* @raise EINVAL if `size` is zero
|
2024-06-21 03:46:42 +00:00
|
|
|
* @raise ENOMEM on tracking memory oom
|
2024-12-29 01:08:18 +00:00
|
|
|
* @raise EDEADLK if called from signal handler interrupting mmap()
|
2024-06-21 03:46:42 +00:00
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|