1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
/*
*
* Copyright 2016 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/chttp2/transport/chttp2_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/util/memory_counters.h"
#include "test/core/util/mock_endpoint.h"
bool squelch = true;
bool leak_check = true;
static void discard_write(grpc_slice slice) {}
static void* tag(int n) { return (void*)static_cast<uintptr_t>(n); }
static int detag(void* p) { return static_cast<int>((uintptr_t)p); }
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);
struct grpc_memory_counters counters;
if (squelch) gpr_set_log_function(dont_log);
if (leak_check) grpc_memory_counters_init();
grpc_init();
{
grpc_core::ExecCtx exec_ctx;
grpc_executor_set_threading(false);
grpc_resource_quota* resource_quota =
grpc_resource_quota_create("server_fuzzer");
grpc_endpoint* mock_endpoint =
grpc_mock_endpoint_create(discard_write, resource_quota);
grpc_resource_quota_unref_internal(resource_quota);
grpc_mock_endpoint_put_read(
mock_endpoint, grpc_slice_from_copied_buffer((const char*)data, size));
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 registered methods (one for POST, one for PUT)
// void *registered_method =
// grpc_server_register_method(server, "/reg", NULL, 0);
grpc_server_start(server);
grpc_transport* transport =
grpc_create_chttp2_transport(nullptr, mock_endpoint, false);
grpc_server_setup_transport(server, transport, nullptr, nullptr, 0);
grpc_chttp2_transport_start_reading(transport, 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 (1) {
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:
switch (detag(ev.tag)) {
case 1:
requested_calls--;
// TODO(ctiller): keep reading that call!
break;
}
}
}
done:
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();
if (leak_check) {
counters = grpc_memory_counters_snapshot();
grpc_memory_counters_destroy();
GPR_ASSERT(counters.total_size_relative == 0);
}
return 0;
}
|