tcg: Allocate a guard page after code_gen_buffer

This will catch any overflow of the buffer.

Add a native win32 alternative for alloc_code_gen_buffer;
remove the malloc alternative.

Signed-off-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
Richard Henderson 2015-09-19 12:03:15 -07:00
parent 8163b74938
commit f293709c6a
1 changed files with 121 additions and 93 deletions

View File

@ -312,31 +312,6 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t retaddr)
return false; return false;
} }
#ifdef _WIN32
static __attribute__((unused)) void map_exec(void *addr, long size)
{
DWORD old_protect;
VirtualProtect(addr, size,
PAGE_EXECUTE_READWRITE, &old_protect);
}
#else
static __attribute__((unused)) void map_exec(void *addr, long size)
{
unsigned long start, end, page_size;
page_size = getpagesize();
start = (unsigned long)addr;
start &= ~(page_size - 1);
end = (unsigned long)addr + size;
end += page_size - 1;
end &= ~(page_size - 1);
mprotect((void *)start, end - start,
PROT_READ | PROT_WRITE | PROT_EXEC);
}
#endif
void page_size_init(void) void page_size_init(void)
{ {
/* NOTE: we can always suppose that qemu_host_page_size >= /* NOTE: we can always suppose that qemu_host_page_size >=
@ -473,14 +448,6 @@ static inline PageDesc *page_find(tb_page_addr_t index)
#define USE_STATIC_CODE_GEN_BUFFER #define USE_STATIC_CODE_GEN_BUFFER
#endif #endif
/* ??? Should configure for this, not list operating systems here. */
#if (defined(__linux__) \
|| defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \
|| defined(__DragonFly__) || defined(__OpenBSD__) \
|| defined(__NetBSD__))
# define USE_MMAP
#endif
/* Minimum size of the code gen buffer. This number is randomly chosen, /* Minimum size of the code gen buffer. This number is randomly chosen,
but not so small that we can't have a fair number of TB's live. */ but not so small that we can't have a fair number of TB's live. */
#define MIN_CODE_GEN_BUFFER_SIZE (1024u * 1024) #define MIN_CODE_GEN_BUFFER_SIZE (1024u * 1024)
@ -568,22 +535,102 @@ static inline void *split_cross_256mb(void *buf1, size_t size1)
static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE] static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE]
__attribute__((aligned(CODE_GEN_ALIGN))); __attribute__((aligned(CODE_GEN_ALIGN)));
# ifdef _WIN32
static inline void do_protect(void *addr, long size, int prot)
{
DWORD old_protect;
VirtualProtect(addr, size, prot, &old_protect);
}
static inline void map_exec(void *addr, long size)
{
do_protect(addr, size, PAGE_EXECUTE_READWRITE);
}
static inline void map_none(void *addr, long size)
{
do_protect(addr, size, PAGE_NOACCESS);
}
# else
static inline void do_protect(void *addr, long size, int prot)
{
uintptr_t start, end;
start = (uintptr_t)addr;
start &= qemu_real_host_page_mask;
end = (uintptr_t)addr + size;
end = ROUND_UP(end, qemu_real_host_page_size);
mprotect((void *)start, end - start, prot);
}
static inline void map_exec(void *addr, long size)
{
do_protect(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC);
}
static inline void map_none(void *addr, long size)
{
do_protect(addr, size, PROT_NONE);
}
# endif /* WIN32 */
static inline void *alloc_code_gen_buffer(void) static inline void *alloc_code_gen_buffer(void)
{ {
void *buf = static_code_gen_buffer; void *buf = static_code_gen_buffer;
size_t full_size, size;
/* The size of the buffer, rounded down to end on a page boundary. */
full_size = (((uintptr_t)buf + sizeof(static_code_gen_buffer))
& qemu_real_host_page_mask) - (uintptr_t)buf;
/* Reserve a guard page. */
size = full_size - qemu_real_host_page_size;
/* Honor a command-line option limiting the size of the buffer. */
if (size > tcg_ctx.code_gen_buffer_size) {
size = (((uintptr_t)buf + tcg_ctx.code_gen_buffer_size)
& qemu_real_host_page_mask) - (uintptr_t)buf;
}
tcg_ctx.code_gen_buffer_size = size;
#ifdef __mips__ #ifdef __mips__
if (cross_256mb(buf, tcg_ctx.code_gen_buffer_size)) { if (cross_256mb(buf, size)) {
buf = split_cross_256mb(buf, tcg_ctx.code_gen_buffer_size); buf = split_cross_256mb(buf, size);
size = tcg_ctx.code_gen_buffer_size;
} }
#endif #endif
map_exec(buf, tcg_ctx.code_gen_buffer_size);
map_exec(buf, size);
map_none(buf + size, qemu_real_host_page_size);
qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE);
return buf; return buf;
} }
#elif defined(USE_MMAP) #elif defined(_WIN32)
static inline void *alloc_code_gen_buffer(void)
{
size_t size = tcg_ctx.code_gen_buffer_size;
void *buf1, *buf2;
/* Perform the allocation in two steps, so that the guard page
is reserved but uncommitted. */
buf1 = VirtualAlloc(NULL, size + qemu_real_host_page_size,
MEM_RESERVE, PAGE_NOACCESS);
if (buf1 != NULL) {
buf2 = VirtualAlloc(buf1, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
assert(buf1 == buf2);
}
return buf1;
}
#else
static inline void *alloc_code_gen_buffer(void) static inline void *alloc_code_gen_buffer(void)
{ {
int flags = MAP_PRIVATE | MAP_ANONYMOUS; int flags = MAP_PRIVATE | MAP_ANONYMOUS;
uintptr_t start = 0; uintptr_t start = 0;
size_t size = tcg_ctx.code_gen_buffer_size;
void *buf; void *buf;
/* Constrain the position of the buffer based on the host cpu. /* Constrain the position of the buffer based on the host cpu.
@ -599,86 +646,70 @@ static inline void *alloc_code_gen_buffer(void)
Leave the choice of exact location with the kernel. */ Leave the choice of exact location with the kernel. */
flags |= MAP_32BIT; flags |= MAP_32BIT;
/* Cannot expect to map more than 800MB in low memory. */ /* Cannot expect to map more than 800MB in low memory. */
if (tcg_ctx.code_gen_buffer_size > 800u * 1024 * 1024) { if (size > 800u * 1024 * 1024) {
tcg_ctx.code_gen_buffer_size = 800u * 1024 * 1024; tcg_ctx.code_gen_buffer_size = size = 800u * 1024 * 1024;
} }
# elif defined(__sparc__) # elif defined(__sparc__)
start = 0x40000000ul; start = 0x40000000ul;
# elif defined(__s390x__) # elif defined(__s390x__)
start = 0x90000000ul; start = 0x90000000ul;
# elif defined(__mips__) # elif defined(__mips__)
/* ??? We ought to more explicitly manage layout for softmmu too. */ # if _MIPS_SIM == _ABI64
# ifdef CONFIG_USER_ONLY
start = 0x68000000ul;
# elif _MIPS_SIM == _ABI64
start = 0x128000000ul; start = 0x128000000ul;
# else # else
start = 0x08000000ul; start = 0x08000000ul;
# endif # endif
# endif # endif
buf = mmap((void *)start, tcg_ctx.code_gen_buffer_size, buf = mmap((void *)start, size + qemu_real_host_page_size,
PROT_WRITE | PROT_READ | PROT_EXEC, flags, -1, 0); PROT_NONE, flags, -1, 0);
if (buf == MAP_FAILED) { if (buf == MAP_FAILED) {
return NULL; return NULL;
} }
#ifdef __mips__ #ifdef __mips__
if (cross_256mb(buf, tcg_ctx.code_gen_buffer_size)) { if (cross_256mb(buf, size)) {
/* Try again, with the original still mapped, to avoid re-acquiring /* Try again, with the original still mapped, to avoid re-acquiring
that 256mb crossing. This time don't specify an address. */ that 256mb crossing. This time don't specify an address. */
size_t size2, size1 = tcg_ctx.code_gen_buffer_size; size_t size2;
void *buf2 = mmap(NULL, size1, PROT_WRITE | PROT_READ | PROT_EXEC, void *buf2 = mmap(NULL, size + qemu_real_host_page_size,
flags, -1, 0); PROT_NONE, flags, -1, 0);
if (buf2 != MAP_FAILED) { switch (buf2 != MAP_FAILED) {
if (!cross_256mb(buf2, size1)) { case 1:
if (!cross_256mb(buf2, size)) {
/* Success! Use the new buffer. */ /* Success! Use the new buffer. */
munmap(buf, size1); munmap(buf, size);
return buf2; break;
} }
/* Failure. Work with what we had. */ /* Failure. Work with what we had. */
munmap(buf2, size1); munmap(buf2, size);
} /* fallthru */
default:
/* Split the original buffer. Free the smaller half. */ /* Split the original buffer. Free the smaller half. */
buf2 = split_cross_256mb(buf, size1); buf2 = split_cross_256mb(buf, size);
size2 = tcg_ctx.code_gen_buffer_size; size2 = tcg_ctx.code_gen_buffer_size;
munmap(buf + (buf == buf2 ? size2 : 0), size1 - size2); if (buf == buf2) {
return buf2; munmap(buf + size2 + qemu_real_host_page_size, size - size2);
}
#endif
return buf;
}
#else
static inline void *alloc_code_gen_buffer(void)
{
void *buf = g_try_malloc(tcg_ctx.code_gen_buffer_size);
if (buf == NULL) {
return NULL;
}
#ifdef __mips__
if (cross_256mb(buf, tcg_ctx.code_gen_buffer_size)) {
void *buf2 = g_malloc(tcg_ctx.code_gen_buffer_size);
if (buf2 != NULL && !cross_256mb(buf2, size1)) {
/* Success! Use the new buffer. */
free(buf);
buf = buf2;
} else { } else {
/* Failure. Work with what we had. Since this is malloc munmap(buf, size - size2);
and not mmap, we can't free the other half. */
free(buf2);
buf = split_cross_256mb(buf, tcg_ctx.code_gen_buffer_size);
} }
size = size2;
break;
}
buf = buf2;
} }
#endif #endif
map_exec(buf, tcg_ctx.code_gen_buffer_size); /* Make the final buffer accessible. The guard page at the end
will remain inaccessible with PROT_NONE. */
mprotect(buf, size, PROT_WRITE | PROT_READ | PROT_EXEC);
/* Request large pages for the buffer. */
qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE);
return buf; return buf;
} }
#endif /* USE_STATIC_CODE_GEN_BUFFER, USE_MMAP */ #endif /* USE_STATIC_CODE_GEN_BUFFER, WIN32, POSIX */
static inline void code_gen_alloc(size_t tb_size) static inline void code_gen_alloc(size_t tb_size)
{ {
@ -689,9 +720,6 @@ static inline void code_gen_alloc(size_t tb_size)
exit(1); exit(1);
} }
qemu_madvise(tcg_ctx.code_gen_buffer, tcg_ctx.code_gen_buffer_size,
QEMU_MADV_HUGEPAGE);
/* Estimate a good size for the number of TBs we can support. We /* Estimate a good size for the number of TBs we can support. We
still haven't deducted the prologue from the buffer size here, still haven't deducted the prologue from the buffer size here,
but that's minimal and won't affect the estimate much. */ but that's minimal and won't affect the estimate much. */
@ -708,8 +736,8 @@ static inline void code_gen_alloc(size_t tb_size)
void tcg_exec_init(unsigned long tb_size) void tcg_exec_init(unsigned long tb_size)
{ {
cpu_gen_init(); cpu_gen_init();
code_gen_alloc(tb_size);
page_init(); page_init();
code_gen_alloc(tb_size);
#if defined(CONFIG_SOFTMMU) #if defined(CONFIG_SOFTMMU)
/* There's no guest base to take into account, so go ahead and /* There's no guest base to take into account, so go ahead and
initialize the prologue now. */ initialize the prologue now. */