// libFuzzer harness for Program::load_method() -> Method::init() // Targets the ExecutionPlan.delegates iteration in Method::init() specifically - // this loop runs BEFORE any kernel/backend resolution, so no kernel or backend // registration is required to reach it. #include #include #include #include #include #include #include #include #include #include #include using executorch::extension::BufferDataLoader; using executorch::runtime::Program; using executorch::runtime::Method; using executorch::runtime::MemoryAllocator; using executorch::runtime::HierarchicalAllocator; using executorch::runtime::MemoryManager; using executorch::runtime::Span; static bool g_initialized = false; extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, std::size_t size) { if (!g_initialized) { executorch::runtime::runtime_init(); g_initialized = true; } constexpr std::size_t kMaxInput = 8U * 1024U * 1024U; if (data == nullptr || size == 0 || size > kMaxInput) { return 0; } BufferDataLoader loader(data, size); auto program = Program::load(&loader, Program::Verification::InternalConsistency); if (!program.ok()) { return 0; } // Minimal allocators - large enough for a tiny ExecutionPlan with no real // tensors/kernels; we only need to reach Method::init()'s delegates loop. static std::vector method_pool(256 * 1024); static std::vector planned_pool(64 * 1024); MemoryAllocator method_allocator(method_pool.size(), method_pool.data()); static Span planned_span(planned_pool.data(), planned_pool.size()); HierarchicalAllocator planned_memory({&planned_span, 1}); MemoryManager mm(&method_allocator, &planned_memory, nullptr); auto& p = program.get(); auto n = p.num_methods(); for (size_t i = 0; i < n; ++i) { auto name = p.get_method_name(i); if (name.ok()) { // load_method() calls Method::init() internally. We expect this to // fail gracefully for most fuzzer inputs (missing kernels, etc) - // we're specifically hunting for a crash, not a successful load. auto method = p.load_method(name.get(), &mm); (void)method; } } return 0; }