/src/h2o/fuzz/driver_h3.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021 Fastly, Inc. |
3 | | * |
4 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | | * of this software and associated documentation files (the "Software"), to |
6 | | * deal in the Software without restriction, including without limitation the |
7 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
8 | | * sell copies of the Software, and to permit persons to whom the Software is |
9 | | * furnished to do so, subject to the following conditions: |
10 | | * |
11 | | * The above copyright notice and this permission notice shall be included in |
12 | | * all copies or substantial portions of the Software. |
13 | | * |
14 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
19 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
20 | | * IN THE SOFTWARE. |
21 | | */ |
22 | | |
23 | | /* |
24 | | * This file implements a test harness for using h2o with LibFuzzer. |
25 | | * See http://llvm.org/docs/LibFuzzer.html for more info. |
26 | | */ |
27 | | |
28 | | #define H2O_USE_EPOLL 1 |
29 | | #include <signal.h> |
30 | | |
31 | | #include "driver_common.h" |
32 | | |
33 | | #include "picotls.h" |
34 | | #include "quicly.h" |
35 | | #include "quicly_mock.h" |
36 | | #include "picotls/openssl.h" |
37 | | #include "h2o.h" |
38 | | #include "h2o/http3_server.h" |
39 | | #include "h2o/http3_internal.h" |
40 | | #include "h2o/qpack.h" |
41 | | |
42 | | static h2o_globalconf_t config; |
43 | | static h2o_context_t ctx; |
44 | | static h2o_accept_ctx_t accept_ctx; |
45 | | static quicly_cid_plaintext_t next_cid; |
46 | | static h2o_http3_server_ctx_t server_ctx; |
47 | | static quicly_context_t qctx = quicly_spec_context; |
48 | | static ptls_context_t ptls_ctx = { |
49 | | .random_bytes = ptls_openssl_random_bytes, |
50 | | .cipher_suites = ptls_openssl_cipher_suites, |
51 | | .get_time = &ptls_get_time, |
52 | | }; |
53 | | static h2o_http3_conn_callbacks_t conn_callbacks = H2O_HTTP3_CONN_CALLBACKS; |
54 | | static char unix_listener[PATH_MAX]; |
55 | | static int client_timeout_ms; |
56 | | |
57 | | static quicly_address_t src_addr; |
58 | | static quicly_address_t dst_addr; |
59 | | |
60 | | static void quic_init_context(h2o_quic_ctx_t *ctx, h2o_evloop_t *loop) |
61 | 1 | { |
62 | 1 | ctx->loop = loop; |
63 | 1 | ctx->quic = &qctx; |
64 | 1 | ctx->quic->tls = &ptls_ctx; |
65 | 1 | ctx->conns_by_id = kh_init_h2o_quic_idmap(); |
66 | 1 | ctx->conns_accepting = kh_init_h2o_quic_acceptmap(); |
67 | 1 | } |
68 | | |
69 | | static bool init_done; |
70 | | static size_t num_connections = 0; |
71 | | |
72 | | static void on_destroy_connection(h2o_quic_conn_t *conn) |
73 | 6.68k | { |
74 | 6.68k | --num_connections; |
75 | 6.68k | H2O_HTTP3_CONN_CALLBACKS.super.destroy_connection(conn); |
76 | 6.68k | } |
77 | | |
78 | | /* record egress activities on stream 0 */ |
79 | | /** |
80 | | * sent a full response? |
81 | | */ |
82 | | static bool sent_response = false; |
83 | | static uint64_t last_sent_off; |
84 | | static size_t last_sent_len; |
85 | | |
86 | | static void on_stream_send_cb(mquicly_on_stream_send_t *self, quicly_conn_t *conn, quicly_stream_t *stream, const void *buff, |
87 | | uint64_t off, size_t len, int is_fin) |
88 | 21.9k | { |
89 | | /* at the moment we aren't interested in streams other than 0 */ |
90 | 21.9k | if (stream->stream_id != 0) |
91 | 20.0k | return; |
92 | | |
93 | 1.87k | last_sent_off = off; |
94 | 1.87k | last_sent_len = len; |
95 | | |
96 | 1.87k | if (is_fin) |
97 | 1.40k | sent_response = true; |
98 | 1.87k | } |
99 | | |
100 | | static mquicly_on_stream_send_t on_stream_send = {.cb = on_stream_send_cb}; |
101 | | |
102 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) |
103 | 6.68k | { |
104 | 6.68k | if (!init_done) { |
105 | 1 | h2o_hostconf_t *hostconf; |
106 | 1 | h2o_access_log_filehandle_t *logfh = NULL; |
107 | 1 | h2o_pathconf_t *pathconf; |
108 | 1 | static char tmpname[] = "/tmp/h2o-fuzz-XXXXXX"; |
109 | 1 | char *dirname; |
110 | 1 | pthread_t tupstream; |
111 | 1 | const char *client_timeout_ms_str, *log_access_str; |
112 | | |
113 | 1 | h2o_barrier_init(&init_barrier, 2); |
114 | 1 | signal(SIGPIPE, SIG_IGN); |
115 | | |
116 | 1 | dirname = mkdtemp(tmpname); |
117 | 1 | snprintf(unix_listener, sizeof(unix_listener), "http://[unix://%s/_.sock]/proxy", dirname); |
118 | 1 | if ((client_timeout_ms_str = getenv("H2O_FUZZER_CLIENT_TIMEOUT")) != NULL) |
119 | 0 | client_timeout_ms = atoi(client_timeout_ms_str); |
120 | 1 | if (!client_timeout_ms) |
121 | 1 | client_timeout_ms = 10; |
122 | | |
123 | 1 | if ((log_access_str = getenv("H2O_FUZZER_LOG_ACCESS")) != NULL) { |
124 | 0 | bool log_access = atoi(log_access_str) != 0; |
125 | 0 | if (log_access) |
126 | 0 | logfh = h2o_access_log_open_handle("/dev/stdout", NULL, H2O_LOGCONF_ESCAPE_APACHE); |
127 | 0 | } |
128 | | |
129 | 1 | h2o_config_init(&config); |
130 | 1 | hostconf = h2o_config_register_host(&config, h2o_iovec_init(H2O_STRLIT("default")), 65535); |
131 | | |
132 | 1 | register_proxy(hostconf, unix_listener, logfh); |
133 | | |
134 | 1 | pathconf = h2o_config_register_path(hostconf, "/", 0); |
135 | 1 | h2o_file_register(pathconf, "examples/doc_root", NULL, NULL, 0); |
136 | 1 | if (logfh != NULL) |
137 | 0 | h2o_access_log_register(pathconf, logfh); |
138 | | |
139 | 1 | mquicly_context.on_stream_send = &on_stream_send; |
140 | | |
141 | 1 | h2o_context_init(&ctx, h2o_evloop_create(), &config); |
142 | 1 | accept_ctx.ctx = &ctx; |
143 | 1 | accept_ctx.hosts = config.hosts; |
144 | | |
145 | 1 | server_ctx.super.next_cid = &next_cid; |
146 | 1 | server_ctx.accept_ctx = &accept_ctx; |
147 | 1 | server_ctx.send_retry = 0; |
148 | 1 | server_ctx.qpack.encoder_table_capacity = 4096; |
149 | | |
150 | 1 | conn_callbacks.super.destroy_connection = on_destroy_connection; |
151 | | |
152 | 1 | ptls_log._generation = 0; /* disable tracing */ |
153 | | |
154 | 1 | quic_init_context(&server_ctx.super, ctx.loop); |
155 | 1 | h2o_http3_server_amend_quicly_context(&config, server_ctx.super.quic); |
156 | 1 | if (pthread_create(&tupstream, NULL, upstream_thread, dirname) != 0) { |
157 | 0 | abort(); |
158 | 0 | } |
159 | 1 | h2o_barrier_wait(&init_barrier); |
160 | 1 | init_done = true; |
161 | 1 | } |
162 | | |
163 | 6.68k | src_addr.sin.sin_family = dst_addr.sin.sin_family = AF_INET; |
164 | 6.68k | src_addr.sin.sin_addr.s_addr = dst_addr.sin.sin_addr.s_addr = htonl(0x7f000001); |
165 | 6.68k | src_addr.sin.sin_port = htons(12345); |
166 | 6.68k | dst_addr.sin.sin_port = htons(8443); |
167 | | |
168 | 6.68k | ++num_connections; |
169 | 6.68k | sent_response = false; |
170 | 6.68k | h2o_http3_conn_t *conn = h2o_http3_server_accept(&server_ctx, &dst_addr, &src_addr, NULL /* initial_packet */, |
171 | 6.68k | NULL /* address_token */, &conn_callbacks); |
172 | 6.68k | assert(conn != NULL); |
173 | 6.68k | assert(&conn->super != &h2o_quic_accept_conn_decryption_failed); |
174 | 6.68k | quicly_stream_t *stream; |
175 | 6.68k | int ret = mquicly_open_stream(conn->super.quic, &stream, 1 /* remote initiated */, 0 /* bidi */); |
176 | 6.68k | assert(ret == 0); |
177 | 6.68k | assert(stream != NULL); |
178 | | |
179 | 6.68k | quicly_recvstate_update(&stream->recvstate, 0, &Size, 1, 63); |
180 | 6.68k | stream->callbacks->on_receive(stream, 0, Data, Size); |
181 | | |
182 | 6.68k | uint64_t loop_start = h2o_now(ctx.loop); |
183 | 1.88M | do { |
184 | 1.88M | if (loop_start + client_timeout_ms < h2o_now(ctx.loop)) { |
185 | 1.08k | break; /* time up */ |
186 | 1.08k | } |
187 | 1.87M | uint64_t time_left = loop_start + client_timeout_ms - h2o_now(ctx.loop); |
188 | 1.87M | h2o_evloop_run(ctx.loop, time_left); |
189 | 1.87M | if (num_connections == 0) { |
190 | | /* connection was closed abruptly due to an error in the input */ |
191 | 4.20k | return 0; |
192 | 4.20k | } |
193 | | |
194 | 1.87M | if (last_sent_len > 0) { |
195 | | /* simulate ack-receiving event */ |
196 | 1.40k | quicly_sendstate_sent_t sent = {.start = last_sent_off, .end = last_sent_off + last_sent_len}; |
197 | 1.40k | size_t bytes_to_shift; |
198 | | |
199 | 1.40k | last_sent_len = 0; |
200 | 1.40k | int ret = quicly_sendstate_acked(&stream->sendstate, &sent, &bytes_to_shift); |
201 | 1.40k | assert(ret == 0); |
202 | 1.40k | if (bytes_to_shift != 0) |
203 | 926 | stream->callbacks->on_send_shift(stream, bytes_to_shift); |
204 | 1.40k | } |
205 | 1.87M | } while (!sent_response); |
206 | | |
207 | 2.48k | mquicly_closed_by_remote(conn->super.quic, 0, 0 /* TODO: frame_type */, ptls_iovec_init(NULL, 0)); |
208 | | /* simulate timer update at the end of process_packets() */ |
209 | 2.48k | h2o_quic_schedule_timer(&conn->super); |
210 | | |
211 | 2.48k | do { |
212 | 2.48k | h2o_evloop_run(ctx.loop, 1); |
213 | 2.48k | } while (num_connections != 0); |
214 | | |
215 | 2.48k | return 0; |
216 | 6.68k | } |