blob: cebabc3eb08968e874c1c061d169324c74ac593d [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/fuzzer/cpp/fidl.h>
#include <zircon/status.h>
#include <memory>
#include "src/sys/fuzzing/common/artifact.h"
#include "src/sys/fuzzing/common/async-socket.h"
#include "src/sys/fuzzing/common/async-types.h"
#include "src/sys/fuzzing/common/component-context.h"
#include "src/sys/fuzzing/common/controller-provider.h"
#include "src/sys/fuzzing/common/testing/async-test.h"
#include "src/sys/fuzzing/common/testing/process.h"
#include "src/sys/fuzzing/common/testing/registrar.h"
namespace fuzzing {
using fuchsia::fuzzer::ControllerProviderPtr;
using fuchsia::fuzzer::ControllerPtr;
using fuchsia::fuzzer::Registrar;
// Test fixtures.
// The |FrameworkIntegrationTest| fakes the registrar but uses the real framework/engine.
//
// TODO(fxbug.dev/71912): This could be converted to use RealmBuilder, at which point specific tests
// could provide individual components for the target adapter capability to be routed to. This would
// facilitate writing tests for the engine under specific scenarios, analogous to libFuzzer's tests
// under https://github.com/llvm/llvm-project/tree/main/compiler-rt/test/fuzzer.
class FrameworkIntegrationTest : public AsyncTest {
protected:
void SetUp() override {
AsyncTest::SetUp();
context_ = ComponentContext::CreateWithExecutor(executor());
}
// Creates fake registry and coverage components, and spawns the engine.
ZxPromise<ControllerPtr> Start() {
registrar_ = std::make_unique<FakeRegistrar>(executor());
return fpromise::make_promise([this]() -> ZxResult<> {
std::vector<zx::channel> channels;
fidl::InterfaceHandle<Registrar> registrar_handle = registrar_->NewBinding();
channels.emplace_back(registrar_handle.TakeChannel());
if (auto status =
StartProcess("component_fuzzing_engine", std::move(channels), &engine_);
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to start engine process: " << zx_status_get_string(status);
return fpromise::error(status);
}
return fpromise::ok();
})
.and_then(registrar_->TakeProvider())
.and_then([this, consumer_fut = Future<>(), controller = ControllerPtr()](
Context& context,
ControllerProviderHandle& handle) mutable -> ZxResult<ControllerPtr> {
// Connect a controller to the spawned process.
if (!consumer_fut) {
auto request = controller.NewRequest(executor()->dispatcher());
Bridge<> bridge;
provider_ = handle.Bind();
provider_->Connect(std::move(request), bridge.completer.bind());
consumer_fut = bridge.consumer.promise();
}
if (!consumer_fut(context)) {
return fpromise::pending();
}
return fpromise::ok(std::move(controller));
})
.wrap_with(scope_);
}
ZxPromise<> Stop() {
provider_->Stop();
return AwaitTermination(std::move(engine_), executor());
}
void TearDown() override {
engine_.kill();
AsyncTest::TearDown();
}
private:
std::unique_ptr<ComponentContext> context_;
zx::process engine_;
ControllerProviderPtr provider_;
std::unique_ptr<FakeRegistrar> registrar_;
Scope scope_;
};
// Integration tests.
TEST_F(FrameworkIntegrationTest, Crash) {
ControllerPtr controller;
FUZZING_EXPECT_OK(Start(), &controller);
RunUntilIdle();
Input input("FUZZ");
ZxBridge<FuzzResult> bridge1;
controller->Execute(AsyncSocketWrite(executor(), input.Duplicate()),
ZxBind<FuzzResult>(std::move(bridge1.completer)));
FUZZING_EXPECT_OK(bridge1.consumer.promise(), FuzzResult::CRASH);
Bridge<Status> bridge2;
controller->GetStatus(bridge2.completer.bind());
Status status;
FUZZING_EXPECT_OK(bridge2.consumer.promise(), &status);
RunUntilIdle();
EXPECT_TRUE(status.has_elapsed());
ZxBridge<FidlArtifact> bridge3;
controller->GetResults([completer = std::move(bridge3.completer)](FuzzResult fuzz_result,
FidlInput fidl_input) mutable {
completer.complete_ok(MakeFidlArtifact(fuzz_result, std::move(fidl_input)));
});
auto task = bridge3.consumer.promise().and_then([this](FidlArtifact& fidl_artifact) {
return AsyncSocketRead(executor(), std::move(fidl_artifact));
});
Artifact artifact;
FUZZING_EXPECT_OK(task, &artifact);
RunUntilIdle();
EXPECT_EQ(artifact.fuzz_result(), FuzzResult::CRASH);
EXPECT_EQ(artifact.input(), input);
}
} // namespace fuzzing