diff --git a/tests/general/mem_fd_helper.hpp b/tests/general/mem_fd_helper.hpp index eb50ae1..9bd27f1 100644 --- a/tests/general/mem_fd_helper.hpp +++ b/tests/general/mem_fd_helper.hpp @@ -1,17 +1,39 @@ #pragma once -// Linux-only helper: creates an anonymous in-memory file via memfd_create. +// Cross-platform helper for creating binary test "files" without writing to disk where possible. +// +// Strategy: +// - Linux (non-Android, or Android API >= 30): memfd_create → /proc/self/fd/ (no disk I/O) +// - All other platforms: anonymous temp file via std::tmpfile(), accessed via /proc/self/fd/ +// on Linux, or a named temp file (cleaned up on destruction) elsewhere. +// // Usage: -// MemFdFile f = MemFdFile::create(data.data(), data.size()); -// // use f.path() as a std::filesystem::path -// // fd is automatically closed on destruction +// auto f = MemFdFile::create(myVector); +// ASSERT_TRUE(f.valid()); +// scanner.scan_for_pattern_in_file(f.path(), ...); + #include #include #include -#include +#include +#include #include #include -#include -#include + +#if defined(__linux__) +# include +# include +# if defined(__ANDROID__) +# if __ANDROID_API__ >= 30 +# include +# define OMATH_TEST_USE_MEMFD 1 +# endif +// Android < 30: fall through to tmpfile() path below +# else +// Desktop Linux: memfd_create available since glibc 2.27 / kernel 3.17 +# include +# define OMATH_TEST_USE_MEMFD 1 +# endif +#endif class MemFdFile { @@ -20,61 +42,115 @@ public: ~MemFdFile() { +#if defined(OMATH_TEST_USE_MEMFD) if (m_fd >= 0) ::close(m_fd); +#else + if (!m_temp_path.empty()) + std::filesystem::remove(m_temp_path); +#endif } MemFdFile(const MemFdFile&) = delete; MemFdFile& operator=(const MemFdFile&) = delete; - MemFdFile(MemFdFile&& o) noexcept : m_fd(o.m_fd), m_path(std::move(o.m_path)) + MemFdFile(MemFdFile&& o) noexcept + : m_path(std::move(o.m_path)) +#if defined(OMATH_TEST_USE_MEMFD) + , m_fd(o.m_fd) +#else + , m_temp_path(std::move(o.m_temp_path)) +#endif { +#if defined(OMATH_TEST_USE_MEMFD) o.m_fd = -1; +#else + o.m_temp_path.clear(); +#endif } - [[nodiscard]] bool valid() const { return m_fd >= 0; } + [[nodiscard]] bool valid() const { return !m_path.empty(); } - [[nodiscard]] std::filesystem::path path() const { return m_path; } - - static MemFdFile create(const std::uint8_t* data, std::size_t size) - { - MemFdFile f; - f.m_fd = static_cast(::memfd_create("test_bin", 0)); - if (f.m_fd < 0) - return f; - - f.m_path = std::format("/proc/self/fd/{}", f.m_fd); - - const auto* ptr = reinterpret_cast(data); - std::size_t written = 0; - while (written < size) - { - const auto n = ::write(f.m_fd, ptr + written, size - written); - if (n <= 0) - { - ::close(f.m_fd); - f.m_fd = -1; - return f; - } - written += static_cast(n); - } - return f; - } + [[nodiscard]] const std::filesystem::path& path() const { return m_path; } static MemFdFile create(const std::vector& data) { return create(data.data(), data.size()); } + static MemFdFile create(const std::uint8_t* data, std::size_t size) + { + MemFdFile f; + +#if defined(OMATH_TEST_USE_MEMFD) + f.m_fd = static_cast(::memfd_create("test_bin", 0)); + if (f.m_fd < 0) + return f; + + if (!write_all(f.m_fd, data, size)) + { + ::close(f.m_fd); + f.m_fd = -1; + return f; + } + f.m_path = "/proc/self/fd/" + std::to_string(f.m_fd); + +#else + // Portable fallback: write to a uniquely-named temp file and delete on destruction + const auto tmp_dir = std::filesystem::temp_directory_path(); + std::mt19937_64 rng(std::random_device{}()); + const auto unique_name = "omath_test_" + std::to_string(rng()) + ".bin"; + f.m_temp_path = (tmp_dir / unique_name).string(); + f.m_path = f.m_temp_path; + + std::ofstream out(f.m_temp_path, std::ios::binary | std::ios::trunc); + if (!out.is_open()) + { + f.m_temp_path.clear(); + f.m_path.clear(); + return f; + } + out.write(reinterpret_cast(data), static_cast(size)); + if (!out) + { + out.close(); + std::filesystem::remove(f.m_temp_path); + f.m_temp_path.clear(); + f.m_path.clear(); + } +#endif + return f; + } + private: + std::filesystem::path m_path; + +#if defined(OMATH_TEST_USE_MEMFD) int m_fd = -1; - std::string m_path; + + static bool write_all(int fd, const std::uint8_t* data, std::size_t size) + { + std::size_t written = 0; + while (written < size) + { + const auto n = ::write(fd, data + written, size - written); + if (n <= 0) + return false; + written += static_cast(n); + } + return true; + } +#else + std::string m_temp_path; +#endif }; +// --------------------------------------------------------------------------- // Build a minimal PE binary in-memory with a single .text section. // Layout (all offsets compile-time): // 0x00: DOS header (64 B) 0x40: pad 0x80: NT sig 0x84: FileHeader (20 B) // 0x98: OptionalHeader (0xF0 B) 0x188: SectionHeader (44 B) 0x1B4: section data +// --------------------------------------------------------------------------- inline std::vector build_minimal_pe(const std::vector& section_bytes) { constexpr std::uint32_t e_lfanew = 0x80u; @@ -103,8 +179,8 @@ inline std::vector build_minimal_pe(const std::vector(section_bytes.size()); - const std::uint32_t vaddr = 0x1000u; + const auto vsize = static_cast(section_bytes.size()); + const std::uint32_t vaddr = 0x1000u; const auto ptr_raw = static_cast(data_off); std::memcpy(buf.data() + sh_off + 8, &vsize, 4); std::memcpy(buf.data() + sh_off + 12, &vaddr, 4);