Server fuzzer for binder transport (#27122)

* [binder] Add server fuzzer

* Add a crash reproducer related to #27321

* Address coments & fix bugs

* Rebase & Deal with merge conflicts
pull/27438/head
Ta-Wei Tu 3 years ago committed by GitHub
parent a8592b0a84
commit b7f27209cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
  2. 45
      test/core/transport/binder/end2end/fuzzers/BUILD
  3. 224
      test/core/transport/binder/end2end/fuzzers/client_fuzzer.cc
  4. 153
      test/core/transport/binder/end2end/fuzzers/fuzzer_utils.cc
  5. 146
      test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h
  6. 122
      test/core/transport/binder/end2end/fuzzers/server_fuzzer.cc
  7. BIN
      test/core/transport/binder/end2end/fuzzers/server_fuzzer_corpus/crash-missing-authority
  8. 0
      test/core/transport/binder/end2end/fuzzers/server_fuzzer_corpus/empty

@ -117,7 +117,7 @@ void TransportStreamReceiverImpl::RegisterRecvTrailingMetadata(
void TransportStreamReceiverImpl::NotifyRecvInitialMetadata(
StreamIdentifier id, absl::StatusOr<Metadata> 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;

@ -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",
],
)

@ -12,228 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fuzzer/FuzzedDataProvider.h>
#include <thread>
#include <utility>
#include "absl/memory/memory.h"
#include <grpc/grpc.h>
#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<NoOpWritableParcel>()) {}
BinderForFuzzing(const uint8_t* data, size_t size)
: data_(data),
size_(size),
input_(absl::make_unique<NoOpWritableParcel>()) {}
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<grpc_binder::TransactionReceiver> ConstructTxReceiver(
grpc_core::RefCountedPtr<grpc_binder::WireReader> 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<grpc_binder::WritableParcel> 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<int32_t>();
}
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<int32_t>();
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<int64_t>();
consumed_data_size_ += sizeof(int64_t);
return absl::OkStatus();
}
absl::Status ReadBinder(
std::unique_ptr<grpc_binder::Binder>* 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<BinderForFuzzing>();
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<void*>(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<grpc_binder::WireReader> wire_reader_ref,
grpc_binder::TransactionReceiver::OnTransactCb callback) {
FuzzedDataProvider data_provider(data, size);
{
// Send SETUP_TRANSPORT request.
std::unique_ptr<grpc_binder::ReadableParcel> parcel =
absl::make_unique<ReadableParcelForFuzzing>(
&data_provider,
/*is_setup_transport=*/true);
callback(static_cast<transaction_code_t>(
grpc_binder::BinderTransportTxCode::SETUP_TRANSPORT),
parcel.get(), /*uid=*/0)
.IgnoreError();
}
while (data_provider.remaining_bytes() > 0) {
transaction_code_t tx_code =
data_provider.ConsumeIntegralInRange<transaction_code_t>(
0, LAST_CALL_TRANSACTION);
std::unique_ptr<grpc_binder::ReadableParcel> parcel =
absl::make_unique<ReadableParcelForFuzzing>(
&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<grpc_binder::WireReader> 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<grpc_binder::TransactionReceiver>
BinderForFuzzing::ConstructTxReceiver(
grpc_core::RefCountedPtr<grpc_binder::WireReader> wire_reader_ref,
grpc_binder::TransactionReceiver::OnTransactCb cb) const {
return absl::make_unique<TransactionReceiverForFuzzing>(data_, size_,
wire_reader_ref, cb);
}
void* tag(intptr_t t) { return reinterpret_cast<void*>(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<BinderForFuzzing>(data, size));
absl::make_unique<grpc_binder::fuzzing::BinderForFuzzing>(data, size));
grpc_arg authority_arg = grpc_channel_arg_string_create(
const_cast<char*>(GRPC_ARG_DEFAULT_AUTHORITY),
const_cast<char*>("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;
}

@ -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 <typename... Args>
void CreateFuzzingThread(Args&&... args) {
GPR_ASSERT(g_fuzzing_thread == nullptr);
g_fuzzing_thread = new std::thread(std::forward<Args>(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<int32_t>();
}
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<int32_t>();
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<int64_t>();
consumed_data_size_ += sizeof(int64_t);
return absl::OkStatus();
}
absl::Status ReadableParcelForFuzzing::ReadBinder(
std::unique_ptr<Binder>* 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<BinderForFuzzing>();
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<grpc_binder::WireReader> wire_reader_ref,
grpc_binder::TransactionReceiver::OnTransactCb callback) {
FuzzedDataProvider data_provider(data, size);
{
// Send SETUP_TRANSPORT request.
std::unique_ptr<grpc_binder::ReadableParcel> parcel =
absl::make_unique<ReadableParcelForFuzzing>(
&data_provider,
/*is_setup_transport=*/true);
callback(static_cast<transaction_code_t>(
grpc_binder::BinderTransportTxCode::SETUP_TRANSPORT),
parcel.get(), /*uid=*/data_provider.ConsumeIntegral<int>())
.IgnoreError();
}
while (data_provider.remaining_bytes() > 0) {
transaction_code_t tx_code =
data_provider.ConsumeIntegralInRange<transaction_code_t>(
0, LAST_CALL_TRANSACTION);
std::unique_ptr<grpc_binder::ReadableParcel> parcel =
absl::make_unique<ReadableParcelForFuzzing>(
&data_provider,
/*is_setup_transport=*/false);
callback(tx_code, parcel.get(),
/*uid=*/data_provider.ConsumeIntegral<int>())
.IgnoreError();
}
wire_reader_ref = nullptr;
}
TranasctionReceiverForFuzzing::TranasctionReceiverForFuzzing(
const uint8_t* data, size_t size,
grpc_core::RefCountedPtr<WireReader> 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<TransactionReceiver> BinderForFuzzing::ConstructTxReceiver(
grpc_core::RefCountedPtr<WireReader> wire_reader_ref,
TransactionReceiver::OnTransactCb cb) const {
return absl::make_unique<TranasctionReceiverForFuzzing>(data_, size_,
wire_reader_ref, cb);
}
} // namespace fuzzing
} // namespace grpc_binder

@ -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 <fuzzer/FuzzedDataProvider.h>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include <grpc/support/log.h>
#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<NoOpWritableParcel>()) {}
BinderForFuzzing(const uint8_t* data, size_t size)
: data_(data),
size_(size),
input_(absl::make_unique<NoOpWritableParcel>()) {}
void Initialize() override {}
absl::Status PrepareTransaction() override { return absl::OkStatus(); }
absl::Status Transact(BinderTransportTxCode /*tx_code*/) override {
return absl::OkStatus();
}
std::unique_ptr<TransactionReceiver> ConstructTxReceiver(
grpc_core::RefCountedPtr<WireReader> 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<WritableParcel> 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>* 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<WireReader> 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<WireReader> 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

@ -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 <grpc/grpc.h>
#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<void*>(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<grpc_binder::fuzzing::BinderForFuzzing>(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;
}
Loading…
Cancel
Save