diff --git a/src/io/elf_reader.cpp b/src/io/elf_reader.cpp index 9a74d270b..5023216b0 100644 --- a/src/io/elf_reader.cpp +++ b/src/io/elf_reader.cpp @@ -220,6 +220,23 @@ RawProgram* find_subprogram(std::vector& programs, const ELFIO::sect return nullptr; } +static std::uintmax_t get_data_size(std::istream& input_stream, const std::string& path) { + std::error_code ec; + const auto size = std::filesystem::file_size(path, ec); + if (!ec) { + return size; + } + // Fallback: compute size from the input stream (handles in-memory ELF data + // where path is not a real filesystem path, e.g. path="memory"). + input_stream.seekg(0, std::ios::end); + const auto end_pos = input_stream.tellg(); + if (end_pos < 0) { + throw MalformedElf("Cannot determine data size for " + path); + } + input_stream.seekg(0); + return end_pos; +} + ELFIO::elfio load_elf(std::istream& input_stream, const std::string& path) { ELFIO::elfio reader; if (!reader.load(input_stream)) { @@ -230,11 +247,7 @@ ELFIO::elfio load_elf(std::istream& input_stream, const std::string& path) { throw MalformedElf("Unsupported ELF machine in file " + path + ": expected EM_BPF"); } - std::error_code ec; - const std::uintmax_t file_size = std::filesystem::file_size(path, ec); - if (ec) { - throw MalformedElf("Cannot determine file size for " + path + ": " + ec.message()); - } + const std::uintmax_t data_size = get_data_size(input_stream, path); for (const auto& section : reader.sections) { if (!section || section->get_type() == ELFIO::SHT_NOBITS) { continue; @@ -242,7 +255,7 @@ ELFIO::elfio load_elf(std::istream& input_stream, const std::string& path) { const std::uintmax_t offset = section->get_offset(); const std::uintmax_t size = section->get_size(); - if (offset > file_size || size > file_size - offset) { + if (offset > data_size || size > data_size - offset) { throw MalformedElf("ELF section '" + section->get_name() + "' has out-of-bounds file range"); } } diff --git a/src/test/test_elf_loader.cpp b/src/test/test_elf_loader.cpp index 33c4ff836..a8baffaa6 100644 --- a/src/test/test_elf_loader.cpp +++ b/src/test/test_elf_loader.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include "config.hpp" #include "io/elf_loader.hpp" +#include "io/elf_reader.hpp" #include "platform.hpp" using namespace prevail; @@ -507,3 +509,20 @@ TEST_CASE("ELF loader ignores non-function .ksyms entries", "[elf]") { const auto& progs = elf.get_programs("socket", "ksym_test"); REQUIRE(progs.size() == 1); } + +// Regression test: read_elf(istream, path) must work when path is not a real file. +// The load_elf function uses file_size(path) for section-bounds validation, which +// fails for non-file paths like "memory". The fix falls back to stream size. +TEST_CASE("read_elf succeeds with istream and non-file path", "[elf]") { + thread_local_options = {}; + + // Read a valid ELF file into memory. + const auto bytes = read_file_bytes("ebpf-samples/build/twomaps.o"); + std::string data(reinterpret_cast(bytes.data()), bytes.size()); + std::istringstream stream(data); + + // Use a non-file path — this is how ebpf-for-windows loads ELF from memory. + ebpf_verifier_options_t options{}; + auto programs = read_elf(stream, "memory", ".text", "", options, &g_ebpf_platform_linux); + REQUIRE(!programs.empty()); +} diff --git a/test-data/jump.yaml b/test-data/jump.yaml index a371ce52b..88fb627ab 100644 --- a/test-data/jump.yaml +++ b/test-data/jump.yaml @@ -1121,3 +1121,47 @@ post: [] messages: - "0: Invalid type (r1.type == number)" +--- +# Regression tests for aa31b0ed: Assume with operands of different inferred types. +# Before the fix, the verifier hit assert(same_type(left, right)) which on Windows +# opened a WER dialog (blocking the process), causing fuzzer timeouts. +test-case: assume reg-vs-reg with different types (ctx == number) + +pre: ["r1.type=ctx", "r1.ctx_offset=0", "r2.type=number", "r2.svalue=0", "r2.uvalue=0"] + +code: + : | + assume r1 == r2 + +post: + - r1.type=ctx + - r1.ctx_offset=0 + - r1.svalue=0 + - r1.uvalue=0 + - r2.type=number + - r2.svalue=r1.svalue + - r2.uvalue=r1.uvalue +--- +test-case: assume reg-vs-reg with different types (packet != number) + +pre: ["r1.type=packet", "r1.packet_offset=0", "r2.type=number", "r2.svalue=5", "r2.uvalue=5"] + +code: + : | + assume r1 != r2 + +post: [] +messages: + - "0: Lower bound must be at least meta_offset (valid_access(r1.offset) for comparison/subtraction)" +--- +test-case: assume reg-vs-reg with different types (stack < number) + +pre: ["r1.type=stack", "r1.stack_offset=0", "r2.type=number", "r2.svalue=10", "r2.uvalue=10"] + +code: + : | + assume r1 < r2 + +post: [] +messages: + - "0: Lower bound must be at least r10.stack_offset - EBPF_SUBPROGRAM_STACK_SIZE (valid_access(r1.offset) for comparison/subtraction)"