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