Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,7 @@ set(BASIC_TESTS
memfd_create_efault
memfd_create_shared
memfd_create_shared_huge
memfd_create_dotnet_huge_mapping
mincore
mknod
mlock
Expand Down
15 changes: 10 additions & 5 deletions src/ExtraRegisters.cc
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,7 @@ static void compare_regs(const ExtraRegisters& reg1,

void ExtraRegisters::compare_internal(const ExtraRegisters& reg2,
Registers::Comparison& result) const {
const GdbServerRegister NOT_PRESENT = GdbServerRegister(0);
if (arch() != reg2.arch()) {
FATAL() << "Can't compare register files with different archs";
}
Expand All @@ -922,19 +923,23 @@ void ExtraRegisters::compare_internal(const ExtraRegisters& reg2,
if (format() != reg2.format()) {
FATAL() << "Can't compare register files with different formats";
}
bool avx_present = false;
if (format() == XSAVE) {
avx_present = !!(xsave_native_layout().supported_feature_bits & (1 << AVX_FEATURE_BIT));
}

switch (arch()) {
case x86:
compare_regs(*this, reg2, DREG_ST0, GdbServerRegister(0), 8, "st", result);
compare_regs(*this, reg2, DREG_XMM0, DREG_YMM0H, 8, "ymm", result);
compare_regs(*this, reg2, DREG_ST0, NOT_PRESENT, 8, "st", result);
compare_regs(*this, reg2, DREG_XMM0, avx_present ? DREG_YMM0H : NOT_PRESENT, 8, "ymm", result);
break;
case x86_64:
compare_regs(*this, reg2, DREG_64_ST0, GdbServerRegister(0), 8, "st", result);
compare_regs(*this, reg2, DREG_64_XMM0, DREG_64_YMM0H, 8, "ymm", result);
compare_regs(*this, reg2, DREG_64_ST0, NOT_PRESENT, 8, "st", result);
compare_regs(*this, reg2, DREG_64_XMM0, avx_present ? DREG_64_YMM0H : NOT_PRESENT, 8, "ymm", result);
break;
case aarch64:
DEBUG_ASSERT(format_ == NT_FPR);
compare_regs(*this, reg2, DREG_V0, GdbServerRegister(0), 32, "v", result);
compare_regs(*this, reg2, DREG_V0, NOT_PRESENT, 32, "v", result);
break;
default:
DEBUG_ASSERT(0 && "Unknown arch");
Expand Down
23 changes: 16 additions & 7 deletions src/TraceStream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -995,14 +995,23 @@ TraceWriter::RecordInTrace TraceWriter::write_mapped_region(
// debuggers can't find the file, but the Linux loader doesn't create
// shared mappings so situations where a shared-mapped executable contains
// usable debug info should be very rare at best...
string backing_file_name;
if ((km.prot() & PROT_EXEC) &&
copy_file(km.fsname(), file_name, &backing_file_name) &&
!(km.flags() & MAP_SHARED)) {
src.initFile().setBackingFileName(str_to_data(backing_file_name));
} else {
src.setTrace();
if ((km.prot() & PROT_EXEC) && (!(km.flags() & MAP_SHARED))) {
// copy files when the mapping is PROT_EXEC, unless the file is too big
// the size of km is not equal to the file size obtained through the fstat
struct stat src_stat;
ScopedFd src_fd(file_name.c_str(), O_RDONLY);
if (src_fd.is_open() && (fstat(src_fd.get(), &src_stat) == 0)) {
string backing_file_name;
constexpr off_t ONE_GB = 1024 * 1024 * 1024;
if ((src_stat.st_size <= ONE_GB) && copy_file(km.fsname(), file_name, &backing_file_name)) {
src.initFile().setBackingFileName(str_to_data(backing_file_name));
return;
}
} else {
LOG(debug) << "fstat failed for " << km.fsname();
}
}
src.setTrace();
};

if (origin == REMAP_MAPPING || origin == PATCH_MAPPING ||
Expand Down
146 changes: 146 additions & 0 deletions src/test/memfd_create_dotnet_huge_mapping.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#include "util.h"
#include <time.h>
/*
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/memfd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
*/
#define memfd_create(...) syscall(__NR_memfd_create, __VA_ARGS__)

typedef void (*simple_func_t)(void);

#ifdef __x86_64__
static const unsigned char x86_ret_instruction = 0xC3; // ret
static const unsigned long long func_size = 1;
#elif defined(__aarch64__)
static const unsigned int arm64_ret_instruction = 0xD65F03C0; // ret
static const unsigned long long func_size = 4;
#elif defined(__i386__)
#else
#error "Unsupported architecture"
#endif

/*
https://github.com/dotnet/runtime/blob/23aeecc9f91a9ae0a211702dbd849c90cdd81d36/src/coreclr/minipal/Unix/doublemapping.cpp#L85
#ifdef TARGET_64BIT
static const off_t MaxDoubleMappedSize = 2048ULL*1024*1024*1024;
#else
static const off_t MaxDoubleMappedSize = UINT_MAX;
#endif

*/
// To prevent bugs that could result in writing a 2TB file, a 10GB limit is used
// instead.
#define _1GB (1024 * 1024 * 1024ULL)
unsigned long long MaxDoubleMappedSize = 10 * _1GB;

int create_double_mapping(void **writable_addr, void **executable_addr,
unsigned long long size, int *fd_ptr)
{
*writable_addr = MAP_FAILED;
*executable_addr = MAP_FAILED;
*fd_ptr = -1;
int fd = memfd_create("double_mapper_test", MFD_CLOEXEC);
do
{
if (fd == -1)
{
atomic_puts("memfd_create failed");
break;
}
if (ftruncate(fd, MaxDoubleMappedSize) == -1)
{
atomic_puts("ftruncate failed");
break;
}
*writable_addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (*writable_addr == MAP_FAILED)
{
atomic_puts("mmap for writable mapping failed");
break;
}
*executable_addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
if (*executable_addr == MAP_FAILED)
{
atomic_puts("mmap for executable mapping failed");
munmap(*writable_addr, size);
break;
}
*fd_ptr = fd;
return 0;
} while (0);
if(fd!=-1){
close(fd);
}
return -1;
}

void free_double_mapping(void *writable_addr, void *executable_addr,
unsigned long long size)
{
if (writable_addr != MAP_FAILED && writable_addr != NULL)
{
munmap(writable_addr, size);
}
if (executable_addr != MAP_FAILED && executable_addr != NULL)
{
munmap(executable_addr, size);
}
}

void test_double_mapping(void)
{
void *writable_addr = MAP_FAILED;
void *executable_addr = MAP_FAILED;
unsigned long long page_size = getpagesize();
int memfd = -1;
if (create_double_mapping(&writable_addr, &executable_addr, page_size,
&memfd) != 0)
{
return;
}
atomic_puts("Writing and Executing function to writable page...\n");
#ifdef __x86_64__
memcpy(writable_addr, &x86_ret_instruction, func_size);
#elif defined(__aarch64__)
memcpy(writable_addr, &arm64_ret_instruction, func_size);
#endif
simple_func_t func = (simple_func_t)executable_addr;
func();
free_double_mapping(writable_addr, executable_addr, page_size);
if (memfd != -1)
{
close(memfd);
}
}

int main(void)
{
#if defined(__i386__)
atomic_puts("Skipping test on 32 bit");
#else
struct timespec start, end;
long elapsed_seconds = 0;
test_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &start));
test_double_mapping();
test_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &end));
elapsed_seconds = end.tv_sec - start.tv_sec;
//10GB/sec;
long limited_sec = 2;
atomic_printf("elapsed_seconds %ld : limited_sec %ld \n", elapsed_seconds,
limited_sec);
/*check timeout*/
test_assert(elapsed_seconds <= limited_sec);
#endif
atomic_puts("EXIT-SUCCESS");
return 0;
}