143 lines
3.4 KiB
C
143 lines
3.4 KiB
C
#include "sentry_unix_pageallocator.h"
|
|
#include "sentry_core.h"
|
|
#include "sentry_unix_spinlock.h"
|
|
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
|
|
// some macos versions do not have this yet
|
|
#if !defined(MAP_ANONYMOUS) && defined(SENTRY_PLATFORM_DARWIN)
|
|
# define MAP_ANONYMOUS MAP_ANON
|
|
#endif
|
|
|
|
#define ALIGN 8
|
|
|
|
struct page_header;
|
|
struct page_header {
|
|
struct page_header *next;
|
|
size_t num_pages;
|
|
};
|
|
|
|
struct page_allocator_s {
|
|
size_t page_size;
|
|
struct page_header *last_page;
|
|
char *current_page;
|
|
size_t page_offset;
|
|
size_t pages_allocated;
|
|
};
|
|
|
|
static struct page_allocator_s g_page_allocator_backing = { 0 };
|
|
static struct page_allocator_s *g_alloc = NULL;
|
|
static sentry_spinlock_t g_lock = SENTRY__SPINLOCK_INIT;
|
|
|
|
bool
|
|
sentry__page_allocator_enabled(void)
|
|
{
|
|
return !!g_alloc;
|
|
}
|
|
|
|
void
|
|
sentry__page_allocator_enable(void)
|
|
{
|
|
sentry__spinlock_lock(&g_lock);
|
|
if (!g_alloc) {
|
|
g_alloc = &g_page_allocator_backing;
|
|
g_alloc->page_size = getpagesize();
|
|
g_alloc->last_page = NULL;
|
|
g_alloc->current_page = NULL;
|
|
g_alloc->page_offset = 0;
|
|
g_alloc->pages_allocated = 0;
|
|
}
|
|
sentry__spinlock_unlock(&g_lock);
|
|
}
|
|
|
|
static void *
|
|
get_pages(size_t num_pages)
|
|
{
|
|
void *rv = mmap(NULL, g_alloc->page_size * num_pages,
|
|
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
if (rv == MAP_FAILED) {
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(__has_feature)
|
|
# if __has_feature(memory_sanitizer)
|
|
__msan_unpoison(rv, g_alloc->page_size * num_pages);
|
|
# endif
|
|
#endif
|
|
|
|
struct page_header *header = (struct page_header *)rv;
|
|
header->next = g_alloc->last_page;
|
|
header->num_pages = num_pages;
|
|
g_alloc->last_page = header;
|
|
|
|
g_alloc->pages_allocated += num_pages;
|
|
|
|
return rv;
|
|
}
|
|
|
|
void *
|
|
sentry__page_allocator_alloc(size_t size)
|
|
{
|
|
if (!size) {
|
|
return NULL;
|
|
}
|
|
|
|
// make sure the requested size is correctly aligned
|
|
size_t diff = size % ALIGN;
|
|
size = size + ALIGN - diff;
|
|
|
|
char *rv = NULL;
|
|
|
|
sentry__spinlock_lock(&g_lock);
|
|
|
|
// current page is large enough
|
|
if (g_alloc->current_page
|
|
&& g_alloc->page_size - g_alloc->page_offset >= size) {
|
|
rv = g_alloc->current_page + g_alloc->page_offset;
|
|
g_alloc->page_offset += size;
|
|
if (g_alloc->page_offset == g_alloc->page_size) {
|
|
g_alloc->page_offset = 0;
|
|
g_alloc->current_page = NULL;
|
|
}
|
|
} else {
|
|
// allocate new pages
|
|
|
|
size_t requested_size = size + sizeof(struct page_header);
|
|
size_t pages
|
|
= (requested_size + g_alloc->page_size - 1) / g_alloc->page_size;
|
|
size_t actual_size = g_alloc->page_size * pages;
|
|
|
|
rv = get_pages(pages);
|
|
|
|
if (rv) {
|
|
size_t diff = actual_size - requested_size;
|
|
g_alloc->page_offset
|
|
= (g_alloc->page_size - diff) % g_alloc->page_size;
|
|
g_alloc->current_page = g_alloc->page_offset
|
|
? rv + g_alloc->page_size * (pages - 1)
|
|
: NULL;
|
|
rv += sizeof(struct page_header);
|
|
}
|
|
}
|
|
|
|
sentry__spinlock_unlock(&g_lock);
|
|
return rv;
|
|
}
|
|
|
|
#ifdef SENTRY_UNITTEST
|
|
void
|
|
sentry__page_allocator_disable(void)
|
|
{
|
|
if (!g_alloc) {
|
|
return;
|
|
}
|
|
struct page_header *next;
|
|
for (struct page_header *cur = g_alloc->last_page; cur; cur = next) {
|
|
next = cur->next;
|
|
munmap(cur, cur->num_pages * g_alloc->page_size);
|
|
}
|
|
g_alloc = NULL;
|
|
}
|
|
#endif
|