1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "failmalloc_test.h"
26
27 #include <stdio.h>
28 #include <assert.h>
29
30 #include "munit.h"
31
32 #include "nghttp2_session.h"
33 #include "nghttp2_stream.h"
34 #include "nghttp2_frame.h"
35 #include "nghttp2_helper.h"
36 #include "malloc_wrapper.h"
37 #include "nghttp2_test_helper.h"
38
39 static const MunitTest tests[] = {
40 munit_void_test(test_nghttp2_session_send),
41 munit_void_test(test_nghttp2_session_send_server),
42 munit_void_test(test_nghttp2_session_recv),
43 munit_void_test(test_nghttp2_frame),
44 munit_void_test(test_nghttp2_hd),
45 munit_test_end(),
46 };
47
48 const MunitSuite failmalloc_suite = {
49 "/failmalloc", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE,
50 };
51
52 typedef struct {
53 uint8_t data[8192];
54 uint8_t *datamark, *datalimit;
55 } data_feed;
56
57 typedef struct {
58 data_feed *df;
59 size_t data_source_length;
60 } my_user_data;
61
data_feed_init(data_feed * df,nghttp2_bufs * bufs)62 static void data_feed_init(data_feed *df, nghttp2_bufs *bufs) {
63 nghttp2_buf *buf;
64 size_t data_length;
65
66 buf = &bufs->head->buf;
67 data_length = nghttp2_buf_len(buf);
68
69 assert(data_length <= sizeof(df->data));
70 memcpy(df->data, buf->pos, data_length);
71 df->datamark = df->data;
72 df->datalimit = df->data + data_length;
73 }
74
null_send_callback(nghttp2_session * session,const uint8_t * data,size_t len,int flags,void * user_data)75 static nghttp2_ssize null_send_callback(nghttp2_session *session,
76 const uint8_t *data, size_t len,
77 int flags, void *user_data) {
78 (void)session;
79 (void)data;
80 (void)flags;
81 (void)user_data;
82
83 return (nghttp2_ssize)len;
84 }
85
data_feed_recv_callback(nghttp2_session * session,uint8_t * data,size_t len,int flags,void * user_data)86 static nghttp2_ssize data_feed_recv_callback(nghttp2_session *session,
87 uint8_t *data, size_t len,
88 int flags, void *user_data) {
89 data_feed *df = ((my_user_data *)user_data)->df;
90 size_t avail = (size_t)(df->datalimit - df->datamark);
91 size_t wlen = nghttp2_min_size(avail, len);
92 (void)session;
93 (void)flags;
94
95 memcpy(data, df->datamark, wlen);
96 df->datamark += wlen;
97 return (nghttp2_ssize)wlen;
98 }
99
fixed_length_data_source_read_callback(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t len,uint32_t * data_flags,nghttp2_data_source * source,void * user_data)100 static nghttp2_ssize fixed_length_data_source_read_callback(
101 nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
102 uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
103 my_user_data *ud = (my_user_data *)user_data;
104 size_t wlen;
105 (void)session;
106 (void)stream_id;
107 (void)buf;
108 (void)source;
109
110 if (len < ud->data_source_length) {
111 wlen = len;
112 } else {
113 wlen = ud->data_source_length;
114 }
115 ud->data_source_length -= wlen;
116 if (ud->data_source_length == 0) {
117 *data_flags = NGHTTP2_DATA_FLAG_EOF;
118 }
119 return (nghttp2_ssize)wlen;
120 }
121
122 #define TEST_FAILMALLOC_RUN(FUN) \
123 do { \
124 int nmalloc, i; \
125 \
126 nghttp2_failmalloc = 0; \
127 nghttp2_nmalloc = 0; \
128 FUN(); \
129 nmalloc = nghttp2_nmalloc; \
130 \
131 nghttp2_failmalloc = 1; \
132 for (i = 0; i < nmalloc; ++i) { \
133 nghttp2_nmalloc = 0; \
134 nghttp2_failstart = i; \
135 /* printf("i=%zu\n", i); */ \
136 FUN(); \
137 /* printf("nmalloc=%d\n", nghttp2_nmalloc); */ \
138 } \
139 nghttp2_failmalloc = 0; \
140 } while (0)
141
run_nghttp2_session_send(void)142 static void run_nghttp2_session_send(void) {
143 nghttp2_session *session;
144 nghttp2_session_callbacks callbacks;
145 nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"),
146 MAKE_NV(":scheme", "https")};
147 nghttp2_data_provider2 data_prd;
148 nghttp2_settings_entry iv[2];
149 my_user_data ud;
150 int rv;
151 memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
152 callbacks.send_callback2 = null_send_callback;
153
154 data_prd.read_callback = fixed_length_data_source_read_callback;
155 ud.data_source_length = 64 * 1024;
156
157 iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
158 iv[0].value = 4096;
159 iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
160 iv[1].value = 100;
161
162 rv = nghttp2_session_client_new3(&session, &callbacks, &ud, NULL,
163 nghttp2_mem_fm());
164 if (rv != 0) {
165 goto client_new_fail;
166 }
167 rv = nghttp2_submit_request2(session, NULL, nv, ARRLEN(nv), &data_prd, NULL);
168 if (rv < 0) {
169 goto fail;
170 }
171 rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, nv,
172 ARRLEN(nv), NULL);
173 if (rv < 0) {
174 goto fail;
175 }
176 rv = nghttp2_session_send(session);
177 if (rv != 0) {
178 goto fail;
179 }
180 /* The HEADERS submitted by the previous nghttp2_submit_headers will
181 have stream ID 3. Send HEADERS to that stream. */
182 rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv,
183 ARRLEN(nv), NULL);
184 if (rv != 0) {
185 goto fail;
186 }
187 rv = nghttp2_submit_data2(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd);
188 if (rv != 0) {
189 goto fail;
190 }
191 rv = nghttp2_session_send(session);
192 if (rv != 0) {
193 goto fail;
194 }
195 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 3, NGHTTP2_CANCEL);
196 if (rv != 0) {
197 goto fail;
198 }
199 rv = nghttp2_session_send(session);
200 if (rv != 0) {
201 goto fail;
202 }
203 rv = nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL);
204 if (rv != 0) {
205 goto fail;
206 }
207 rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2);
208 if (rv != 0) {
209 goto fail;
210 }
211 rv = nghttp2_session_send(session);
212 if (rv != 0) {
213 goto fail;
214 }
215 rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 100, NGHTTP2_NO_ERROR,
216 NULL, 0);
217 if (rv != 0) {
218 goto fail;
219 }
220 rv = nghttp2_session_send(session);
221 if (rv != 0) {
222 goto fail;
223 }
224
225 fail:
226 nghttp2_session_del(session);
227 client_new_fail:;
228 }
229
test_nghttp2_session_send(void)230 void test_nghttp2_session_send(void) {
231 TEST_FAILMALLOC_RUN(run_nghttp2_session_send);
232 }
233
run_nghttp2_session_send_server(void)234 static void run_nghttp2_session_send_server(void) {
235 nghttp2_session *session;
236 nghttp2_session_callbacks *callbacks;
237 int rv;
238 const uint8_t *txdata;
239 nghttp2_ssize txdatalen;
240 const uint8_t origin[] = "nghttp2.org";
241 const uint8_t altsvc_field_value[] = "h2=\":443\"";
242 static const uint8_t nghttp2[] = "https://nghttp2.org";
243 static const nghttp2_origin_entry ov = {
244 (uint8_t *)nghttp2,
245 sizeof(nghttp2) - 1,
246 };
247
248 rv = nghttp2_session_callbacks_new(&callbacks);
249 if (rv != 0) {
250 return;
251 }
252
253 rv = nghttp2_session_server_new3(&session, callbacks, NULL, NULL,
254 nghttp2_mem_fm());
255
256 nghttp2_session_callbacks_del(callbacks);
257
258 if (rv != 0) {
259 return;
260 }
261
262 rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin,
263 sizeof(origin) - 1, altsvc_field_value,
264 sizeof(altsvc_field_value) - 1);
265 if (rv != 0) {
266 goto fail;
267 }
268
269 rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, &ov, 1);
270 if (rv != 0) {
271 goto fail;
272 }
273
274 txdatalen = nghttp2_session_mem_send2(session, &txdata);
275
276 if (txdatalen < 0) {
277 goto fail;
278 }
279
280 fail:
281 nghttp2_session_del(session);
282 }
283
test_nghttp2_session_send_server(void)284 void test_nghttp2_session_send_server(void) {
285 TEST_FAILMALLOC_RUN(run_nghttp2_session_send_server);
286 }
287
run_nghttp2_session_recv(void)288 static void run_nghttp2_session_recv(void) {
289 nghttp2_session *session;
290 nghttp2_session_callbacks callbacks;
291 nghttp2_hd_deflater deflater;
292 nghttp2_frame frame;
293 nghttp2_bufs bufs;
294 nghttp2_nv nv[] = {
295 MAKE_NV(":method", "GET"),
296 MAKE_NV(":scheme", "https"),
297 MAKE_NV(":authority", "example.org"),
298 MAKE_NV(":path", "/"),
299 };
300 nghttp2_settings_entry iv[2];
301 my_user_data ud;
302 data_feed df;
303 int rv;
304 nghttp2_nv *nva;
305 size_t nvlen;
306
307 rv = frame_pack_bufs_init(&bufs);
308
309 if (rv != 0) {
310 return;
311 }
312
313 memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
314 callbacks.recv_callback2 = data_feed_recv_callback;
315 ud.df = &df;
316
317 nghttp2_failmalloc_pause();
318 nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm());
319 nghttp2_session_server_new3(&session, &callbacks, &ud, NULL,
320 nghttp2_mem_fm());
321
322 /* Client preface */
323 nghttp2_bufs_add(&bufs, NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN);
324 data_feed_init(&df, &bufs);
325 nghttp2_bufs_reset(&bufs);
326 nghttp2_failmalloc_unpause();
327
328 rv = nghttp2_session_recv(session);
329 if (rv != 0) {
330 goto fail;
331 }
332
333 nghttp2_failmalloc_pause();
334 /* SETTINGS */
335 iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
336 iv[0].value = 4096;
337 iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
338 iv[1].value = 100;
339 nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE,
340 nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()),
341 2);
342 nghttp2_frame_pack_settings(&bufs, &frame.settings);
343 nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm());
344 data_feed_init(&df, &bufs);
345 nghttp2_bufs_reset(&bufs);
346 nghttp2_failmalloc_unpause();
347
348 rv = nghttp2_session_recv(session);
349 if (rv != 0) {
350 goto fail;
351 }
352
353 nghttp2_failmalloc_pause();
354 /* HEADERS */
355 nvlen = ARRLEN(nv);
356 nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm());
357 nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1,
358 NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen);
359 nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
360 nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm());
361 data_feed_init(&df, &bufs);
362 nghttp2_bufs_reset(&bufs);
363 nghttp2_failmalloc_unpause();
364
365 rv = nghttp2_session_recv(session);
366 if (rv != 0) {
367 goto fail;
368 }
369
370 /* PING */
371 nghttp2_failmalloc_pause();
372 nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL);
373 nghttp2_frame_pack_ping(&bufs, &frame.ping);
374 nghttp2_frame_ping_free(&frame.ping);
375 data_feed_init(&df, &bufs);
376 nghttp2_bufs_reset(&bufs);
377
378 nghttp2_failmalloc_unpause();
379
380 rv = nghttp2_session_recv(session);
381 if (rv != 0) {
382 goto fail;
383 }
384
385 /* RST_STREAM */
386 nghttp2_failmalloc_pause();
387 nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR);
388 nghttp2_frame_pack_rst_stream(&bufs, &frame.rst_stream);
389 nghttp2_frame_rst_stream_free(&frame.rst_stream);
390 nghttp2_bufs_reset(&bufs);
391
392 nghttp2_failmalloc_unpause();
393
394 rv = nghttp2_session_recv(session);
395 if (rv != 0) {
396 goto fail;
397 }
398
399 fail:
400 nghttp2_bufs_free(&bufs);
401 nghttp2_session_del(session);
402 nghttp2_hd_deflate_free(&deflater);
403 }
404
test_nghttp2_session_recv(void)405 void test_nghttp2_session_recv(void) {
406 TEST_FAILMALLOC_RUN(run_nghttp2_session_recv);
407 }
408
run_nghttp2_frame_pack_headers(void)409 static void run_nghttp2_frame_pack_headers(void) {
410 nghttp2_hd_deflater deflater;
411 nghttp2_hd_inflater inflater;
412 nghttp2_frame frame, oframe;
413 nghttp2_bufs bufs;
414 nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"),
415 MAKE_NV(":scheme", "https")};
416 int rv;
417 nghttp2_nv *nva;
418 size_t nvlen;
419
420 rv = frame_pack_bufs_init(&bufs);
421
422 if (rv != 0) {
423 return;
424 }
425
426 rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm());
427 if (rv != 0) {
428 goto deflate_init_fail;
429 }
430 rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm());
431 if (rv != 0) {
432 goto inflate_init_fail;
433 }
434 nvlen = ARRLEN(nv);
435 rv = nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm());
436 if (rv < 0) {
437 goto nv_copy_fail;
438 }
439 nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1,
440 NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen);
441 rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
442 if (rv != 0) {
443 goto fail;
444 }
445 rv = unpack_framebuf(&oframe, &bufs);
446 if (rv != 0) {
447 goto fail;
448 }
449 nghttp2_frame_headers_free(&oframe.headers, nghttp2_mem_fm());
450
451 fail:
452 nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm());
453 nv_copy_fail:
454 nghttp2_hd_inflate_free(&inflater);
455 inflate_init_fail:
456 nghttp2_hd_deflate_free(&deflater);
457 deflate_init_fail:
458 nghttp2_bufs_free(&bufs);
459 }
460
run_nghttp2_frame_pack_settings(void)461 static void run_nghttp2_frame_pack_settings(void) {
462 nghttp2_frame frame, oframe;
463 nghttp2_bufs bufs;
464 nghttp2_buf *buf;
465 nghttp2_settings_entry iv[2], *iv_copy;
466 int rv;
467
468 rv = frame_pack_bufs_init(&bufs);
469
470 if (rv != 0) {
471 return;
472 }
473
474 iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
475 iv[0].value = 4096;
476 iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
477 iv[1].value = 100;
478
479 iv_copy = nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm());
480
481 if (iv_copy == NULL) {
482 goto iv_copy_fail;
483 }
484
485 nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, iv_copy, 2);
486
487 rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
488
489 if (rv != 0) {
490 goto fail;
491 }
492
493 buf = &bufs.head->buf;
494
495 rv = nghttp2_frame_unpack_settings_payload2(
496 &oframe.settings.iv, &oframe.settings.niv, buf->pos + NGHTTP2_FRAME_HDLEN,
497 nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN, nghttp2_mem_fm());
498
499 if (rv != 0) {
500 goto fail;
501 }
502 nghttp2_frame_settings_free(&oframe.settings, nghttp2_mem_fm());
503
504 fail:
505 nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm());
506 iv_copy_fail:
507 nghttp2_bufs_free(&bufs);
508 }
509
test_nghttp2_frame(void)510 void test_nghttp2_frame(void) {
511 TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_headers);
512 TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_settings);
513 }
514
deflate_inflate(nghttp2_hd_deflater * deflater,nghttp2_hd_inflater * inflater,nghttp2_bufs * bufs,nghttp2_nv * nva,size_t nvlen,nghttp2_mem * mem)515 static int deflate_inflate(nghttp2_hd_deflater *deflater,
516 nghttp2_hd_inflater *inflater, nghttp2_bufs *bufs,
517 nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) {
518 int rv;
519
520 rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, nva, nvlen);
521
522 if (rv != 0) {
523 return rv;
524 }
525
526 rv = (int)inflate_hd(inflater, NULL, bufs, 0, mem);
527
528 if (rv < 0) {
529 return rv;
530 }
531
532 nghttp2_bufs_reset(bufs);
533
534 return 0;
535 }
536
run_nghttp2_hd(void)537 static void run_nghttp2_hd(void) {
538 nghttp2_hd_deflater deflater;
539 nghttp2_hd_inflater inflater;
540 nghttp2_bufs bufs;
541 int rv;
542 nghttp2_nv nva1[] = {
543 MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
544 MAKE_NV(":path", "/slashdot"),
545 MAKE_NV("accept-encoding", "gzip, deflate"), MAKE_NV("foo", "bar")};
546 nghttp2_nv nva2[] = {
547 MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
548 MAKE_NV(":path", "/style.css"), MAKE_NV("cookie", "nghttp2=FTW"),
549 MAKE_NV("foo", "bar2")};
550
551 rv = frame_pack_bufs_init(&bufs);
552
553 if (rv != 0) {
554 return;
555 }
556
557 rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm());
558
559 if (rv != 0) {
560 goto deflate_init_fail;
561 }
562
563 rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm());
564
565 if (rv != 0) {
566 goto inflate_init_fail;
567 }
568
569 rv = deflate_inflate(&deflater, &inflater, &bufs, nva1, ARRLEN(nva1),
570 nghttp2_mem_fm());
571
572 if (rv != 0) {
573 goto deflate_hd_fail;
574 }
575
576 rv = deflate_inflate(&deflater, &inflater, &bufs, nva2, ARRLEN(nva2),
577 nghttp2_mem_fm());
578
579 if (rv != 0) {
580 goto deflate_hd_fail;
581 }
582
583 deflate_hd_fail:
584 nghttp2_hd_inflate_free(&inflater);
585 inflate_init_fail:
586 nghttp2_hd_deflate_free(&deflater);
587 deflate_init_fail:
588 nghttp2_bufs_free(&bufs);
589 }
590
test_nghttp2_hd(void)591 void test_nghttp2_hd(void) { TEST_FAILMALLOC_RUN(run_nghttp2_hd); }
592