mirror of https://github.com/grpc/grpc.git
[fuzzer] Add a fuzzer for SubChannelConnector instances (#37397)
Includes a few changes to pollset stuff to make it easier to not use pollsets (which I think is going to be generally helpful in the coming months)
Closes #37397
COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/37397 from ctiller:client-chicken 1099cd500a
PiperOrigin-RevId: 660014128
pull/37358/head
parent
b95e8dd3b9
commit
3de09c544d
13 changed files with 538 additions and 15 deletions
@ -0,0 +1,189 @@ |
||||
// Copyright 2024 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "test/core/end2end/fuzzers/connector_fuzzer.h" |
||||
|
||||
#include "src/core/lib/address_utils/parse_address.h" |
||||
#include "src/core/lib/event_engine/channel_args_endpoint_config.h" |
||||
#include "src/core/lib/event_engine/default_event_engine.h" |
||||
#include "src/core/lib/event_engine/tcp_socket_utils.h" |
||||
#include "src/core/lib/gprpp/env.h" |
||||
#include "src/core/lib/iomgr/executor.h" |
||||
#include "src/core/lib/iomgr/timer_manager.h" |
||||
#include "test/core/end2end/fuzzers/fuzzer_input.pb.h" |
||||
#include "test/core/end2end/fuzzers/network_input.h" |
||||
#include "test/core/test_util/fuzz_config_vars.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
|
||||
bool squelch = true; |
||||
bool leak_check = true; |
||||
|
||||
using ::grpc_event_engine::experimental::ChannelArgsEndpointConfig; |
||||
using ::grpc_event_engine::experimental::EventEngine; |
||||
using ::grpc_event_engine::experimental::FuzzingEventEngine; |
||||
using ::grpc_event_engine::experimental::GetDefaultEventEngine; |
||||
using ::grpc_event_engine::experimental::MockEndpointController; |
||||
using ::grpc_event_engine::experimental::SetEventEngineFactory; |
||||
using ::grpc_event_engine::experimental::URIToResolvedAddress; |
||||
|
||||
namespace grpc_core { |
||||
namespace { |
||||
|
||||
class ConnectorFuzzer { |
||||
public: |
||||
ConnectorFuzzer( |
||||
const fuzzer_input::Msg& msg, |
||||
absl::FunctionRef<RefCountedPtr<grpc_channel_security_connector>()> |
||||
make_security_connector, |
||||
absl::FunctionRef<OrphanablePtr<SubchannelConnector>()> make_connector) |
||||
: make_security_connector_(make_security_connector), |
||||
engine_([actions = msg.event_engine_actions()]() { |
||||
SetEventEngineFactory([actions]() -> std::unique_ptr<EventEngine> { |
||||
return std::make_unique<FuzzingEventEngine>( |
||||
FuzzingEventEngine::Options(), actions); |
||||
}); |
||||
return std::dynamic_pointer_cast<FuzzingEventEngine>( |
||||
GetDefaultEventEngine()); |
||||
}()), |
||||
mock_endpoint_controller_(MockEndpointController::Create(engine_)), |
||||
connector_(make_connector()) { |
||||
CHECK(engine_); |
||||
for (const auto& input : msg.network_input()) { |
||||
network_inputs_.push(input); |
||||
} |
||||
grpc_timer_manager_set_start_threaded(false); |
||||
grpc_init(); |
||||
ExecCtx exec_ctx; |
||||
Executor::SetThreadingAll(false); |
||||
listener_ = |
||||
engine_ |
||||
->CreateListener( |
||||
[this](std::unique_ptr<EventEngine::Endpoint> endpoint, |
||||
MemoryAllocator) { |
||||
if (network_inputs_.empty()) return; |
||||
ScheduleWrites(network_inputs_.front(), std::move(endpoint), |
||||
engine_.get()); |
||||
network_inputs_.pop(); |
||||
}, |
||||
[](absl::Status) {}, ChannelArgsEndpointConfig(ChannelArgs{}), |
||||
std::make_unique<MemoryQuota>("foo")) |
||||
.value(); |
||||
if (msg.has_shutdown_connector() && |
||||
msg.shutdown_connector().delay_ms() > 0) { |
||||
auto shutdown_connector = msg.shutdown_connector(); |
||||
const auto delay = Duration::Milliseconds(shutdown_connector.delay_ms()); |
||||
engine_->RunAfterExactly(delay, [this, shutdown_connector = std::move( |
||||
shutdown_connector)]() { |
||||
if (connector_ == nullptr) return; |
||||
connector_->Shutdown(absl::Status( |
||||
static_cast<absl::StatusCode>(shutdown_connector.shutdown_status()), |
||||
shutdown_connector.shutdown_message())); |
||||
}); |
||||
} |
||||
// Abbreviated runtime for interpreting API actions, since we simply don't
|
||||
// support many here.
|
||||
uint64_t when_ms = 0; |
||||
for (const auto& action : msg.api_actions()) { |
||||
switch (action.type_case()) { |
||||
default: |
||||
break; |
||||
case api_fuzzer::Action::kSleepMs: |
||||
when_ms += action.sleep_ms(); |
||||
break; |
||||
case api_fuzzer::Action::kResizeResourceQuota: |
||||
engine_->RunAfterExactly( |
||||
Duration::Milliseconds(when_ms), |
||||
[this, new_size = action.resize_resource_quota()]() { |
||||
resource_quota_->memory_quota()->SetSize(new_size); |
||||
}); |
||||
when_ms += 1; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
~ConnectorFuzzer() { |
||||
listener_.reset(); |
||||
connector_.reset(); |
||||
mock_endpoint_controller_.reset(); |
||||
engine_->TickUntilIdle(); |
||||
grpc_shutdown_blocking(); |
||||
engine_->UnsetGlobalHooks(); |
||||
} |
||||
|
||||
void Run() { |
||||
grpc_resolved_address addr; |
||||
CHECK(grpc_parse_uri(URI::Parse("ipv4:127.0.0.1:1234").value(), &addr)); |
||||
CHECK_OK( |
||||
listener_->Bind(URIToResolvedAddress("ipv4:127.0.0.1:1234").value())); |
||||
CHECK_OK(listener_->Start()); |
||||
OrphanablePtr<grpc_endpoint> endpoint( |
||||
mock_endpoint_controller_->TakeCEndpoint()); |
||||
SubchannelConnector::Result result; |
||||
bool done = false; |
||||
auto channel_args = ChannelArgs{}.SetObject<EventEngine>(engine_).SetObject( |
||||
resource_quota_); |
||||
auto security_connector = make_security_connector_(); |
||||
if (security_connector != nullptr) { |
||||
channel_args = channel_args.SetObject(std::move(security_connector)); |
||||
} |
||||
connector_->Connect( |
||||
SubchannelConnector::Args{&addr, nullptr, |
||||
Timestamp::Now() + Duration::Seconds(20), |
||||
channel_args}, |
||||
&result, NewClosure([&done, &result](grpc_error_handle status) { |
||||
done = true; |
||||
if (status.ok()) result.transport->Orphan(); |
||||
})); |
||||
|
||||
while (!done) { |
||||
engine_->Tick(); |
||||
grpc_timer_manager_tick(); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
RefCountedPtr<ResourceQuota> resource_quota_ = |
||||
MakeRefCounted<ResourceQuota>("fuzzer"); |
||||
absl::FunctionRef<RefCountedPtr<grpc_channel_security_connector>()> |
||||
make_security_connector_; |
||||
std::shared_ptr<FuzzingEventEngine> engine_; |
||||
std::queue<fuzzer_input::NetworkInput> network_inputs_; |
||||
std::shared_ptr<MockEndpointController> mock_endpoint_controller_; |
||||
std::unique_ptr<EventEngine::Listener> listener_; |
||||
OrphanablePtr<SubchannelConnector> connector_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
void RunConnectorFuzzer( |
||||
const fuzzer_input::Msg& msg, |
||||
absl::FunctionRef<RefCountedPtr<grpc_channel_security_connector>()> |
||||
make_security_connector, |
||||
absl::FunctionRef<OrphanablePtr<SubchannelConnector>()> make_connector) { |
||||
if (squelch && !GetEnv("GRPC_TRACE_FUZZER").has_value()) { |
||||
grpc_disable_all_absl_logs(); |
||||
} |
||||
static const int once = []() { |
||||
ForceEnableExperiment("event_engine_client", true); |
||||
ForceEnableExperiment("event_engine_listener", true); |
||||
return 42; |
||||
}(); |
||||
CHECK_EQ(once, 42); // avoid unused variable warning
|
||||
ApplyFuzzConfigVars(msg.config_vars()); |
||||
TestOnlyReloadExperimentsFromConfigVariables(); |
||||
ConnectorFuzzer(msg, make_security_connector, make_connector).Run(); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,34 @@ |
||||
// Copyright 2024 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_TEST_CORE_END2END_FUZZERS_CONNECTOR_FUZZER_H |
||||
#define GRPC_TEST_CORE_END2END_FUZZERS_CONNECTOR_FUZZER_H |
||||
|
||||
#include "absl/functional/function_ref.h" |
||||
|
||||
#include "src/core/client_channel/connector.h" |
||||
#include "src/core/lib/security/security_connector/security_connector.h" |
||||
#include "test/core/end2end/fuzzers/fuzzer_input.pb.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
void RunConnectorFuzzer( |
||||
const fuzzer_input::Msg& msg, |
||||
absl::FunctionRef<RefCountedPtr<grpc_channel_security_connector>()> |
||||
make_security_connector, |
||||
absl::FunctionRef<OrphanablePtr<SubchannelConnector>()> make_connector); |
||||
|
||||
} |
||||
|
||||
#endif // GRPC_TEST_CORE_END2END_FUZZERS_CONNECTOR_FUZZER_H
|
@ -0,0 +1,30 @@ |
||||
// Copyright 2024 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/credentials.h> |
||||
#include <grpc/grpc.h> |
||||
#include <grpc/grpc_security.h> |
||||
|
||||
#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" |
||||
#include "src/libfuzzer/libfuzzer_macro.h" |
||||
#include "test/core/end2end/fuzzers/connector_fuzzer.h" |
||||
|
||||
DEFINE_PROTO_FUZZER(const fuzzer_input::Msg& msg) { |
||||
grpc_core::RunConnectorFuzzer( |
||||
msg, |
||||
[]() { |
||||
return grpc_core::RefCountedPtr<grpc_channel_security_connector>(); |
||||
}, |
||||
[]() { return grpc_core::MakeOrphanable<grpc_core::Chttp2Connector>(); }); |
||||
} |
@ -0,0 +1,36 @@ |
||||
// Copyright 2024 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/credentials.h> |
||||
#include <grpc/grpc.h> |
||||
#include <grpc/grpc_security.h> |
||||
|
||||
#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" |
||||
#include "src/core/lib/security/credentials/credentials.h" |
||||
#include "src/core/lib/security/credentials/fake/fake_credentials.h" |
||||
#include "src/core/lib/security/security_connector/fake/fake_security_connector.h" |
||||
#include "src/libfuzzer/libfuzzer_macro.h" |
||||
#include "test/core/end2end/fuzzers/connector_fuzzer.h" |
||||
|
||||
DEFINE_PROTO_FUZZER(const fuzzer_input::Msg& msg) { |
||||
grpc_core::RunConnectorFuzzer( |
||||
msg, |
||||
[]() { |
||||
return grpc_fake_channel_security_connector_create( |
||||
grpc_core::RefCountedPtr<grpc_channel_credentials>( |
||||
grpc_fake_transport_security_credentials_create()), |
||||
nullptr, "foobar", grpc_core::ChannelArgs{}); |
||||
}, |
||||
[]() { return grpc_core::MakeOrphanable<grpc_core::Chttp2Connector>(); }); |
||||
} |
@ -0,0 +1 @@ |
||||
|
Loading…
Reference in new issue