From c445f53bdcbb4cac6a1daf4a009a88a241a25803 Mon Sep 17 00:00:00 2001 From: Iamhuskar Date: Sat, 7 Feb 2026 16:07:27 +0800 Subject: [PATCH 1/4] fix: copy_file skip files larger than 1GB (Fixes #4039) --- CMakeLists.txt | 1 + src/TraceStream.cc | 23 ++- src/test/memfd_create_dotnet_huge_mapping.c | 146 ++++++++++++++++++++ 3 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 src/test/memfd_create_dotnet_huge_mapping.c diff --git a/CMakeLists.txt b/CMakeLists.txt index cec1abdf37d..2bf58d55a34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/TraceStream.cc b/src/TraceStream.cc index 04b34c8818e..6fecc5d4801 100644 --- a/src/TraceStream.cc +++ b/src/TraceStream.cc @@ -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 || diff --git a/src/test/memfd_create_dotnet_huge_mapping.c b/src/test/memfd_create_dotnet_huge_mapping.c new file mode 100644 index 00000000000..4b832abdcc8 --- /dev/null +++ b/src/test/memfd_create_dotnet_huge_mapping.c @@ -0,0 +1,146 @@ +#include "util.h" +#include +/* +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +*/ +#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; +} From c84125b078933f5702987d551e3ca1401d9310cb Mon Sep 17 00:00:00 2001 From: Iamhuskar Date: Tue, 10 Feb 2026 17:45:11 +0800 Subject: [PATCH 2/4] fix:Get fd size from args;Simplify test case; #4093 --- src/TraceStream.cc | 16 +-- src/test/memfd_create_dotnet_huge_mapping.c | 111 +++----------------- 2 files changed, 18 insertions(+), 109 deletions(-) diff --git a/src/TraceStream.cc b/src/TraceStream.cc index 6fecc5d4801..0524a5f25d7 100644 --- a/src/TraceStream.cc +++ b/src/TraceStream.cc @@ -998,17 +998,11 @@ TraceWriter::RecordInTrace TraceWriter::write_mapped_region( 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(); + string backing_file_name; + constexpr off_t ONE_GB = 1024 * 1024 * 1024; + if ((stat.st_size <= ONE_GB) && copy_file(km.fsname(), file_name, &backing_file_name)) { + src.initFile().setBackingFileName(str_to_data(backing_file_name)); + return; } } src.setTrace(); diff --git a/src/test/memfd_create_dotnet_huge_mapping.c b/src/test/memfd_create_dotnet_huge_mapping.c index 4b832abdcc8..be72480beec 100644 --- a/src/test/memfd_create_dotnet_huge_mapping.c +++ b/src/test/memfd_create_dotnet_huge_mapping.c @@ -1,34 +1,6 @@ #include "util.h" #include -/* -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -*/ #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 @@ -36,19 +8,14 @@ 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 20GB limit is used instead. */ -// 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) +unsigned long long MaxDoubleMappedSize = 20 * _1GB; +#define PAGE_SIZE_4K 4096 +int test_ftruncate_huge_mapping(void) { - *writable_addr = MAP_FAILED; - *executable_addr = MAP_FAILED; - *fd_ptr = -1; + int ret=-1; int fd = memfd_create("double_mapper_test", MFD_CLOEXEC); do { @@ -62,65 +29,19 @@ int create_double_mapping(void **writable_addr, void **executable_addr, 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) + void* executable_addr = mmap(NULL, PAGE_SIZE_4K, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); + if (executable_addr == MAP_FAILED) { atomic_puts("mmap for executable mapping failed"); - munmap(*writable_addr, size); break; } - *fd_ptr = fd; - return 0; + munmap(executable_addr, PAGE_SIZE_4K); + ret = 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); - } + return ret; } int main(void) @@ -129,17 +50,11 @@ int main(void) 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_ftruncate_huge_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); + //check timeout + test_assert((end.tv_sec - start.tv_sec) <= 2); #endif atomic_puts("EXIT-SUCCESS"); return 0; From 6ddef3669cd557b1c240f5b942f1ed446056ec71 Mon Sep 17 00:00:00 2001 From: Iamhuskar Date: Tue, 10 Feb 2026 18:12:55 +0800 Subject: [PATCH 3/4] refactor(copy_file_or_trace): simplify condition logic for readability --- src/TraceStream.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/TraceStream.cc b/src/TraceStream.cc index 0524a5f25d7..45adbd80220 100644 --- a/src/TraceStream.cc +++ b/src/TraceStream.cc @@ -995,17 +995,17 @@ 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... - 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 - string backing_file_name; - constexpr off_t ONE_GB = 1024 * 1024 * 1024; - if ((stat.st_size <= ONE_GB) && copy_file(km.fsname(), file_name, &backing_file_name)) { - src.initFile().setBackingFileName(str_to_data(backing_file_name)); - return; - } + // copy files when the mapping is PROT_EXEC, unless the file is too big + // sometimes km.size() does not equal the file size from fstat(). + string backing_file_name; + if ((km.prot() & PROT_EXEC) && + (!(km.flags() & MAP_SHARED) && + (stat.st_size <= 1024 * 1024 * 1024/*1GB*/) && + copy_file(km.fsname(), file_name, &backing_file_name))) { + src.initFile().setBackingFileName(str_to_data(backing_file_name)); + }else{ + src.setTrace(); } - src.setTrace(); }; if (origin == REMAP_MAPPING || origin == PATCH_MAPPING || From 2517dfff0ee37d458e4b59493609348c83fd8514 Mon Sep 17 00:00:00 2001 From: Iamhuskar Date: Tue, 10 Feb 2026 18:23:44 +0800 Subject: [PATCH 4/4] style: fix indentation --- src/TraceStream.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TraceStream.cc b/src/TraceStream.cc index 45adbd80220..e462197429a 100644 --- a/src/TraceStream.cc +++ b/src/TraceStream.cc @@ -1003,7 +1003,7 @@ TraceWriter::RecordInTrace TraceWriter::write_mapped_region( (stat.st_size <= 1024 * 1024 * 1024/*1GB*/) && copy_file(km.fsname(), file_name, &backing_file_name))) { src.initFile().setBackingFileName(str_to_data(backing_file_name)); - }else{ + } else { src.setTrace(); } };