test_h3_framing.c revision 2f2f4363
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * test_h3_framing.c -- test generation of H3 frames 4 */ 5 6#include <assert.h> 7#include <errno.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11#include <sys/queue.h> 12#include <sys/types.h> 13#include <fcntl.h> 14#include <limits.h> 15#ifndef WIN32 16#include <unistd.h> 17#else 18#include <getopt.h> 19#endif 20 21#include "lsquic.h" 22 23#include "lsquic_packet_common.h" 24#include "lsquic_packet_ietf.h" 25#include "lsquic_alarmset.h" 26#include "lsquic_packet_in.h" 27#include "lsquic_conn_flow.h" 28#include "lsquic_rtt.h" 29#include "lsquic_sfcw.h" 30#include "lsquic_varint.h" 31#include "lsquic_hq.h" 32#include "lsquic_hash.h" 33#include "lsquic_stream.h" 34#include "lsquic_types.h" 35#include "lsquic_malo.h" 36#include "lsquic_mm.h" 37#include "lsquic_conn_public.h" 38#include "lsquic_logger.h" 39#include "lsquic_parse.h" 40#include "lsquic_conn.h" 41#include "lsquic_engine_public.h" 42#include "lsquic_cubic.h" 43#include "lsquic_pacer.h" 44#include "lsquic_senhist.h" 45#include "lsquic_bw_sampler.h" 46#include "lsquic_minmax.h" 47#include "lsquic_bbr.h" 48#include "lsquic_send_ctl.h" 49#include "lsquic_ver_neg.h" 50#include "lsquic_packet_out.h" 51#include "lsquic_enc_sess.h" 52#include "lsqpack.h" 53#include "lsxpack_header.h" 54#include "lsquic_frab_list.h" 55#include "lsquic_qenc_hdl.h" 56#include "lsquic_http1x_if.h" 57#include "lsquic_qdec_hdl.h" 58#include "lsquic_varint.h" 59#include "lsquic_hq.h" 60#include "lsquic_data_in_if.h" 61 62#define MIN(a, b) ((a) < (b) ? (a) : (b)) 63#define MAX(a, b) ((a) > (b) ? (a) : (b)) 64 65static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_ID27); 66 67struct test_ctl_settings 68{ 69 int tcs_schedule_stream_packets_immediately; 70 int tcs_have_delayed_packets; 71 unsigned tcs_can_send; 72 enum buf_packet_type 73 tcs_bp_type; 74 enum packno_bits 75 tcs_guess_packno_bits, 76 tcs_calc_packno_bits; 77}; 78 79 80static struct test_ctl_settings g_ctl_settings; 81 82 83static void 84init_buf (void *buf, size_t sz); 85 86 87/* Set values to default */ 88static void 89init_test_ctl_settings (struct test_ctl_settings *settings) 90{ 91 settings->tcs_schedule_stream_packets_immediately = 1; 92 settings->tcs_have_delayed_packets = 0; 93 settings->tcs_can_send = UINT_MAX; 94 settings->tcs_bp_type = BPT_HIGHEST_PRIO; 95 settings->tcs_guess_packno_bits = PACKNO_BITS_1; 96 settings->tcs_calc_packno_bits = PACKNO_BITS_1; 97} 98 99 100enum packno_bits 101lsquic_send_ctl_calc_packno_bits (struct lsquic_send_ctl *ctl) 102{ 103 return g_ctl_settings.tcs_calc_packno_bits; 104} 105 106 107int 108lsquic_send_ctl_schedule_stream_packets_immediately (struct lsquic_send_ctl *ctl) 109{ 110 return g_ctl_settings.tcs_schedule_stream_packets_immediately; 111} 112 113 114int 115lsquic_send_ctl_have_delayed_packets (const struct lsquic_send_ctl *ctl) 116{ 117 return g_ctl_settings.tcs_have_delayed_packets; 118} 119 120 121int 122lsquic_send_ctl_can_send (struct lsquic_send_ctl *ctl) 123{ 124 return ctl->sc_n_scheduled < g_ctl_settings.tcs_can_send; 125} 126 127 128enum packno_bits 129lsquic_send_ctl_guess_packno_bits (struct lsquic_send_ctl *ctl) 130{ 131 return g_ctl_settings.tcs_guess_packno_bits; 132} 133 134 135enum buf_packet_type 136lsquic_send_ctl_determine_bpt (struct lsquic_send_ctl *ctl, 137 const struct lsquic_stream *stream) 138{ 139 return g_ctl_settings.tcs_bp_type; 140} 141 142 143/* This function is only here to avoid crash in the test: */ 144void 145lsquic_engine_add_conn_to_tickable (struct lsquic_engine_public *enpub, 146 lsquic_conn_t *conn) 147{ 148} 149 150 151static unsigned n_closed; 152static enum stream_ctor_flags stream_ctor_flags = 153 SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH; 154 155struct test_ctx { 156 lsquic_stream_t *stream; 157}; 158 159 160static lsquic_stream_ctx_t * 161on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 162{ 163 struct test_ctx *test_ctx = stream_if_ctx; 164 test_ctx->stream = stream; 165 return NULL; 166} 167 168 169static void 170on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 171{ 172 ++n_closed; 173} 174 175 176const struct lsquic_stream_if stream_if = { 177 .on_new_stream = on_new_stream, 178 .on_close = on_close, 179}; 180 181 182static size_t 183read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t stream_id, 184 unsigned char *const begin, size_t bufsz, uint64_t first_offset, int *p_fin, 185 int fullcheck) 186{ 187 const struct parse_funcs *const pf_local = send_ctl->sc_conn_pub->lconn->cn_pf; 188 unsigned char *p = begin; 189 unsigned char *const end = p + bufsz; 190 const struct frame_rec *frec; 191 struct packet_out_frec_iter pofi; 192 struct lsquic_packet_out *packet_out; 193 struct stream_frame frame; 194 enum quic_frame_type expected_type; 195 int len, fin = 0; 196 197 expected_type = QUIC_FRAME_STREAM; 198 199 TAILQ_FOREACH(packet_out, &send_ctl->sc_scheduled_packets, po_next) 200 for (frec = lsquic_pofi_first(&pofi, packet_out); frec; 201 frec = lsquic_pofi_next(&pofi)) 202 { 203 if (fullcheck) 204 { 205 assert(frec->fe_frame_type == expected_type); 206 if (0 && packet_out->po_packno != 1) 207 { 208 /* First packet may contain two stream frames, do not 209 * check it. 210 */ 211 assert(!lsquic_pofi_next(&pofi)); 212 if (TAILQ_NEXT(packet_out, po_next)) 213 { 214 assert(packet_out->po_data_sz == packet_out->po_n_alloc); 215 assert(frec->fe_len == packet_out->po_data_sz); 216 } 217 } 218 } 219 if (frec->fe_frame_type == expected_type && 220 frec->fe_stream->id == stream_id) 221 { 222 assert(!fin); 223 if (QUIC_FRAME_STREAM == expected_type) 224 len = pf_local->pf_parse_stream_frame(packet_out->po_data + frec->fe_off, 225 packet_out->po_data_sz - frec->fe_off, &frame); 226 else 227 len = pf_local->pf_parse_crypto_frame(packet_out->po_data + frec->fe_off, 228 packet_out->po_data_sz - frec->fe_off, &frame); 229 assert(len > 0); 230 if (QUIC_FRAME_STREAM == expected_type) 231 assert(frame.stream_id == frec->fe_stream->id); 232 else 233 assert(frame.stream_id == ~0ULL); 234 /* Otherwise not enough to copy to: */ 235 assert(end - p >= frame.data_frame.df_size); 236 /* Checks offset ordering: */ 237 assert(frame.data_frame.df_offset == 238 first_offset + (uintptr_t) (p - begin)); 239 if (frame.data_frame.df_fin) 240 { 241 assert(!fin); 242 fin = 1; 243 } 244 memcpy(p, packet_out->po_data + frec->fe_off + len - 245 frame.data_frame.df_size, frame.data_frame.df_size); 246 p += frame.data_frame.df_size; 247 } 248 } 249 250 if (p_fin) 251 *p_fin = fin; 252 return p + bufsz - end; 253} 254 255 256static struct test_ctx test_ctx; 257 258 259struct test_objs { 260 struct lsquic_engine_public eng_pub; 261 struct lsquic_conn lconn; 262 struct lsquic_conn_public conn_pub; 263 struct lsquic_send_ctl send_ctl; 264 struct lsquic_alarmset alset; 265 void *stream_if_ctx; 266 struct ver_neg ver_neg; 267 const struct lsquic_stream_if * 268 stream_if; 269 unsigned initial_stream_window; 270 enum stream_ctor_flags ctor_flags; 271 struct qpack_enc_hdl qeh; 272 struct qpack_dec_hdl qdh; 273 struct lsquic_hset_if hsi_if; 274}; 275 276static int s_ack_written; 277 278static void 279write_ack (struct lsquic_conn *conn, struct lsquic_packet_out *packet_out) 280{ 281 /* We don't need to generate full-blown ACK, as logic in 282 * lsquic_send_ctl_rollback() only looks at po_frame_types. 283 */ 284 packet_out->po_frame_types |= QUIC_FRAME_ACK; 285 s_ack_written = 1; 286} 287 288static int s_can_write_ack; 289 290static int 291can_write_ack (struct lsquic_conn *lconn) 292{ 293 return s_can_write_ack; 294} 295 296 297static struct network_path network_path; 298 299static struct network_path * 300get_network_path (struct lsquic_conn *lconn, const struct sockaddr *sa) 301{ 302 return &network_path; 303} 304 305static enum { 306 SNAPSHOT_STATE_NONE = 0, 307 SNAPSHOT_STATE_TAKEN = 1 << 0, 308 SNAPSHOT_STATE_ROLLED_BACK = 1 << 1, 309} s_snapshot_state; 310 311static void 312ack_snapshot (struct lsquic_conn *lconn, struct ack_state *ack_state) 313{ 314 s_snapshot_state |= SNAPSHOT_STATE_TAKEN; 315} 316 317static void 318ack_rollback (struct lsquic_conn *lconn, struct ack_state *ack_state) 319{ 320 s_snapshot_state |= SNAPSHOT_STATE_ROLLED_BACK; 321} 322 323static const struct conn_iface our_conn_if = 324{ 325 .ci_can_write_ack = can_write_ack, 326 .ci_write_ack = write_ack, 327 .ci_get_path = get_network_path, 328 .ci_ack_snapshot = ack_snapshot, 329 .ci_ack_rollback = ack_rollback, 330}; 331 332 333static void 334init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window, 335 unsigned initial_stream_window, unsigned short packet_sz) 336{ 337 int s; 338 memset(tobjs, 0, sizeof(*tobjs)); 339 LSCONN_INITIALIZE(&tobjs->lconn); 340 tobjs->lconn.cn_pf = g_pf; 341 tobjs->lconn.cn_version = LSQVER_ID27; 342 tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1; 343 network_path.np_pack_size = packet_sz; 344 tobjs->lconn.cn_if = &our_conn_if; 345 lsquic_mm_init(&tobjs->eng_pub.enp_mm); 346 TAILQ_INIT(&tobjs->conn_pub.sending_streams); 347 TAILQ_INIT(&tobjs->conn_pub.read_streams); 348 TAILQ_INIT(&tobjs->conn_pub.write_streams); 349 TAILQ_INIT(&tobjs->conn_pub.service_streams); 350 lsquic_cfcw_init(&tobjs->conn_pub.cfcw, &tobjs->conn_pub, 351 initial_conn_window); 352 lsquic_conn_cap_init(&tobjs->conn_pub.conn_cap, initial_conn_window); 353 lsquic_alarmset_init(&tobjs->alset, 0); 354 tobjs->conn_pub.mm = &tobjs->eng_pub.enp_mm; 355 tobjs->conn_pub.lconn = &tobjs->lconn; 356 tobjs->conn_pub.enpub = &tobjs->eng_pub; 357 tobjs->conn_pub.send_ctl = &tobjs->send_ctl; 358 tobjs->conn_pub.packet_out_malo = 359 lsquic_malo_create(sizeof(struct lsquic_packet_out)); 360 tobjs->conn_pub.path = &network_path; 361 tobjs->initial_stream_window = initial_stream_window; 362 tobjs->eng_pub.enp_settings.es_cc_algo = 1; /* Cubic */ 363 tobjs->eng_pub.enp_hsi_if = &tobjs->hsi_if; 364 lsquic_send_ctl_init(&tobjs->send_ctl, &tobjs->alset, &tobjs->eng_pub, 365 &tobjs->ver_neg, &tobjs->conn_pub, 0); 366 tobjs->send_ctl.sc_cong_u.cubic.cu_cwnd = ~0ull; 367 tobjs->stream_if = &stream_if; 368 tobjs->stream_if_ctx = &test_ctx; 369 tobjs->ctor_flags = stream_ctor_flags; 370 if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS) 371 { 372 lsquic_qeh_init(&tobjs->qeh, &tobjs->lconn); 373 s = lsquic_qeh_settings(&tobjs->qeh, 0, 0, 0, 0); 374 assert(0 == s); 375 tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh; 376 lsquic_qdh_init(&tobjs->qdh, &tobjs->lconn, 0, &tobjs->eng_pub, 0, 0); 377 tobjs->conn_pub.u.ietf.qdh = &tobjs->qdh; 378 } 379} 380 381 382static void 383deinit_test_objs (struct test_objs *tobjs) 384{ 385 assert(!lsquic_malo_first(tobjs->eng_pub.enp_mm.malo.stream_frame)); 386 lsquic_send_ctl_cleanup(&tobjs->send_ctl); 387 lsquic_malo_destroy(tobjs->conn_pub.packet_out_malo); 388 lsquic_mm_cleanup(&tobjs->eng_pub.enp_mm); 389 if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS) 390 lsquic_qeh_cleanup(&tobjs->qeh); 391} 392 393 394static struct lsquic_stream * 395new_stream (struct test_objs *tobjs, unsigned stream_id, uint64_t send_off) 396{ 397 return lsquic_stream_new(stream_id, &tobjs->conn_pub, tobjs->stream_if, 398 tobjs->stream_if_ctx, tobjs->initial_stream_window, send_off, 399 tobjs->ctor_flags); 400} 401 402 403struct packetization_test_stream_ctx 404{ 405 const unsigned char *buf; 406 unsigned len, off, write_size; 407 int flush_after_each_write; 408}; 409 410 411static lsquic_stream_ctx_t * 412packetization_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 413{ 414 lsquic_stream_wantwrite(stream, 1); 415 return stream_if_ctx; 416} 417 418 419static void 420packetization_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 421{ 422} 423 424 425#define RANDOM_WRITE_SIZE ~0U 426 427static unsigned 428calc_n_to_write (unsigned write_size) 429{ 430 if (write_size == RANDOM_WRITE_SIZE) 431 return rand() % 1000 + 1; 432 else 433 return write_size; 434} 435 436 437static void 438packetization_write_as_much_as_you_can (lsquic_stream_t *stream, 439 lsquic_stream_ctx_t *ctx) 440{ 441 struct packetization_test_stream_ctx *const pack_ctx = (void *) ctx; 442 unsigned n_to_write, n_sched; 443 ssize_t n_written; 444 size_t avail; 445 int s; 446 447 while (pack_ctx->off < pack_ctx->len) 448 { 449 n_to_write = calc_n_to_write(pack_ctx->write_size); 450 n_sched = lsquic_send_ctl_n_scheduled(stream->conn_pub->send_ctl); 451 if (n_to_write > pack_ctx->len - pack_ctx->off) 452 n_to_write = pack_ctx->len - pack_ctx->off; 453 n_written = lsquic_stream_write(stream, pack_ctx->buf + pack_ctx->off, 454 n_to_write); 455 if (n_written == 0) 456 { 457 if (n_to_write && SSHS_BEGIN == stream->sm_send_headers_state 458 && lsquic_send_ctl_can_send(stream->conn_pub->send_ctl)) 459 { 460 avail = lsquic_stream_write_avail(stream); 461 assert(avail == 0 462 || lsquic_send_ctl_n_scheduled( 463 stream->conn_pub->send_ctl) > n_sched); 464 } 465 break; 466 } 467 pack_ctx->off += n_written; 468 if (pack_ctx->flush_after_each_write) 469 { 470 s = lsquic_stream_flush(stream); 471 assert(s == 0); 472 } 473 } 474 475 s = lsquic_stream_flush(stream); 476 assert(s == 0); 477 lsquic_stream_wantwrite(stream, 0); 478} 479 480 481static void 482packetization_perform_one_write (lsquic_stream_t *stream, 483 lsquic_stream_ctx_t *ctx) 484{ 485 struct packetization_test_stream_ctx *const pack_ctx = (void *) ctx; 486 unsigned n_to_write, n_sched; 487 ssize_t n_written; 488 size_t avail; 489 int s; 490 491 n_to_write = calc_n_to_write(pack_ctx->write_size); 492 if (n_to_write > pack_ctx->len - pack_ctx->off) 493 n_to_write = pack_ctx->len - pack_ctx->off; 494 n_sched = lsquic_send_ctl_n_scheduled(stream->conn_pub->send_ctl); 495 n_written = lsquic_stream_write(stream, pack_ctx->buf + pack_ctx->off, 496 n_to_write); 497 assert(n_written >= 0); 498 if (n_written == 0 && SSHS_BEGIN == stream->sm_send_headers_state 499 && n_to_write 500 && lsquic_send_ctl_can_send(stream->conn_pub->send_ctl)) 501 { 502 avail = lsquic_stream_write_avail(stream); 503 assert(avail == 0 504 || lsquic_send_ctl_n_scheduled( 505 stream->conn_pub->send_ctl) > n_sched); 506 } 507 pack_ctx->off += n_written; 508 if (pack_ctx->flush_after_each_write) 509 { 510 s = lsquic_stream_flush(stream); 511 assert(s == 0); 512 } 513 if (n_written == 0) 514 lsquic_stream_wantwrite(stream, 0); 515} 516 517 518static const struct lsquic_stream_if packetization_inside_once_stream_if = { 519 .on_new_stream = packetization_on_new_stream, 520 .on_close = packetization_on_close, 521 .on_write = packetization_write_as_much_as_you_can, 522}; 523 524 525static const struct lsquic_stream_if packetization_inside_many_stream_if = { 526 .on_new_stream = packetization_on_new_stream, 527 .on_close = packetization_on_close, 528 .on_write = packetization_perform_one_write, 529}; 530 531 532#define XHDR(name_, value_) .buf = name_ value_, .name_offset = 0, .name_len = sizeof(name_) - 1, .val_offset = sizeof(name_) - 1, .val_len = sizeof(value_) - 1, 533 534static void 535test_hq_framing (int sched_immed, int dispatch_once, unsigned wsize, 536 int flush_after_each_write, size_t conn_limit, 537 unsigned n_packets, unsigned short packet_sz) 538{ 539 struct test_objs tobjs; 540 struct lsquic_stream *stream; 541 size_t nw; 542 int fin, s; 543 unsigned char *buf_in, *buf_out; 544 const size_t buf_in_sz = 0x40000, buf_out_sz = 0x500000; 545 546 /* We'll write headers first after which stream will switch to using 547 * data-framing writer. This is simply so that we don't have to 548 * expose more stream things only for testing. 549 */ 550 struct lsxpack_header header = { XHDR(":method", "GET") }; 551 struct lsquic_http_headers headers = { 1, &header, }; 552 553 buf_in = malloc(buf_in_sz); 554 buf_out = malloc(buf_out_sz); 555 assert(buf_in && buf_out); 556 557 struct packetization_test_stream_ctx packet_stream_ctx = 558 { 559 .buf = buf_in, 560 .off = 0, 561 .len = buf_in_sz, 562 .write_size = wsize, 563 .flush_after_each_write = flush_after_each_write, 564 }; 565 566 init_buf(buf_in, buf_in_sz); 567 568 init_test_ctl_settings(&g_ctl_settings); 569 g_ctl_settings.tcs_schedule_stream_packets_immediately = sched_immed; 570 571 stream_ctor_flags |= SCF_IETF; 572 init_test_objs(&tobjs, conn_limit ? conn_limit : buf_out_sz, buf_out_sz, packet_sz); 573 tobjs.stream_if_ctx = &packet_stream_ctx; 574 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 575 if (sched_immed) 576 { 577 g_ctl_settings.tcs_can_send = n_packets; 578 if (dispatch_once) 579 { 580 tobjs.stream_if = &packetization_inside_once_stream_if; 581 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 582 } 583 else 584 tobjs.stream_if = &packetization_inside_many_stream_if; 585 } 586 else 587 { 588 lsquic_send_ctl_set_max_bpq_count(n_packets); 589 g_ctl_settings.tcs_can_send = INT_MAX; 590 /* Need this for on_new_stream() callback not to mess with 591 * the context, otherwise this is not used. 592 */ 593 tobjs.stream_if = &packetization_inside_many_stream_if; 594 } 595 596 stream = new_stream(&tobjs, 0, buf_out_sz); 597 598 s = lsquic_stream_send_headers(stream, &headers, 0); 599 assert(0 == s); 600 601 if (sched_immed) 602 { 603 lsquic_stream_dispatch_write_events(stream); 604 lsquic_stream_flush(stream); 605 } 606 else 607 { 608 packetization_write_as_much_as_you_can(stream, 609 (void *) &packet_stream_ctx); 610 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 611 lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl, BPT_HIGHEST_PRIO); 612 g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; 613 } 614 lsquic_send_ctl_set_max_bpq_count(10); 615 616 /* Verify written data: */ 617 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 618 0, &fin, 1); 619 if (!conn_limit) 620 assert(nw > buf_in_sz); 621 { /* Remove framing and verify contents */ 622 const unsigned char *src; 623 unsigned char *dst; 624 uint64_t sz; 625 unsigned frame_type; 626 int s; 627 628 src = buf_out; 629 dst = buf_out; 630 while (src < buf_out + nw) 631 { 632 frame_type = *src++; 633 s = vint_read(src, buf_out + buf_out_sz, &sz); 634 assert(s > 0); 635 /* In some rare circumstances it is possible to produce zero-length 636 * DATA frames: 637 * 638 * assert(sz > 0); 639 */ 640 assert(sz < (1 << 14)); 641 src += s; 642 if (src == buf_out + s + 1) 643 { 644 /* Ignore headers */ 645 assert(frame_type == HQFT_HEADERS); 646 src += sz; 647 } 648 else 649 { 650 assert(frame_type == HQFT_DATA); 651 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 652 sz = buf_out + nw - src; 653 memmove(dst, src, sz); 654 dst += sz; 655 src += sz; 656 } 657 } 658 if (!conn_limit) 659 assert(buf_in_sz == (uintptr_t) dst - (uintptr_t) buf_out); 660 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 661 } 662 663 lsquic_stream_destroy(stream); 664 deinit_test_objs(&tobjs); 665 free(buf_in); 666 free(buf_out); 667 668 stream_ctor_flags &= ~SCF_IETF; 669} 670 671 672static void 673main_test_hq_framing (void) 674{ 675 const unsigned wsizes[] = { 1, 2, 3, 7, 10, 50, 100, 201, 211, 1000, 2003, 20000, }; 676 const size_t conn_limits[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 677 14, 15, 16, 17, 18, 19, 20, 21, 30, 31, 32, 33, 63, 64, 128, 200, 255, 678 256, 512, 1024, 2045, 2048, 2049, 3000, 4091, 4096, 4097, 5000, 8192, 679 16 * 1024 - 1, 16 * 1024, 32 * 1024, 33 * 1024, }; 680 const unsigned n_packets[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, UINT_MAX, }; 681 const unsigned short packet_sz[] = { 1252, 1370, 0x1000, 0xFF00, }; 682 unsigned i, j, k, l; 683 int sched_immed, dispatch_once, flush_after_each_write; 684 685 for (sched_immed = 0; sched_immed <= 1; ++sched_immed) 686 for (dispatch_once = 0; dispatch_once <= 1; ++dispatch_once) 687 for (i = 0; i < sizeof(wsizes) / sizeof(wsizes[i]); ++i) 688 for (j = 0; j < sizeof(conn_limits) / sizeof(conn_limits[j]); ++j) 689 for (flush_after_each_write = 0; flush_after_each_write < 2; ++flush_after_each_write) 690 for (k = 0; k < sizeof(n_packets) / sizeof(n_packets[0]); ++k) 691 for (l = 0; l < sizeof(packet_sz) / sizeof(packet_sz[0]); ++l) 692 test_hq_framing(sched_immed, dispatch_once, wsizes[i], flush_after_each_write, conn_limits[j], n_packets[k], packet_sz[l]); 693} 694 695 696/* Instead of the not-very-random testing done in main_test_hq_framing(), 697 * the fuzz-guided testing initializes parameters based on the fuzz input 698 * file. This allows afl-fuzz explore the code paths. 699 */ 700void 701fuzz_guided_hq_framing_testing (const char *input) 702{ 703 /* Range */ /* Bytes from file */ 704 unsigned short packet_sz; /* [200, 0x3FFF] */ /* 2 */ 705 unsigned wsize; /* [1, 20000] */ /* 2 */ 706 unsigned n_packets; /* [1, 255] and UINT_MAX */ /* 1 */ 707 size_t conn_limit; /* [1, 33K] */ /* 2 */ 708 int sched_immed; /* 0 or 1 */ /* 1 */ 709 int dispatch_once; /* 0 or 1 */ /* 0 (same as above) */ 710 int flush_after_each_write; /* 0 or 1 */ /* 0 (same as above) */ 711 /* TOTAL: 8 bytes */ 712 713 FILE *f; 714 size_t nread; 715 uint16_t tmp; 716 unsigned char buf[9]; 717 718 f = fopen(input, "rb"); 719 if (!f) 720 { 721 assert(0); 722 return; 723 } 724 725 nread = fread(buf, 1, sizeof(buf), f); 726 if (nread != 8) 727 goto cleanup; 728 729 memcpy(&tmp, &buf[0], 2); 730 if (tmp < 200) 731 tmp = 200; 732 else if (tmp > IQUIC_MAX_OUT_PACKET_SZ) 733 tmp = IQUIC_MAX_OUT_PACKET_SZ; 734 packet_sz = tmp; 735 736 memcpy(&tmp, &buf[2], 2); 737 if (tmp < 1) 738 tmp = 1; 739 else if (tmp > 20000) 740 tmp = 20000; 741 wsize = tmp; 742 743 if (buf[4]) 744 n_packets = buf[4]; 745 else 746 n_packets = UINT_MAX; 747 748 memcpy(&tmp, &buf[5], 2); 749 if (tmp < 1) 750 tmp = 1; 751 else if (tmp > 33 * 1024) 752 tmp = 33 * 1024; 753 conn_limit = tmp; 754 755 sched_immed = !!(buf[7] & 1); 756 dispatch_once = !!(buf[7] & 2); 757 flush_after_each_write = !!(buf[7] & 4); 758 759 test_hq_framing(sched_immed, dispatch_once, wsize, 760 flush_after_each_write, conn_limit, n_packets, packet_sz); 761 762 cleanup: 763 (void) fclose(f); 764} 765 766 767struct pwritev_stream_ctx 768{ 769 int limit; /* Test limit */ 770 size_t avail; 771 const unsigned char *input; 772 size_t input_sz; 773 ssize_t nw; /* Number of bytes written */ 774}; 775 776 777static ssize_t 778my_preadv (void *user_data, const struct iovec *iov, int iovcnt) 779{ 780 struct pwritev_stream_ctx *const pw_ctx = user_data; 781 const unsigned char *p; 782 size_t ntoread, tocopy; 783 int i; 784 785 ntoread = 0; 786 for (i = 0; i < iovcnt; ++i) 787 ntoread += iov[i].iov_len; 788 789 if (pw_ctx->limit < 0) 790 { 791 if ((size_t) -pw_ctx->limit < ntoread) 792 ntoread -= (size_t) -pw_ctx->limit; 793 } 794 else if ((size_t) pw_ctx->limit < ntoread) 795 ntoread = (size_t) pw_ctx->limit; 796 797 assert(ntoread <= pw_ctx->input_sz); /* Self-check */ 798 799 p = pw_ctx->input; 800 for (i = 0; i < iovcnt; ++i) 801 { 802 tocopy = MIN(iov[i].iov_len, ntoread - (p - pw_ctx->input)); 803 memcpy(iov[i].iov_base, p, tocopy); 804 p += tocopy; 805 if (ntoread == (size_t) (p - pw_ctx->input)) 806 break; 807 } 808 809 assert(ntoread == (size_t) (p - pw_ctx->input)); 810 return (ssize_t) (p - pw_ctx->input); 811} 812 813 814static void 815pwritev_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *ctx) 816{ 817 struct pwritev_stream_ctx *const pw_ctx = (void *) ctx; 818 ssize_t nw; 819 820 nw = lsquic_stream_pwritev(stream, my_preadv, pw_ctx, pw_ctx->input_sz); 821 pw_ctx->nw = nw; 822 lsquic_stream_wantwrite(stream, 0); 823} 824 825 826 827static const struct lsquic_stream_if pwritev_stream_if = { 828 .on_new_stream = packetization_on_new_stream, 829 .on_close = packetization_on_close, 830 .on_write = pwritev_on_write, 831}; 832 833 834static void 835test_pwritev (enum lsquic_version version, int http, int sched_immed, 836 int limit, unsigned short packet_sz, size_t prologue_sz, 837 unsigned n_packets) 838{ 839 struct test_objs tobjs; 840 struct lsquic_stream *stream; 841 size_t nw; 842 int fin, s; 843 unsigned char *buf_in, *buf_out; 844 /* Some values that are large enough: */ 845 const size_t buf_in_sz = MAX(n_packets * packet_sz, 0x1000), 846 buf_out_sz = (float) buf_in_sz * 1.1; 847 const int ietf = (1 << version) & LSQUIC_IETF_VERSIONS; 848 const enum stream_ctor_flags ietf_flags = ietf ? SCF_IETF : 0; 849 850 s_snapshot_state = 0; 851 s_ack_written = 0; 852 853 /* We'll write headers first after which stream will switch to using 854 * data-framing writer. This is simply so that we don't have to 855 * expose more stream things only for testing. 856 */ 857 struct lsxpack_header header = { XHDR(":method", "GET") }; 858 struct lsquic_http_headers headers = { 1, &header, }; 859 860 buf_in = malloc(buf_in_sz); 861 buf_out = malloc(buf_out_sz); 862 assert(buf_in && buf_out); 863 864 struct pwritev_stream_ctx pwritev_stream_ctx = 865 { 866 .input = buf_in + prologue_sz, 867 .input_sz = buf_in_sz - prologue_sz, 868 .limit = limit, 869 }; 870 871 init_buf(buf_in, buf_in_sz); 872 873 init_test_ctl_settings(&g_ctl_settings); 874 g_ctl_settings.tcs_schedule_stream_packets_immediately = sched_immed; 875 876 stream_ctor_flags |= ietf_flags; 877 init_test_objs(&tobjs, buf_out_sz, buf_out_sz, packet_sz); 878 tobjs.lconn.cn_version = version; 879 tobjs.lconn.cn_esf_c = select_esf_common_by_ver(version); 880 tobjs.stream_if_ctx = &pwritev_stream_ctx; 881 tobjs.ctor_flags |= (http ? SCF_HTTP : 0)|ietf_flags; 882 if (sched_immed) 883 { 884 g_ctl_settings.tcs_can_send = n_packets; 885 tobjs.stream_if = &pwritev_stream_if; 886 } 887 else 888 { 889 lsquic_send_ctl_set_max_bpq_count(n_packets); 890 g_ctl_settings.tcs_can_send = INT_MAX; 891 g_ctl_settings.tcs_bp_type = BPT_OTHER_PRIO; 892 /* Need this for on_new_stream() callback not to mess with 893 * the context, otherwise this is not used. 894 */ 895 tobjs.stream_if = &pwritev_stream_if; 896 } 897 898 stream = new_stream(&tobjs, 0, buf_out_sz); 899 900 if (http) 901 { 902 if (ietf) 903 { 904 s = lsquic_stream_send_headers(stream, &headers, 0); 905 assert(0 == s); 906 } 907 else 908 /* Here we fake it in order not to have to set up frame writer. */ 909 stream->stream_flags |= STREAM_HEADERS_SENT; 910 } 911 912 if (prologue_sz) 913 { 914 ssize_t written = lsquic_stream_write(stream, buf_in, prologue_sz); 915 assert(written > 0 && (size_t) written == prologue_sz); 916 } 917 918 if (sched_immed) 919 { 920 lsquic_stream_dispatch_write_events(stream); 921 assert(!(s_snapshot_state & SNAPSHOT_STATE_TAKEN)); 922 // lsquic_stream_flush(stream); 923 } 924 else 925 { 926 pwritev_on_write(stream, (void *) &pwritev_stream_ctx); 927 assert(s_snapshot_state & SNAPSHOT_STATE_TAKEN); 928 if (n_packets > 0 929 && s_ack_written 930 && tobjs.send_ctl.sc_buffered_packets[BPT_OTHER_PRIO].bpq_count == 0) 931 assert(s_snapshot_state & SNAPSHOT_STATE_ROLLED_BACK); 932 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 933 lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl, BPT_OTHER_PRIO); 934 g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; 935 lsquic_send_ctl_set_max_bpq_count(10); 936 } 937 938 assert(pwritev_stream_ctx.nw >= 0); 939 940 /* Verify written data: */ 941 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 942 0, &fin, 1); 943 assert(nw <= buf_in_sz); 944 945 if (ietf && http) 946 { /* Remove framing and verify contents */ 947 const unsigned char *src; 948 unsigned char *dst; 949 uint64_t sz; 950 unsigned frame_type; 951 int s; 952 953 src = buf_out; 954 dst = buf_out; 955 while (src < buf_out + nw) 956 { 957 frame_type = *src++; 958 s = vint_read(src, buf_out + buf_out_sz, &sz); 959 assert(s > 0); 960 /* In some rare circumstances it is possible to produce zero-length 961 * DATA frames: 962 * 963 * assert(sz > 0); 964 */ 965 assert(sz < (1 << 14)); 966 src += s; 967 if (src == buf_out + s + 1) 968 { 969 /* Ignore headers */ 970 assert(frame_type == HQFT_HEADERS); 971 src += sz; 972 } 973 else 974 { 975 assert(frame_type == HQFT_DATA); 976 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 977 sz = buf_out + nw - src; 978 memmove(dst, src, sz); 979 dst += sz; 980 src += sz; 981 } 982 } 983 assert(nw <= buf_in_sz); 984 if (n_packets && pwritev_stream_ctx.nw) 985 { 986 assert((size_t) pwritev_stream_ctx.nw + prologue_sz == (uintptr_t) dst - (uintptr_t) buf_out); 987 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 988 } 989 else 990 assert((uintptr_t) dst - (uintptr_t) buf_out == 0 991 || (uintptr_t) dst - (uintptr_t) buf_out == prologue_sz); 992 } 993 else 994 { 995 assert(nw <= buf_in_sz); 996 assert(nw <= buf_out_sz); 997 if (n_packets && pwritev_stream_ctx.nw) 998 { 999 assert((size_t) pwritev_stream_ctx.nw + prologue_sz == nw); 1000 assert(0 == memcmp(buf_in, buf_out, (size_t) nw)); 1001 } 1002 else 1003 assert(nw == 0 || nw == prologue_sz); 1004 } 1005 1006 lsquic_stream_destroy(stream); 1007 deinit_test_objs(&tobjs); 1008 free(buf_in); 1009 free(buf_out); 1010 1011 stream_ctor_flags &= ~ietf_flags; 1012} 1013 1014 1015static void 1016main_test_pwritev (void) 1017{ 1018 const int limits[] = { INT_MAX, -1, -2, -3, -7, -10, -50, -100, -201, -211, 1019 -1000, -2003, -3000, -4000, -17803, -20000, 16 * 1024, 16 * 1024 - 1, 1020 16 * 1024 - 2, 8000, 273, 65, 63, 10, 5, 1, 0, }; 1021 unsigned n_packets; 1022 const unsigned short packet_sz[] = { 1252, 1370, 0x1000, 0xFF00, }; 1023 const size_t prologues[] = { 0, 17, 238, }; 1024 unsigned i, j, k; 1025 enum lsquic_version version; 1026 int http, sched_immed; 1027 const struct { unsigned iovecs, frames; } combos[] = 1028 { 1029 { 32, 16, }, 1030 { 16, 16, }, 1031 { 16, 8, }, 1032 { 3, 7, }, 1033 { 7, 3, }, 1034 { 100, 100, }, 1035 }, *combo = combos; 1036 1037 s_can_write_ack = 1; 1038 1039 run_test: 1040 for (version = 0; version < N_LSQVER; ++version) 1041 if ((1 << version) & LSQUIC_SUPPORTED_VERSIONS) 1042 for (http = 0; http < 2; ++http) 1043 for (sched_immed = 0; sched_immed <= 1; ++sched_immed) 1044 for (i = 0; i < sizeof(limits) / sizeof(limits[i]); ++i) 1045 for (j = 0; j < sizeof(packet_sz) / sizeof(packet_sz[0]); 1046 ++j) 1047 for (k = 0; k < sizeof(prologues) / sizeof(prologues[0]); ++k) 1048 for (n_packets = 1; n_packets < 21; ++n_packets) 1049 test_pwritev(version, http, sched_immed, 1050 limits[i], packet_sz[j], prologues[k], n_packets); 1051 1052 if (combo < combos + sizeof(combos) / sizeof(combos[0])) 1053 { 1054 lsquic_stream_set_pwritev_params(combo->iovecs, combo->frames); 1055 ++combo; 1056 goto run_test; 1057 } 1058 1059 s_can_write_ack = 0; 1060} 1061 1062 1063/* Instead of the not-very-random testing done in main_test_pwritev(), 1064 * the fuzz-guided testing initializes parameters based on the fuzz input 1065 * file. This allows afl-fuzz explore the code paths. 1066 */ 1067void 1068fuzz_guided_pwritev_testing (const char *input) 1069{ 1070 /* Range */ /* Bytes from file */ 1071 unsigned short packet_sz; /* [1200, 0xFF00] */ /* 2 */ 1072 int limit; /* [INT_MIN, INT_MAX] */ /* 2 */ 1073 unsigned n_packets; /* [0, 255] */ /* 1 */ 1074 unsigned n_iovecs; /* [0, 255] */ /* 1 */ 1075 unsigned n_frames; /* [0, 255] */ /* 1 */ 1076 size_t prologue_sz; /* [0, 170] */ /* 1 */ 1077 enum lsquic_version version;/* [0,7] */ /* 1 */ 1078 int sched_immed; /* 0 or 1 */ /* 1 (same byte) */ 1079 int http; /* 0 or 1 */ /* 1 (same byte) */ 1080 1081 /* TOTAL: 9 bytes */ 1082 1083 FILE *f; 1084 size_t nread; 1085 union { 1086 uint16_t tmp; 1087 int16_t itmp; 1088 } u; 1089 unsigned char buf[10]; 1090 1091 f = fopen(input, "rb"); 1092 if (!f) 1093 { 1094 assert(0); 1095 return; 1096 } 1097 1098 nread = fread(buf, 1, sizeof(buf), f); 1099 if (nread != 9) 1100 goto cleanup; 1101 1102 memcpy(&u.tmp, &buf[0], 2); 1103 if (u.tmp < 1200) 1104 u.tmp = 1200; 1105 else if (u.tmp > 0xFF00) 1106 u.tmp = 0xFF00; 1107 packet_sz = u.tmp; 1108 1109 memcpy(&u.itmp, &buf[2], 2); 1110 if (u.itmp < SHRT_MIN / 2) 1111 limit = INT_MIN; 1112 else if (u.itmp < SHRT_MIN / 4) 1113 limit = 0; 1114 else if (u.itmp > SHRT_MAX / 2) 1115 limit = INT_MAX; 1116 else if (u.itmp > SHRT_MAX / 2) 1117 limit = 0; 1118 else 1119 limit = u.itmp; 1120 1121 n_packets = buf[4]; 1122 n_iovecs = buf[5]; 1123 n_frames = buf[6]; 1124 1125 prologue_sz = buf[7]; 1126 if (prologue_sz > 170) 1127 prologue_sz = 170; 1128 1129 switch (buf[8] & 7) 1130 { 1131 case 0: version = LSQVER_043; break; 1132 case 1: version = LSQVER_046; break; 1133 case 2: version = LSQVER_050; break; 1134 case 3: version = LSQVER_ID27; break; 1135 case 4: version = LSQVER_ID28; break; 1136 default: 1137 case 5: version = LSQVER_ID29; break; 1138 } 1139 1140 sched_immed = !!(buf[8] & 0x08); 1141 http = !!(buf[8] & 0x10); 1142 1143 lsquic_stream_set_pwritev_params(n_iovecs, n_frames); 1144 test_pwritev(version, http, sched_immed, limit, packet_sz, prologue_sz, 1145 n_packets); 1146 1147 cleanup: 1148 (void) fclose(f); 1149} 1150 1151 1152static void 1153test_frame_header_split (unsigned n_packets, unsigned extra_sz, 1154 int add_one_more) 1155{ 1156 struct test_objs tobjs; 1157 struct lsquic_stream *stream; 1158 size_t nw; 1159 int fin, s; 1160 unsigned char *buf_in, *buf_out; 1161 const unsigned wsize = 70; 1162 const size_t buf_in_sz = wsize, buf_out_sz = 0x500000; 1163 1164 struct lsxpack_header header = { XHDR(":method", "GET") }; 1165 struct lsquic_http_headers headers = { 1, &header, }; 1166 1167 buf_in = malloc(buf_in_sz); 1168 buf_out = malloc(buf_out_sz); 1169 assert(buf_in && buf_out); 1170 1171 struct packetization_test_stream_ctx packet_stream_ctx = 1172 { 1173 .buf = buf_in, 1174 .off = 0, 1175 .len = buf_in_sz, 1176 .write_size = wsize, 1177 .flush_after_each_write = 0, 1178 }; 1179 1180 init_buf(buf_in, buf_in_sz); 1181 1182 init_test_ctl_settings(&g_ctl_settings); 1183 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 1184 1185 stream_ctor_flags |= SCF_IETF; 1186 init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252); 1187 tobjs.stream_if_ctx = &packet_stream_ctx; 1188 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1189 1190 g_ctl_settings.tcs_can_send = n_packets; 1191 tobjs.stream_if = &packetization_inside_once_stream_if; 1192 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 1193 1194 struct lsquic_packet_out *const packet_out 1195 = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path); 1196 assert(packet_out); 1197 const size_t pad_size = packet_out->po_n_alloc 1198 - 2 /* STREAM header */ 1199 - 5 /* 3-byte HEADERS frame */ 1200 - extra_sz; 1201 packet_out->po_data_sz = pad_size; 1202 lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out); 1203 1204 stream = new_stream(&tobjs, 0, buf_out_sz); 1205 1206 s = lsquic_stream_send_headers(stream, &headers, 0); 1207 assert(0 == s); 1208 1209 const ssize_t w = lsquic_stream_write(stream, buf_in, buf_in_sz); 1210 assert(w >= 0 && (size_t) w == buf_in_sz); 1211 lsquic_stream_flush(stream); 1212 1213 if (add_one_more) 1214 { 1215 ++g_ctl_settings.tcs_can_send; 1216 lsquic_stream_flush(stream); 1217 } 1218 1219 /* Verify written data: */ 1220 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 1221 0, &fin, 1); 1222 { /* Remove framing and verify contents */ 1223 const unsigned char *src; 1224 unsigned char *dst; 1225 uint64_t sz; 1226 unsigned frame_type; 1227 int s; 1228 1229 src = buf_out; 1230 dst = buf_out; 1231 while (src < buf_out + nw) 1232 { 1233 frame_type = *src++; 1234 s = vint_read(src, buf_out + buf_out_sz, &sz); 1235 assert(s > 0); 1236 assert(sz > 0); 1237 assert(sz < (1 << 14)); 1238 src += s; 1239 if (src == buf_out + s + 1) 1240 { 1241 /* Ignore headers */ 1242 assert(frame_type == HQFT_HEADERS); 1243 src += sz; 1244 } 1245 else 1246 { 1247 assert(frame_type == HQFT_DATA); 1248 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 1249 sz = buf_out + nw - src; 1250 memmove(dst, src, sz); 1251 dst += sz; 1252 src += sz; 1253 } 1254 } 1255 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 1256 } 1257 1258 lsquic_stream_destroy(stream); 1259 deinit_test_objs(&tobjs); 1260 free(buf_in); 1261 free(buf_out); 1262 1263 stream_ctor_flags &= ~SCF_IETF; 1264} 1265 1266 1267static void 1268test_zero_size_frame (void) 1269{ 1270 struct test_objs tobjs; 1271 struct lsquic_stream *stream; 1272 ssize_t w; 1273 size_t nw; 1274 int fin, s; 1275 unsigned char *buf_in, *buf_out; 1276 const unsigned wsize = 7000; 1277 const size_t buf_in_sz = wsize, buf_out_sz = 0x500000; 1278 1279 struct lsxpack_header header = { XHDR(":method", "GET") }; 1280 struct lsquic_http_headers headers = { 1, &header, }; 1281 1282 buf_in = malloc(buf_in_sz); 1283 buf_out = malloc(buf_out_sz); 1284 assert(buf_in && buf_out); 1285 1286 struct packetization_test_stream_ctx packet_stream_ctx = 1287 { 1288 .buf = buf_in, 1289 .off = 0, 1290 .len = buf_in_sz, 1291 .write_size = wsize, 1292 .flush_after_each_write = 0, 1293 }; 1294 1295 init_buf(buf_in, buf_in_sz); 1296 1297 init_test_ctl_settings(&g_ctl_settings); 1298 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 1299 1300 stream_ctor_flags |= SCF_IETF; 1301 init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252); 1302 tobjs.stream_if_ctx = &packet_stream_ctx; 1303 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1304 1305 g_ctl_settings.tcs_can_send = 1; 1306 tobjs.stream_if = &packetization_inside_once_stream_if; 1307 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 1308 1309 struct lsquic_packet_out *const packet_out 1310 = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path); 1311 assert(packet_out); 1312 const size_t pad_size = packet_out->po_n_alloc 1313 - 2 /* STREAM header */ 1314 - 5 /* 3-byte HEADERS frame */ 1315 - 3; 1316 packet_out->po_data_sz = pad_size; 1317 lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out); 1318 1319 stream = new_stream(&tobjs, 0, buf_out_sz); 1320 1321 s = lsquic_stream_send_headers(stream, &headers, 0); 1322 assert(0 == s); 1323 1324 w = lsquic_stream_write(stream, buf_in, buf_in_sz); 1325 assert(w >= 0); 1326 lsquic_stream_flush(stream); 1327 1328 g_ctl_settings.tcs_can_send++; 1329 w = lsquic_stream_write(stream, buf_in, buf_in_sz); 1330 assert(w >= 0); 1331 lsquic_stream_flush(stream); 1332 1333 /* Verify written data: */ 1334 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 1335 0, &fin, 1); 1336 { /* Remove framing and verify contents */ 1337 const unsigned char *src; 1338 unsigned char *dst; 1339 uint64_t sz; 1340 unsigned frame_type; 1341 int s; 1342 1343 src = buf_out; 1344 dst = buf_out; 1345 while (src < buf_out + nw) 1346 { 1347 frame_type = *src++; 1348 s = vint_read(src, buf_out + buf_out_sz, &sz); 1349 assert(s > 0); 1350 assert(sz < (1 << 14)); 1351 src += s; 1352 if (src == buf_out + s + 1) 1353 { 1354 /* Ignore headers */ 1355 assert(frame_type == HQFT_HEADERS); 1356 src += sz; 1357 } 1358 else 1359 { 1360 assert(frame_type == HQFT_DATA); 1361 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 1362 sz = buf_out + nw - src; 1363 memmove(dst, src, sz); 1364 dst += sz; 1365 src += sz; 1366 } 1367 } 1368 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 1369 } 1370 1371 lsquic_stream_destroy(stream); 1372 deinit_test_objs(&tobjs); 1373 free(buf_in); 1374 free(buf_out); 1375 1376 stream_ctor_flags &= ~SCF_IETF; 1377} 1378 1379 1380/* Create a new stream frame. Each stream frame has a real packet_in to 1381 * back it up, just like in real code. The contents of the packet do 1382 * not matter. 1383 */ 1384static stream_frame_t * 1385new_frame_in_ext (struct test_objs *tobjs, size_t off, size_t sz, int fin, 1386 const void *data) 1387{ 1388 lsquic_packet_in_t *packet_in; 1389 stream_frame_t *frame; 1390 1391 assert(sz <= 1370); 1392 1393 packet_in = lsquic_mm_get_packet_in(&tobjs->eng_pub.enp_mm); 1394 if (data) 1395 packet_in->pi_data = (void *) data; 1396 else 1397 { 1398 packet_in->pi_data = lsquic_mm_get_packet_in_buf(&tobjs->eng_pub.enp_mm, 1370); 1399 packet_in->pi_flags |= PI_OWN_DATA; 1400 memset(packet_in->pi_data, 'A', sz); 1401 } 1402 /* This is not how stream frame looks in the packet: we have no 1403 * header. In our test case it does not matter, as we only care 1404 * about stream frame. 1405 */ 1406 packet_in->pi_data_sz = sz; 1407 packet_in->pi_refcnt = 1; 1408 1409 frame = lsquic_malo_get(tobjs->eng_pub.enp_mm.malo.stream_frame); 1410 memset(frame, 0, sizeof(*frame)); 1411 frame->packet_in = packet_in; 1412 frame->data_frame.df_offset = off; 1413 frame->data_frame.df_size = sz; 1414 frame->data_frame.df_data = &packet_in->pi_data[0]; 1415 frame->data_frame.df_fin = fin; 1416 1417 return frame; 1418} 1419 1420 1421/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1422 * returning -1. 1423 */ 1424static void 1425test_reading_zero_size_data_frame (void) 1426{ 1427 struct test_objs tobjs; 1428 struct lsquic_stream *stream; 1429 struct stream_frame *frame; 1430 ssize_t nr; 1431 int s; 1432 unsigned char buf[2]; 1433 1434 init_test_ctl_settings(&g_ctl_settings); 1435 1436 stream_ctor_flags |= SCF_IETF; 1437 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1438 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1439 1440 stream = new_stream(&tobjs, 0, 0x1000); 1441 1442 /* Fake out reading of HEADERS frame: */ 1443 stream->stream_flags |= STREAM_HAVE_UH; 1444 stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */; 1445 stream->sm_hq_filter.hqfi_hist_idx++; 1446 1447 /* One-byte DATA frame */ 1448 frame = new_frame_in_ext(&tobjs, 0, 3, 0, (uint8_t[3]){ 0, 1, 'a', }); 1449 s = lsquic_stream_frame_in(stream, frame); 1450 assert(s == 0); /* Self-check */ 1451 1452 /* Zero-length DATA frame */ 1453 frame = new_frame_in_ext(&tobjs, 3, 2, 0, (uint8_t[2]){ 0, 0, }); 1454 s = lsquic_stream_frame_in(stream, frame); 1455 assert(s == 0); /* Self-check */ 1456 1457 assert(stream->read_offset == 2); /* Self-check */ 1458 1459 /* Read 'a' */ 1460 nr = lsquic_stream_read(stream, buf, 1); 1461 assert(nr == 1); 1462 assert(buf[0] == 'a'); 1463 1464 /* Check that read returns -1 */ 1465 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1466 assert(nr == -1); 1467 1468 /* DATA frame was consumed: */ 1469 assert(stream->read_offset == 5); 1470 1471 lsquic_stream_destroy(stream); 1472 deinit_test_objs(&tobjs); 1473 1474 stream_ctor_flags &= ~SCF_IETF; 1475} 1476 1477 1478/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1479 * returning -1. 1480 */ 1481static void 1482test_reading_zero_size_data_frame_scenario2 (void) 1483{ 1484 struct test_objs tobjs; 1485 struct lsquic_stream *stream; 1486 struct stream_frame *frame; 1487 ssize_t nr; 1488 int s; 1489 unsigned char buf[2]; 1490 1491 init_test_ctl_settings(&g_ctl_settings); 1492 1493 stream_ctor_flags |= SCF_IETF; 1494 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1495 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1496 1497 stream = new_stream(&tobjs, 0, 0x1000); 1498 1499 /* Fake out reading of HEADERS frame: */ 1500 stream->stream_flags |= STREAM_HAVE_UH; 1501 stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */; 1502 stream->sm_hq_filter.hqfi_hist_idx++; 1503 1504 /* Zero-length DATA frame */ 1505 frame = new_frame_in_ext(&tobjs, 0, 5, 0, (uint8_t[5]){ 0, 1, 'a', 0, 0, }); 1506 s = lsquic_stream_frame_in(stream, frame); 1507 assert(s == 0); /* Self-check */ 1508 1509 assert(stream->read_offset == 2); /* Self-check */ 1510 1511 /* Read 'a' */ 1512 nr = lsquic_stream_read(stream, buf, 1); 1513 assert(nr == 1); 1514 assert(buf[0] == 'a'); 1515 1516 /* Check that read returns -1 */ 1517 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1518 assert(nr == -1); 1519 1520 /* DATA frame was consumed: */ 1521 assert(stream->read_offset == 5); 1522 1523 lsquic_stream_destroy(stream); 1524 deinit_test_objs(&tobjs); 1525 1526 stream_ctor_flags &= ~SCF_IETF; 1527} 1528 1529 1530/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1531 * returning -1. 1532 */ 1533static void 1534test_reading_zero_size_data_frame_scenario3 (void) 1535{ 1536 struct test_objs tobjs; 1537 struct lsquic_stream *stream; 1538 struct stream_frame *frame; 1539 ssize_t nr; 1540 int s; 1541 unsigned char buf[2]; 1542 1543 init_test_ctl_settings(&g_ctl_settings); 1544 1545 stream_ctor_flags |= SCF_IETF; 1546 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1547 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1548 1549 stream = new_stream(&tobjs, 0, 0x1000); 1550 1551 /* Fake out reading of HEADERS frame: */ 1552 stream->stream_flags |= STREAM_HAVE_UH; 1553 stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */; 1554 stream->sm_hq_filter.hqfi_hist_idx++; 1555 1556 /* Zero-length DATA frame */ 1557 frame = new_frame_in_ext(&tobjs, 0, 4, 0, (uint8_t[4]){ 0, 1, 'a', 0, }); 1558 s = lsquic_stream_frame_in(stream, frame); 1559 assert(s == 0); /* Self-check */ 1560 1561 assert(stream->read_offset == 2); /* Self-check */ 1562 1563 /* Read 'a' */ 1564 nr = lsquic_stream_read(stream, buf, 1); 1565 assert(nr == 1); 1566 assert(buf[0] == 'a'); 1567 1568 /* Check that read returns -1 */ 1569 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1570 assert(nr == -1); 1571 1572 /* Zero-length DATA frame */ 1573 frame = new_frame_in_ext(&tobjs, 4, 1, 0, (uint8_t[1]){ 0, }); 1574 s = lsquic_stream_frame_in(stream, frame); 1575 assert(s == 0); /* Self-check */ 1576 1577 /* Check that read returns -1 */ 1578 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1579 assert(nr == -1); 1580 1581 /* DATA frame was consumed: */ 1582 assert(stream->read_offset == 5); 1583 1584 lsquic_stream_destroy(stream); 1585 deinit_test_objs(&tobjs); 1586 1587 stream_ctor_flags &= ~SCF_IETF; 1588} 1589 1590 1591int 1592main (int argc, char **argv) 1593{ 1594 const char *fuzz_hq_framing_input = NULL; 1595 const char *fuzz_pwritev_input = NULL; 1596 int opt, add_one_more; 1597 unsigned n_packets, extra_sz; 1598 1599 lsquic_global_init(LSQUIC_GLOBAL_SERVER); 1600 1601 while (-1 != (opt = getopt(argc, argv, "f:p:l:"))) 1602 { 1603 switch (opt) 1604 { 1605 case 'f': 1606 fuzz_hq_framing_input = optarg; 1607 break; 1608 case 'p': 1609 fuzz_pwritev_input = optarg; 1610 break; 1611 case 'l': 1612 lsquic_log_to_fstream(stderr, 0); 1613 lsquic_logger_lopt(optarg); 1614 break; 1615 default: 1616 exit(1); 1617 } 1618 } 1619 1620 init_test_ctl_settings(&g_ctl_settings); 1621 1622 if (fuzz_hq_framing_input) 1623 fuzz_guided_hq_framing_testing(fuzz_hq_framing_input); 1624 else if (fuzz_pwritev_input) 1625 fuzz_guided_pwritev_testing(fuzz_pwritev_input); 1626 else 1627 { 1628 main_test_pwritev(); 1629 return 0; 1630 main_test_hq_framing(); 1631 for (n_packets = 1; n_packets <= 2; ++n_packets) 1632 for (extra_sz = 0; extra_sz <= 2; ++extra_sz) 1633 for (add_one_more = 0; add_one_more <= 1; ++add_one_more) 1634 test_frame_header_split(n_packets, extra_sz, add_one_more); 1635 test_zero_size_frame(); 1636 test_reading_zero_size_data_frame(); 1637 test_reading_zero_size_data_frame_scenario2(); 1638 test_reading_zero_size_data_frame_scenario3(); 1639 } 1640 1641 return 0; 1642} 1643 1644static const char on_being_idle[] = 1645"ON BEING IDLE." 1646"" 1647"Now, this is a subject on which I flatter myself I really am _au fait_." 1648"The gentleman who, when I was young, bathed me at wisdom's font for nine" 1649"guineas a term--no extras--used to say he never knew a boy who could" 1650"do less work in more time; and I remember my poor grandmother once" 1651"incidentally observing, in the course of an instruction upon the use" 1652"of the Prayer-book, that it was highly improbable that I should ever do" 1653"much that I ought not to do, but that she felt convinced beyond a doubt" 1654"that I should leave undone pretty well everything that I ought to do." 1655"" 1656"I am afraid I have somewhat belied half the dear old lady's prophecy." 1657"Heaven help me! I have done a good many things that I ought not to have" 1658"done, in spite of my laziness. But I have fully confirmed the accuracy" 1659"of her judgment so far as neglecting much that I ought not to have" 1660"neglected is concerned. Idling always has been my strong point. I take" 1661"no credit to myself in the matter--it is a gift. Few possess it. There" 1662"are plenty of lazy people and plenty of slow-coaches, but a genuine" 1663"idler is a rarity. He is not a man who slouches about with his hands in" 1664"his pockets. On the contrary, his most startling characteristic is that" 1665"he is always intensely busy." 1666"" 1667"It is impossible to enjoy idling thoroughly unless one has plenty of" 1668"work to do. There is no fun in doing nothing when you have nothing to" 1669"do. Wasting time is merely an occupation then, and a most exhausting" 1670"one. Idleness, like kisses, to be sweet must be stolen." 1671"" 1672"Many years ago, when I was a young man, I was taken very ill--I never" 1673"could see myself that much was the matter with me, except that I had" 1674"a beastly cold. But I suppose it was something very serious, for the" 1675"doctor said that I ought to have come to him a month before, and that" 1676"if it (whatever it was) had gone on for another week he would not have" 1677"answered for the consequences. It is an extraordinary thing, but I" 1678"never knew a doctor called into any case yet but what it transpired" 1679"that another day's delay would have rendered cure hopeless. Our medical" 1680"guide, philosopher, and friend is like the hero in a melodrama--he" 1681"always comes upon the scene just, and only just, in the nick of time. It" 1682"is Providence, that is what it is." 1683"" 1684"Well, as I was saying, I was very ill and was ordered to Buxton for a" 1685"month, with strict injunctions to do nothing whatever all the while" 1686"that I was there. \"Rest is what you require,\" said the doctor, \"perfect" 1687"rest.\"" 1688"" 1689"It seemed a delightful prospect. \"This man evidently understands my" 1690"complaint,\" said I, and I pictured to myself a glorious time--a four" 1691"weeks' _dolce far niente_ with a dash of illness in it. Not too much" 1692"illness, but just illness enough--just sufficient to give it the flavor" 1693"of suffering and make it poetical. I should get up late, sip chocolate," 1694"and have my breakfast in slippers and a dressing-gown. I should lie out" 1695"in the garden in a hammock and read sentimental novels with a melancholy" 1696"ending, until the books should fall from my listless hand, and I should" 1697"recline there, dreamily gazing into the deep blue of the firmament," 1698"watching the fleecy clouds floating like white-sailed ships across" 1699"its depths, and listening to the joyous song of the birds and the low" 1700"rustling of the trees. Or, on becoming too weak to go out of doors," 1701"I should sit propped up with pillows at the open window of the" 1702"ground-floor front, and look wasted and interesting, so that all the" 1703"pretty girls would sigh as they passed by." 1704"" 1705"And twice a day I should go down in a Bath chair to the Colonnade to" 1706"drink the waters. Oh, those waters! I knew nothing about them then," 1707"and was rather taken with the idea. \"Drinking the waters\" sounded" 1708"fashionable and Queen Anne-fied, and I thought I should like them. But," 1709"ugh! after the first three or four mornings! Sam Weller's description of" 1710"them as \"having a taste of warm flat-irons\" conveys only a faint idea of" 1711"their hideous nauseousness. If anything could make a sick man get well" 1712"quickly, it would be the knowledge that he must drink a glassful of them" 1713"every day until he was recovered. I drank them neat for six consecutive" 1714"days, and they nearly killed me; but after then I adopted the plan of" 1715"taking a stiff glass of brandy-and-water immediately on the top of them," 1716"and found much relief thereby. I have been informed since, by various" 1717"eminent medical gentlemen, that the alcohol must have entirely" 1718"counteracted the effects of the chalybeate properties contained in the" 1719"water. I am glad I was lucky enough to hit upon the right thing." 1720; 1721 1722static void 1723init_buf (void *buf, size_t sz) 1724{ 1725 unsigned char *p = buf; 1726 unsigned char *const end = (unsigned char*)buf + sz; 1727 size_t n; 1728 1729 while (p < end) 1730 { 1731 n = end - p; 1732 if (sizeof(on_being_idle) - 1 < n) 1733 n = sizeof(on_being_idle) - 1; 1734 memcpy(p, on_being_idle, n); 1735 p +=n; 1736 } 1737 1738 assert(p == end); 1739} 1740