diff --git a/src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc b/src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc index 1add6e7636a..660d94a7e5b 100644 --- a/src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc +++ b/src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc @@ -117,7 +117,7 @@ void TransportStreamReceiverImpl::RegisterRecvTrailingMetadata( void TransportStreamReceiverImpl::NotifyRecvInitialMetadata( StreamIdentifier id, absl::StatusOr initial_metadata) { gpr_log(GPR_INFO, "%s id = %d is_client = %d", __func__, id, is_client_); - if (!is_client_ && accept_stream_callback_) { + if (!is_client_ && accept_stream_callback_ && initial_metadata.ok()) { accept_stream_callback_(); } InitialMetadataCallbackType cb; diff --git a/test/core/transport/binder/end2end/fuzzers/BUILD b/test/core/transport/binder/end2end/fuzzers/BUILD index 91d5fd16f91..249464615e3 100644 --- a/test/core/transport/binder/end2end/fuzzers/BUILD +++ b/test/core/transport/binder/end2end/fuzzers/BUILD @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("//bazel:grpc_build_system.bzl", "grpc_package") +load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_package") load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer") grpc_package( @@ -24,6 +24,25 @@ grpc_package( licenses(["notice"]) +grpc_cc_library( + name = "fuzzer_utils", + srcs = ["fuzzer_utils.cc"], + hdrs = ["fuzzer_utils.h"], + external_deps = [ + "absl/memory", + "absl/status", + ], + tags = [ + "no_mac", + "no_windows", + ], + deps = [ + "//:gpr", + "//src/core/ext/transport/binder/wire_format:binder", + "//src/core/ext/transport/binder/wire_format:wire_reader", + ], +) + grpc_fuzzer( name = "client_fuzzer", srcs = ["client_fuzzer.cc"], @@ -34,14 +53,34 @@ grpc_fuzzer( "no_windows", ], deps = [ + ":fuzzer_utils", + "//:gpr", + "//:gpr_base", + "//:grpc++_base", + "//:grpc_base", + "//:grpc_base_c", + "//src/core/ext/transport/binder/transport:binder_transport", + "//test/core/util:grpc_test_util", + ], +) + +grpc_fuzzer( + name = "server_fuzzer", + srcs = ["server_fuzzer.cc"], + corpus = "server_fuzzer_corpus", + language = "C++", + tags = [ + "no_mac", + "no_windows", + ], + deps = [ + ":fuzzer_utils", "//:gpr", "//:gpr_base", "//:grpc++_base", "//:grpc_base", "//:grpc_base_c", "//src/core/ext/transport/binder/transport:binder_transport", - "//src/core/ext/transport/binder/wire_format:binder", - "//src/core/ext/transport/binder/wire_format:wire_reader", "//test/core/util:grpc_test_util", ], ) diff --git a/test/core/transport/binder/end2end/fuzzers/client_fuzzer.cc b/test/core/transport/binder/end2end/fuzzers/client_fuzzer.cc index 5e788a5e93c..40a6d2c2276 100644 --- a/test/core/transport/binder/end2end/fuzzers/client_fuzzer.cc +++ b/test/core/transport/binder/end2end/fuzzers/client_fuzzer.cc @@ -12,228 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include - #include #include #include "absl/memory/memory.h" +#include + #include "src/core/ext/transport/binder/transport/binder_transport.h" -#include "src/core/ext/transport/binder/wire_format/binder.h" -#include "src/core/ext/transport/binder/wire_format/wire_reader.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/surface/channel.h" +#include "test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h" bool squelch = true; bool leak_check = true; -namespace { - -// A WritableParcel implementation that simply does nothing. Don't use -// MockWritableParcel here since capturing calls is expensive. -class NoOpWritableParcel : public grpc_binder::WritableParcel { - public: - int32_t GetDataSize() const override { return 0; } - absl::Status WriteInt32(int32_t /*data*/) override { - return absl::OkStatus(); - } - absl::Status WriteInt64(int64_t /*data*/) override { - return absl::OkStatus(); - } - absl::Status WriteBinder(grpc_binder::HasRawBinder* /*binder*/) override { - return absl::OkStatus(); - } - absl::Status WriteString(absl::string_view /*s*/) override { - return absl::OkStatus(); - } - absl::Status WriteByteArray(const int8_t* /*buffer*/, - int32_t /*length*/) override { - return absl::OkStatus(); - } -}; - -// Binder implementation used in fuzzing. -// -// Most of its the functionalities are no-op, except ConstructTxReceiver now -// returns a TransactionReceiverForFuzzing. -class BinderForFuzzing : public grpc_binder::Binder { - public: - BinderForFuzzing() : input_(absl::make_unique()) {} - - BinderForFuzzing(const uint8_t* data, size_t size) - : data_(data), - size_(size), - input_(absl::make_unique()) {} - - void Initialize() override {} - absl::Status PrepareTransaction() override { return absl::OkStatus(); } - - absl::Status Transact( - grpc_binder::BinderTransportTxCode /*tx_code*/) override { - return absl::OkStatus(); - } - - std::unique_ptr ConstructTxReceiver( - grpc_core::RefCountedPtr wire_reader_ref, - grpc_binder::TransactionReceiver::OnTransactCb cb) const override; - - grpc_binder::WritableParcel* GetWritableParcel() const override { - return input_.get(); - } - void* GetRawBinder() override { return nullptr; } - - private: - const uint8_t* data_; - size_t size_; - std::unique_ptr input_; -}; - -// ReadableParcel implementation used in fuzzing. -// -// It consumes a FuzzedDataProvider, and returns fuzzed data upon user's -// requests. -class ReadableParcelForFuzzing : public grpc_binder::ReadableParcel { - public: - ReadableParcelForFuzzing(FuzzedDataProvider* data_provider, - bool is_setup_transport) - : data_provider_(data_provider), - is_setup_transport_(is_setup_transport), - consumed_data_size_(0) {} - - int32_t GetDataSize() const override { - return data_provider_->ConsumeIntegral(); - } - absl::Status ReadInt32(int32_t* data) override { - if (consumed_data_size_ >= kParcelDataSizeLimit) { - return absl::InternalError("Parcel size limit exceeds"); - } - if (!is_setup_transport_ && data_provider_->ConsumeBool()) { - return absl::InternalError("error"); - } - *data = data_provider_->ConsumeIntegral(); - consumed_data_size_ += sizeof(int32_t); - return absl::OkStatus(); - } - absl::Status ReadInt64(int64_t* data) override { - if (consumed_data_size_ >= kParcelDataSizeLimit) { - return absl::InternalError("Parcel size limit exceeds"); - } - if (!is_setup_transport_ && data_provider_->ConsumeBool()) { - return absl::InternalError("error"); - } - *data = data_provider_->ConsumeIntegral(); - consumed_data_size_ += sizeof(int64_t); - return absl::OkStatus(); - } - absl::Status ReadBinder( - std::unique_ptr* binder) override { - if (consumed_data_size_ >= kParcelDataSizeLimit) { - return absl::InternalError("Parcel size limit exceeds"); - } - if (!is_setup_transport_ && data_provider_->ConsumeBool()) { - return absl::InternalError("error"); - } - *binder = absl::make_unique(); - consumed_data_size_ += sizeof(void*); - return absl::OkStatus(); - } - absl::Status ReadByteArray(std::string* data) override { - if (consumed_data_size_ >= kParcelDataSizeLimit) { - return absl::InternalError("Parcel size limit exceeds"); - } - if (!is_setup_transport_ && data_provider_->ConsumeBool()) { - return absl::InternalError("error"); - } - *data = data_provider_->ConsumeRandomLengthString(100); - consumed_data_size_ += data->size(); - return absl::OkStatus(); - } - absl::Status ReadString(std::string* data) override { - if (consumed_data_size_ >= kParcelDataSizeLimit) { - return absl::InternalError("Parcel size limit exceeds"); - } - if (!is_setup_transport_ && data_provider_->ConsumeBool()) { - return absl::InternalError("error"); - } - *data = data_provider_->ConsumeRandomLengthString(100); - consumed_data_size_ += data->size(); - return absl::OkStatus(); - } - - private: - FuzzedDataProvider* data_provider_; - // Whether this parcel contains a SETUP_TRANSPORT request. If it is, we will - // avoid returning errors in the Read* functions so that the fuzzer will not - // be blocked waiting for the correct request. - bool is_setup_transport_; +static void* tag(intptr_t t) { return reinterpret_cast(t); } - static constexpr size_t kParcelDataSizeLimit = 1024 * 1024; - size_t consumed_data_size_; -}; - -std::thread* g_fuzzing_thread = nullptr; - -void FuzzingLoop( - const uint8_t* data, size_t size, - grpc_core::RefCountedPtr wire_reader_ref, - grpc_binder::TransactionReceiver::OnTransactCb callback) { - FuzzedDataProvider data_provider(data, size); - { - // Send SETUP_TRANSPORT request. - std::unique_ptr parcel = - absl::make_unique( - &data_provider, - /*is_setup_transport=*/true); - callback(static_cast( - grpc_binder::BinderTransportTxCode::SETUP_TRANSPORT), - parcel.get(), /*uid=*/0) - .IgnoreError(); - } - while (data_provider.remaining_bytes() > 0) { - transaction_code_t tx_code = - data_provider.ConsumeIntegralInRange( - 0, LAST_CALL_TRANSACTION); - std::unique_ptr parcel = - absl::make_unique( - &data_provider, - /*is_setup_transport=*/false); - callback(tx_code, parcel.get(), /*uid=*/0).IgnoreError(); - } - wire_reader_ref = nullptr; -} - -// TransactionReceiver implementation used in fuzzing. -// -// When constructed, start sending fuzzed requests to the client. When all the -// bytes are consumed, the reference to WireReader will be released. -class TransactionReceiverForFuzzing : public grpc_binder::TransactionReceiver { - public: - TransactionReceiverForFuzzing( - const uint8_t* data, size_t size, - grpc_core::RefCountedPtr wire_reader_ref, - grpc_binder::TransactionReceiver::OnTransactCb cb) { - gpr_log(GPR_INFO, "Construct TransactionReceiverForFuzzing"); - GPR_ASSERT(g_fuzzing_thread == nullptr); - g_fuzzing_thread = new std::thread( - FuzzingLoop, data, size, std::move(wire_reader_ref), std::move(cb)); - } - - void* GetRawBinder() override { return nullptr; } -}; - -std::unique_ptr -BinderForFuzzing::ConstructTxReceiver( - grpc_core::RefCountedPtr wire_reader_ref, - grpc_binder::TransactionReceiver::OnTransactCb cb) const { - return absl::make_unique(data_, size_, - wire_reader_ref, cb); -} - -void* tag(intptr_t t) { return reinterpret_cast(t); } -void dont_log(gpr_log_func_args*) {} - -} // namespace +static void dont_log(gpr_log_func_args*) {} extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { grpc_test_only_set_slice_hash_seed(0); @@ -246,7 +42,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr); grpc_transport* client_transport = grpc_create_binder_transport_client( - absl::make_unique(data, size)); + absl::make_unique(data, size)); grpc_arg authority_arg = grpc_channel_arg_string_create( const_cast(GRPC_ARG_DEFAULT_AUTHORITY), const_cast("test-authority")); @@ -322,9 +118,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (requested_calls) { grpc_call_cancel(call, nullptr); } - if (g_fuzzing_thread) { - g_fuzzing_thread->join(); - } + grpc_binder::fuzzing::JoinFuzzingThread(); for (int i = 0; i < requested_calls; i++) { ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), nullptr); @@ -346,10 +140,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { grpc_byte_buffer_destroy(response_payload_recv); } } - if (g_fuzzing_thread) { - delete g_fuzzing_thread; - g_fuzzing_thread = nullptr; - } grpc_shutdown(); return 0; } diff --git a/test/core/transport/binder/end2end/fuzzers/fuzzer_utils.cc b/test/core/transport/binder/end2end/fuzzers/fuzzer_utils.cc new file mode 100644 index 00000000000..e11b6e3652f --- /dev/null +++ b/test/core/transport/binder/end2end/fuzzers/fuzzer_utils.cc @@ -0,0 +1,153 @@ +// Copyright 2021 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/transport/binder/end2end/fuzzers/fuzzer_utils.h" + +namespace grpc_binder { +namespace fuzzing { + +namespace { + +std::thread* g_fuzzing_thread = nullptr; + +template +void CreateFuzzingThread(Args&&... args) { + GPR_ASSERT(g_fuzzing_thread == nullptr); + g_fuzzing_thread = new std::thread(std::forward(args)...); +} + +} // namespace + +void JoinFuzzingThread() { + if (g_fuzzing_thread) { + g_fuzzing_thread->join(); + delete g_fuzzing_thread; + g_fuzzing_thread = nullptr; + } +} + +int32_t ReadableParcelForFuzzing::GetDataSize() const { + return data_provider_->ConsumeIntegral(); +} + +absl::Status ReadableParcelForFuzzing::ReadInt32(int32_t* data) { + if (consumed_data_size_ >= kParcelDataSizeLimit) { + return absl::InternalError("Parcel size limit exceeds"); + } + if (!is_setup_transport_ && data_provider_->ConsumeBool()) { + return absl::InternalError("error"); + } + *data = data_provider_->ConsumeIntegral(); + consumed_data_size_ += sizeof(int32_t); + return absl::OkStatus(); +} + +absl::Status ReadableParcelForFuzzing::ReadInt64(int64_t* data) { + if (consumed_data_size_ >= kParcelDataSizeLimit) { + return absl::InternalError("Parcel size limit exceeds"); + } + if (!is_setup_transport_ && data_provider_->ConsumeBool()) { + return absl::InternalError("error"); + } + *data = data_provider_->ConsumeIntegral(); + consumed_data_size_ += sizeof(int64_t); + return absl::OkStatus(); +} + +absl::Status ReadableParcelForFuzzing::ReadBinder( + std::unique_ptr* binder) { + if (consumed_data_size_ >= kParcelDataSizeLimit) { + return absl::InternalError("Parcel size limit exceeds"); + } + if (!is_setup_transport_ && data_provider_->ConsumeBool()) { + return absl::InternalError("error"); + } + *binder = absl::make_unique(); + consumed_data_size_ += sizeof(void*); + return absl::OkStatus(); +} + +absl::Status ReadableParcelForFuzzing::ReadByteArray(std::string* data) { + if (consumed_data_size_ >= kParcelDataSizeLimit) { + return absl::InternalError("Parcel size limit exceeds"); + } + if (!is_setup_transport_ && data_provider_->ConsumeBool()) { + return absl::InternalError("error"); + } + *data = data_provider_->ConsumeRandomLengthString(100); + consumed_data_size_ += data->size(); + return absl::OkStatus(); +} + +absl::Status ReadableParcelForFuzzing::ReadString(std::string* data) { + if (consumed_data_size_ >= kParcelDataSizeLimit) { + return absl::InternalError("Parcel size limit exceeds"); + } + if (!is_setup_transport_ && data_provider_->ConsumeBool()) { + return absl::InternalError("error"); + } + *data = data_provider_->ConsumeRandomLengthString(100); + consumed_data_size_ += data->size(); + return absl::OkStatus(); +} + +void FuzzingLoop( + const uint8_t* data, size_t size, + grpc_core::RefCountedPtr wire_reader_ref, + grpc_binder::TransactionReceiver::OnTransactCb callback) { + FuzzedDataProvider data_provider(data, size); + { + // Send SETUP_TRANSPORT request. + std::unique_ptr parcel = + absl::make_unique( + &data_provider, + /*is_setup_transport=*/true); + callback(static_cast( + grpc_binder::BinderTransportTxCode::SETUP_TRANSPORT), + parcel.get(), /*uid=*/data_provider.ConsumeIntegral()) + .IgnoreError(); + } + while (data_provider.remaining_bytes() > 0) { + transaction_code_t tx_code = + data_provider.ConsumeIntegralInRange( + 0, LAST_CALL_TRANSACTION); + std::unique_ptr parcel = + absl::make_unique( + &data_provider, + /*is_setup_transport=*/false); + callback(tx_code, parcel.get(), + /*uid=*/data_provider.ConsumeIntegral()) + .IgnoreError(); + } + wire_reader_ref = nullptr; +} + +TranasctionReceiverForFuzzing::TranasctionReceiverForFuzzing( + const uint8_t* data, size_t size, + grpc_core::RefCountedPtr wire_reader_ref, + TransactionReceiver::OnTransactCb cb) { + gpr_log(GPR_INFO, "Construct TranasctionReceiverForFuzzing"); + CreateFuzzingThread(FuzzingLoop, data, size, std::move(wire_reader_ref), + std::move(cb)); +} + +std::unique_ptr BinderForFuzzing::ConstructTxReceiver( + grpc_core::RefCountedPtr wire_reader_ref, + TransactionReceiver::OnTransactCb cb) const { + return absl::make_unique(data_, size_, + wire_reader_ref, cb); +} + +} // namespace fuzzing +} // namespace grpc_binder diff --git a/test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h b/test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h new file mode 100644 index 00000000000..396439ac1ad --- /dev/null +++ b/test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h @@ -0,0 +1,146 @@ +// Copyright 2021 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_TRANSPORT_BINDER_END2END_FUZZERS_FUZZER_UTILS_H +#define GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_FUZZERS_FUZZER_UTILS_H + +#include + +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/status/status.h" + +#include + +#include "src/core/ext/transport/binder/wire_format/binder.h" +#include "src/core/ext/transport/binder/wire_format/wire_reader.h" + +namespace grpc_binder { +namespace fuzzing { + +// A WritableParcel implementation that simply does nothing. Don't use +// MockWritableParcel here since capturing calls is expensive. +class NoOpWritableParcel : public WritableParcel { + public: + int32_t GetDataSize() const override { return 0; } + absl::Status WriteInt32(int32_t /*data*/) override { + return absl::OkStatus(); + } + absl::Status WriteInt64(int64_t /*data*/) override { + return absl::OkStatus(); + } + absl::Status WriteBinder(HasRawBinder* /*binder*/) override { + return absl::OkStatus(); + } + absl::Status WriteString(absl::string_view /*s*/) override { + return absl::OkStatus(); + } + absl::Status WriteByteArray(const int8_t* /*buffer*/, + int32_t /*length*/) override { + return absl::OkStatus(); + } +}; + +// Binder implementation used in fuzzing. +// +// Most of its the functionalities are no-op, except ConstructTxReceiver now +// returns a TranasctionReceiverForFuzzing. +class BinderForFuzzing : public Binder { + public: + BinderForFuzzing() : input_(absl::make_unique()) {} + + BinderForFuzzing(const uint8_t* data, size_t size) + : data_(data), + size_(size), + input_(absl::make_unique()) {} + + void Initialize() override {} + absl::Status PrepareTransaction() override { return absl::OkStatus(); } + + absl::Status Transact(BinderTransportTxCode /*tx_code*/) override { + return absl::OkStatus(); + } + + std::unique_ptr ConstructTxReceiver( + grpc_core::RefCountedPtr wire_reader_ref, + TransactionReceiver::OnTransactCb cb) const override; + + WritableParcel* GetWritableParcel() const override { return input_.get(); } + void* GetRawBinder() override { return nullptr; } + + private: + const uint8_t* data_; + size_t size_; + std::unique_ptr input_; +}; + +// ReadableParcel implementation used in fuzzing. +// +// It consumes a FuzzedDataProvider, and returns fuzzed data upon user's +// requests. Each operation can also fail per fuzzer's request by checking the +// next bool in the data stream. +class ReadableParcelForFuzzing : public ReadableParcel { + public: + ReadableParcelForFuzzing(FuzzedDataProvider* data_provider, + bool is_setup_transport) + : data_provider_(data_provider), + is_setup_transport_(is_setup_transport), + consumed_data_size_(0) {} + + int32_t GetDataSize() const override; + absl::Status ReadInt32(int32_t* data) override; + absl::Status ReadInt64(int64_t* data) override; + absl::Status ReadBinder(std::unique_ptr* binder) override; + absl::Status ReadByteArray(std::string* data) override; + absl::Status ReadString(std::string* data) override; + + private: + FuzzedDataProvider* data_provider_; + // Whether this parcel contains a SETUP_TRANSPORT request. If it is, we will + // avoid returning errors in the Read* functions so that the fuzzer will not + // be blocked waiting for the correct request. + bool is_setup_transport_; + + static constexpr size_t kParcelDataSizeLimit = 1024 * 1024; + size_t consumed_data_size_; +}; + +void JoinFuzzingThread(); + +void FuzzingLoop(const uint8_t* data, size_t size, + grpc_core::RefCountedPtr wire_reader_ref, + TransactionReceiver::OnTransactCb callback); + +// TransactionReceiver implementation used in fuzzing. +// +// When constructed, start sending fuzzed requests to the client. When all the +// bytes are consumed, the reference to WireReader will be released. +class TranasctionReceiverForFuzzing : public TransactionReceiver { + public: + TranasctionReceiverForFuzzing( + const uint8_t* data, size_t size, + grpc_core::RefCountedPtr wire_reader_ref, + TransactionReceiver::OnTransactCb cb); + + void* GetRawBinder() override { return nullptr; } +}; + +} // namespace fuzzing +} // namespace grpc_binder + +#endif // GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_FUZZERS_FUZZER_UTILS_H diff --git a/test/core/transport/binder/end2end/fuzzers/server_fuzzer.cc b/test/core/transport/binder/end2end/fuzzers/server_fuzzer.cc new file mode 100644 index 00000000000..82da7073ff1 --- /dev/null +++ b/test/core/transport/binder/end2end/fuzzers/server_fuzzer.cc @@ -0,0 +1,122 @@ +// Copyright 2021 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 + +#include "src/core/ext/transport/binder/transport/binder_transport.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/server.h" +#include "test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h" + +bool squelch = true; +bool leak_check = true; + +static void* tag(intptr_t t) { return reinterpret_cast(t); } + +static void dont_log(gpr_log_func_args* /*args*/) {} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + grpc_test_only_set_slice_hash_seed(0); + if (squelch) gpr_set_log_function(dont_log); + grpc_init(); + { + // Copied and modified from grpc/test/core/end2end/fuzzers/server_fuzzer.cc + grpc_core::ExecCtx exec_ctx; + grpc_core::Executor::SetThreadingAll(false); + + grpc_server* server = grpc_server_create(nullptr, nullptr); + grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr); + grpc_server_register_completion_queue(server, cq, nullptr); + // TODO(ctiller): add more registered methods (one for POST, one for PUT) + grpc_server_register_method(server, "/reg", nullptr, {}, 0); + grpc_server_start(server); + grpc_transport* server_transport = grpc_create_binder_transport_server( + absl::make_unique(data, size)); + server->core_server->SetupTransport(server_transport, nullptr, nullptr, + nullptr); + + grpc_call* call1 = nullptr; + grpc_call_details call_details1; + grpc_metadata_array request_metadata1; + grpc_call_details_init(&call_details1); + grpc_metadata_array_init(&request_metadata1); + int requested_calls = 0; + + GPR_ASSERT(GRPC_CALL_OK == + grpc_server_request_call(server, &call1, &call_details1, + &request_metadata1, cq, cq, tag(1))); + requested_calls++; + + grpc_event ev; + while (true) { + grpc_core::ExecCtx::Get()->Flush(); + ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), + nullptr); + switch (ev.type) { + case GRPC_QUEUE_TIMEOUT: + goto done; + case GRPC_QUEUE_SHUTDOWN: + break; + case GRPC_OP_COMPLETE: + if (ev.tag == tag(1)) { + requested_calls--; + // TODO(ctiller): keep reading that call! + } + break; + } + } + + done: + grpc_binder::fuzzing::JoinFuzzingThread(); + if (call1 != nullptr) grpc_call_unref(call1); + grpc_call_details_destroy(&call_details1); + grpc_metadata_array_destroy(&request_metadata1); + grpc_server_shutdown_and_notify(server, cq, tag(0xdead)); + grpc_server_cancel_all_calls(server); + grpc_millis deadline = grpc_core::ExecCtx::Get()->Now() + 5000; + for (int i = 0; i <= requested_calls; i++) { + // A single grpc_completion_queue_next might not be sufficient for getting + // the tag from shutdown, because we might potentially get blocked by + // an operation happening on the timer thread. + // For example, the deadline timer might expire, leading to the timer + // thread trying to cancel the RPC and thereby acquiring a few references + // to the call. This will prevent the shutdown to complete till the timer + // thread releases those references. + // As a solution, we are going to keep performing a cq_next for a + // liberal period of 5 seconds for the timer thread to complete its work. + do { + ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), + nullptr); + grpc_core::ExecCtx::Get()->InvalidateNow(); + } while (ev.type != GRPC_OP_COMPLETE && + grpc_core::ExecCtx::Get()->Now() < deadline); + GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); + } + grpc_completion_queue_shutdown(cq); + for (int i = 0; i <= requested_calls; i++) { + do { + ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), + nullptr); + grpc_core::ExecCtx::Get()->InvalidateNow(); + } while (ev.type != GRPC_QUEUE_SHUTDOWN && + grpc_core::ExecCtx::Get()->Now() < deadline); + GPR_ASSERT(ev.type == GRPC_QUEUE_SHUTDOWN); + } + grpc_server_destroy(server); + grpc_completion_queue_destroy(cq); + } + grpc_shutdown(); + return 0; +} diff --git a/test/core/transport/binder/end2end/fuzzers/server_fuzzer_corpus/crash-missing-authority b/test/core/transport/binder/end2end/fuzzers/server_fuzzer_corpus/crash-missing-authority new file mode 100644 index 00000000000..6e51d213d7d Binary files /dev/null and b/test/core/transport/binder/end2end/fuzzers/server_fuzzer_corpus/crash-missing-authority differ diff --git a/test/core/transport/binder/end2end/fuzzers/server_fuzzer_corpus/empty b/test/core/transport/binder/end2end/fuzzers/server_fuzzer_corpus/empty new file mode 100644 index 00000000000..e69de29bb2d