test_h3_framing.c revision b1a7c3f9
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 void 1155test_frame_header_split (unsigned n_packets, unsigned extra_sz, 1156 int add_one_more) 1157{ 1158 struct test_objs tobjs; 1159 struct lsquic_stream *stream; 1160 size_t nw; 1161 int fin, s; 1162 unsigned char *buf_in, *buf_out; 1163 const unsigned wsize = 70; 1164 const size_t buf_in_sz = wsize, buf_out_sz = 0x500000; 1165 1166 struct lsxpack_header header = { XHDR(":method", "GET") }; 1167 struct lsquic_http_headers headers = { 1, &header, }; 1168 1169 buf_in = malloc(buf_in_sz); 1170 buf_out = malloc(buf_out_sz); 1171 assert(buf_in && buf_out); 1172 1173 struct packetization_test_stream_ctx packet_stream_ctx = 1174 { 1175 .buf = buf_in, 1176 .off = 0, 1177 .len = buf_in_sz, 1178 .write_size = wsize, 1179 .flush_after_each_write = 0, 1180 }; 1181 1182 init_buf(buf_in, buf_in_sz); 1183 1184 init_test_ctl_settings(&g_ctl_settings); 1185 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 1186 1187 stream_ctor_flags |= SCF_IETF; 1188 init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252); 1189 tobjs.stream_if_ctx = &packet_stream_ctx; 1190 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1191 1192 g_ctl_settings.tcs_can_send = n_packets; 1193 tobjs.stream_if = &packetization_inside_once_stream_if; 1194 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 1195 1196 struct lsquic_packet_out *const packet_out 1197 = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path); 1198 assert(packet_out); 1199 const size_t pad_size = packet_out->po_n_alloc 1200 - 2 /* STREAM header */ 1201 - 5 /* 3-byte HEADERS frame */ 1202 - extra_sz; 1203 packet_out->po_data_sz = pad_size; 1204 lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out); 1205 1206 stream = new_stream(&tobjs, 0, buf_out_sz); 1207 1208 s = lsquic_stream_send_headers(stream, &headers, 0); 1209 assert(0 == s); 1210 1211 const ssize_t w = lsquic_stream_write(stream, buf_in, buf_in_sz); 1212 assert(w >= 0 && (size_t) w == buf_in_sz); 1213 lsquic_stream_flush(stream); 1214 1215 if (add_one_more) 1216 { 1217 ++g_ctl_settings.tcs_can_send; 1218 lsquic_stream_flush(stream); 1219 } 1220 1221 /* Verify written data: */ 1222 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 1223 0, &fin, 1); 1224 { /* Remove framing and verify contents */ 1225 const unsigned char *src; 1226 unsigned char *dst; 1227 uint64_t sz; 1228 unsigned frame_type; 1229 int s; 1230 1231 src = buf_out; 1232 dst = buf_out; 1233 while (src < buf_out + nw) 1234 { 1235 frame_type = *src++; 1236 s = vint_read(src, buf_out + buf_out_sz, &sz); 1237 assert(s > 0); 1238 assert(sz > 0); 1239 assert(sz < (1 << 14)); 1240 src += s; 1241 if (src == buf_out + s + 1) 1242 { 1243 /* Ignore headers */ 1244 assert(frame_type == HQFT_HEADERS); 1245 src += sz; 1246 } 1247 else 1248 { 1249 assert(frame_type == HQFT_DATA); 1250 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 1251 sz = buf_out + nw - src; 1252 memmove(dst, src, sz); 1253 dst += sz; 1254 src += sz; 1255 } 1256 } 1257 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 1258 } 1259 1260 lsquic_stream_destroy(stream); 1261 deinit_test_objs(&tobjs); 1262 free(buf_in); 1263 free(buf_out); 1264 1265 stream_ctor_flags &= ~SCF_IETF; 1266} 1267 1268 1269static void 1270test_zero_size_frame (void) 1271{ 1272 struct test_objs tobjs; 1273 struct lsquic_stream *stream; 1274 ssize_t w; 1275 size_t nw; 1276 int fin, s; 1277 unsigned char *buf_in, *buf_out; 1278 const unsigned wsize = 7000; 1279 const size_t buf_in_sz = wsize, buf_out_sz = 0x500000; 1280 1281 struct lsxpack_header header = { XHDR(":method", "GET") }; 1282 struct lsquic_http_headers headers = { 1, &header, }; 1283 1284 buf_in = malloc(buf_in_sz); 1285 buf_out = malloc(buf_out_sz); 1286 assert(buf_in && buf_out); 1287 1288 struct packetization_test_stream_ctx packet_stream_ctx = 1289 { 1290 .buf = buf_in, 1291 .off = 0, 1292 .len = buf_in_sz, 1293 .write_size = wsize, 1294 .flush_after_each_write = 0, 1295 }; 1296 1297 init_buf(buf_in, buf_in_sz); 1298 1299 init_test_ctl_settings(&g_ctl_settings); 1300 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 1301 1302 stream_ctor_flags |= SCF_IETF; 1303 init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252); 1304 tobjs.stream_if_ctx = &packet_stream_ctx; 1305 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1306 1307 g_ctl_settings.tcs_can_send = 1; 1308 tobjs.stream_if = &packetization_inside_once_stream_if; 1309 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 1310 1311 struct lsquic_packet_out *const packet_out 1312 = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path); 1313 assert(packet_out); 1314 const size_t pad_size = packet_out->po_n_alloc 1315 - 2 /* STREAM header */ 1316 - 5 /* 3-byte HEADERS frame */ 1317 - 3; 1318 packet_out->po_data_sz = pad_size; 1319 lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out); 1320 1321 stream = new_stream(&tobjs, 0, buf_out_sz); 1322 1323 s = lsquic_stream_send_headers(stream, &headers, 0); 1324 assert(0 == s); 1325 1326 w = lsquic_stream_write(stream, buf_in, buf_in_sz); 1327 assert(w >= 0); 1328 lsquic_stream_flush(stream); 1329 1330 g_ctl_settings.tcs_can_send++; 1331 w = lsquic_stream_write(stream, buf_in, buf_in_sz); 1332 assert(w >= 0); 1333 lsquic_stream_flush(stream); 1334 1335 /* Verify written data: */ 1336 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 1337 0, &fin, 1); 1338 { /* Remove framing and verify contents */ 1339 const unsigned char *src; 1340 unsigned char *dst; 1341 uint64_t sz; 1342 unsigned frame_type; 1343 int s; 1344 1345 src = buf_out; 1346 dst = buf_out; 1347 while (src < buf_out + nw) 1348 { 1349 frame_type = *src++; 1350 s = vint_read(src, buf_out + buf_out_sz, &sz); 1351 assert(s > 0); 1352 assert(sz < (1 << 14)); 1353 src += s; 1354 if (src == buf_out + s + 1) 1355 { 1356 /* Ignore headers */ 1357 assert(frame_type == HQFT_HEADERS); 1358 src += sz; 1359 } 1360 else 1361 { 1362 assert(frame_type == HQFT_DATA); 1363 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 1364 sz = buf_out + nw - src; 1365 memmove(dst, src, sz); 1366 dst += sz; 1367 src += sz; 1368 } 1369 } 1370 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 1371 } 1372 1373 lsquic_stream_destroy(stream); 1374 deinit_test_objs(&tobjs); 1375 free(buf_in); 1376 free(buf_out); 1377 1378 stream_ctor_flags &= ~SCF_IETF; 1379} 1380 1381 1382/* Create a new stream frame. Each stream frame has a real packet_in to 1383 * back it up, just like in real code. The contents of the packet do 1384 * not matter. 1385 */ 1386static stream_frame_t * 1387new_frame_in_ext (struct test_objs *tobjs, size_t off, size_t sz, int fin, 1388 const void *data) 1389{ 1390 lsquic_packet_in_t *packet_in; 1391 stream_frame_t *frame; 1392 1393 assert(sz <= 1370); 1394 1395 packet_in = lsquic_mm_get_packet_in(&tobjs->eng_pub.enp_mm); 1396 if (data) 1397 packet_in->pi_data = (void *) data; 1398 else 1399 { 1400 packet_in->pi_data = lsquic_mm_get_packet_in_buf(&tobjs->eng_pub.enp_mm, 1370); 1401 packet_in->pi_flags |= PI_OWN_DATA; 1402 memset(packet_in->pi_data, 'A', sz); 1403 } 1404 /* This is not how stream frame looks in the packet: we have no 1405 * header. In our test case it does not matter, as we only care 1406 * about stream frame. 1407 */ 1408 packet_in->pi_data_sz = sz; 1409 packet_in->pi_refcnt = 1; 1410 1411 frame = lsquic_malo_get(tobjs->eng_pub.enp_mm.malo.stream_frame); 1412 memset(frame, 0, sizeof(*frame)); 1413 frame->packet_in = packet_in; 1414 frame->data_frame.df_offset = off; 1415 frame->data_frame.df_size = sz; 1416 frame->data_frame.df_data = &packet_in->pi_data[0]; 1417 frame->data_frame.df_fin = fin; 1418 1419 return frame; 1420} 1421 1422 1423/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1424 * returning -1. 1425 */ 1426static void 1427test_reading_zero_size_data_frame (void) 1428{ 1429 struct test_objs tobjs; 1430 struct lsquic_stream *stream; 1431 struct stream_frame *frame; 1432 ssize_t nr; 1433 int s; 1434 unsigned char buf[2]; 1435 1436 init_test_ctl_settings(&g_ctl_settings); 1437 1438 stream_ctor_flags |= SCF_IETF; 1439 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1440 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1441 1442 stream = new_stream(&tobjs, 0, 0x1000); 1443 1444 /* Fake out reading of HEADERS frame: */ 1445 stream->stream_flags |= STREAM_HAVE_UH; 1446 stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */; 1447 stream->sm_hq_filter.hqfi_hist_idx++; 1448 1449 /* One-byte DATA frame */ 1450 frame = new_frame_in_ext(&tobjs, 0, 3, 0, (uint8_t[3]){ 0, 1, 'a', }); 1451 s = lsquic_stream_frame_in(stream, frame); 1452 assert(s == 0); /* Self-check */ 1453 1454 /* Zero-length DATA frame */ 1455 frame = new_frame_in_ext(&tobjs, 3, 2, 0, (uint8_t[2]){ 0, 0, }); 1456 s = lsquic_stream_frame_in(stream, frame); 1457 assert(s == 0); /* Self-check */ 1458 1459 assert(stream->read_offset == 2); /* Self-check */ 1460 1461 /* Read 'a' */ 1462 nr = lsquic_stream_read(stream, buf, 1); 1463 assert(nr == 1); 1464 assert(buf[0] == 'a'); 1465 1466 /* Check that read returns -1 */ 1467 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1468 assert(nr == -1); 1469 1470 /* DATA frame was consumed: */ 1471 assert(stream->read_offset == 5); 1472 1473 lsquic_stream_destroy(stream); 1474 deinit_test_objs(&tobjs); 1475 1476 stream_ctor_flags &= ~SCF_IETF; 1477} 1478 1479 1480/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1481 * returning -1. 1482 */ 1483static void 1484test_reading_zero_size_data_frame_scenario2 (void) 1485{ 1486 struct test_objs tobjs; 1487 struct lsquic_stream *stream; 1488 struct stream_frame *frame; 1489 ssize_t nr; 1490 int s; 1491 unsigned char buf[2]; 1492 1493 init_test_ctl_settings(&g_ctl_settings); 1494 1495 stream_ctor_flags |= SCF_IETF; 1496 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1497 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1498 1499 stream = new_stream(&tobjs, 0, 0x1000); 1500 1501 /* Fake out reading of HEADERS frame: */ 1502 stream->stream_flags |= STREAM_HAVE_UH; 1503 stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */; 1504 stream->sm_hq_filter.hqfi_hist_idx++; 1505 1506 /* Zero-length DATA frame */ 1507 frame = new_frame_in_ext(&tobjs, 0, 5, 0, (uint8_t[5]){ 0, 1, 'a', 0, 0, }); 1508 s = lsquic_stream_frame_in(stream, frame); 1509 assert(s == 0); /* Self-check */ 1510 1511 assert(stream->read_offset == 2); /* Self-check */ 1512 1513 /* Read 'a' */ 1514 nr = lsquic_stream_read(stream, buf, 1); 1515 assert(nr == 1); 1516 assert(buf[0] == 'a'); 1517 1518 /* Check that read returns -1 */ 1519 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1520 assert(nr == -1); 1521 1522 /* DATA frame was consumed: */ 1523 assert(stream->read_offset == 5); 1524 1525 lsquic_stream_destroy(stream); 1526 deinit_test_objs(&tobjs); 1527 1528 stream_ctor_flags &= ~SCF_IETF; 1529} 1530 1531 1532/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1533 * returning -1. 1534 */ 1535static void 1536test_reading_zero_size_data_frame_scenario3 (void) 1537{ 1538 struct test_objs tobjs; 1539 struct lsquic_stream *stream; 1540 struct stream_frame *frame; 1541 ssize_t nr; 1542 int s; 1543 unsigned char buf[2]; 1544 1545 init_test_ctl_settings(&g_ctl_settings); 1546 1547 stream_ctor_flags |= SCF_IETF; 1548 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1549 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1550 1551 stream = new_stream(&tobjs, 0, 0x1000); 1552 1553 /* Fake out reading of HEADERS frame: */ 1554 stream->stream_flags |= STREAM_HAVE_UH; 1555 stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */; 1556 stream->sm_hq_filter.hqfi_hist_idx++; 1557 1558 /* Zero-length DATA frame */ 1559 frame = new_frame_in_ext(&tobjs, 0, 4, 0, (uint8_t[4]){ 0, 1, 'a', 0, }); 1560 s = lsquic_stream_frame_in(stream, frame); 1561 assert(s == 0); /* Self-check */ 1562 1563 assert(stream->read_offset == 2); /* Self-check */ 1564 1565 /* Read 'a' */ 1566 nr = lsquic_stream_read(stream, buf, 1); 1567 assert(nr == 1); 1568 assert(buf[0] == 'a'); 1569 1570 /* Check that read returns -1 */ 1571 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1572 assert(nr == -1); 1573 1574 /* Zero-length DATA frame */ 1575 frame = new_frame_in_ext(&tobjs, 4, 1, 0, (uint8_t[1]){ 0, }); 1576 s = lsquic_stream_frame_in(stream, frame); 1577 assert(s == 0); /* Self-check */ 1578 1579 /* Check that read returns -1 */ 1580 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1581 assert(nr == -1); 1582 1583 /* DATA frame was consumed: */ 1584 assert(stream->read_offset == 5); 1585 1586 lsquic_stream_destroy(stream); 1587 deinit_test_objs(&tobjs); 1588 1589 stream_ctor_flags &= ~SCF_IETF; 1590} 1591 1592 1593int 1594main (int argc, char **argv) 1595{ 1596 const char *fuzz_hq_framing_input = NULL; 1597 const char *fuzz_pwritev_input = NULL; 1598 int opt, add_one_more; 1599 unsigned n_packets, extra_sz; 1600 1601 lsquic_global_init(LSQUIC_GLOBAL_SERVER); 1602 1603 while (-1 != (opt = getopt(argc, argv, "f:p:l:"))) 1604 { 1605 switch (opt) 1606 { 1607 case 'f': 1608 fuzz_hq_framing_input = optarg; 1609 break; 1610 case 'p': 1611 fuzz_pwritev_input = optarg; 1612 break; 1613 case 'l': 1614 lsquic_log_to_fstream(stderr, 0); 1615 lsquic_logger_lopt(optarg); 1616 break; 1617 default: 1618 exit(1); 1619 } 1620 } 1621 1622 init_test_ctl_settings(&g_ctl_settings); 1623 1624 if (fuzz_hq_framing_input) 1625 fuzz_guided_hq_framing_testing(fuzz_hq_framing_input); 1626 else if (fuzz_pwritev_input) 1627 fuzz_guided_pwritev_testing(fuzz_pwritev_input); 1628 else 1629 { 1630 main_test_pwritev(); 1631 main_test_hq_framing(); 1632 for (n_packets = 1; n_packets <= 2; ++n_packets) 1633 for (extra_sz = 0; extra_sz <= 2; ++extra_sz) 1634 for (add_one_more = 0; add_one_more <= 1; ++add_one_more) 1635 test_frame_header_split(n_packets, extra_sz, add_one_more); 1636 test_zero_size_frame(); 1637 test_reading_zero_size_data_frame(); 1638 test_reading_zero_size_data_frame_scenario2(); 1639 test_reading_zero_size_data_frame_scenario3(); 1640 } 1641 1642 return 0; 1643} 1644 1645static const char on_being_idle[] = 1646"ON BEING IDLE." 1647"" 1648"Now, this is a subject on which I flatter myself I really am _au fait_." 1649"The gentleman who, when I was young, bathed me at wisdom's font for nine" 1650"guineas a term--no extras--used to say he never knew a boy who could" 1651"do less work in more time; and I remember my poor grandmother once" 1652"incidentally observing, in the course of an instruction upon the use" 1653"of the Prayer-book, that it was highly improbable that I should ever do" 1654"much that I ought not to do, but that she felt convinced beyond a doubt" 1655"that I should leave undone pretty well everything that I ought to do." 1656"" 1657"I am afraid I have somewhat belied half the dear old lady's prophecy." 1658"Heaven help me! I have done a good many things that I ought not to have" 1659"done, in spite of my laziness. But I have fully confirmed the accuracy" 1660"of her judgment so far as neglecting much that I ought not to have" 1661"neglected is concerned. Idling always has been my strong point. I take" 1662"no credit to myself in the matter--it is a gift. Few possess it. There" 1663"are plenty of lazy people and plenty of slow-coaches, but a genuine" 1664"idler is a rarity. He is not a man who slouches about with his hands in" 1665"his pockets. On the contrary, his most startling characteristic is that" 1666"he is always intensely busy." 1667"" 1668"It is impossible to enjoy idling thoroughly unless one has plenty of" 1669"work to do. There is no fun in doing nothing when you have nothing to" 1670"do. Wasting time is merely an occupation then, and a most exhausting" 1671"one. Idleness, like kisses, to be sweet must be stolen." 1672"" 1673"Many years ago, when I was a young man, I was taken very ill--I never" 1674"could see myself that much was the matter with me, except that I had" 1675"a beastly cold. But I suppose it was something very serious, for the" 1676"doctor said that I ought to have come to him a month before, and that" 1677"if it (whatever it was) had gone on for another week he would not have" 1678"answered for the consequences. It is an extraordinary thing, but I" 1679"never knew a doctor called into any case yet but what it transpired" 1680"that another day's delay would have rendered cure hopeless. Our medical" 1681"guide, philosopher, and friend is like the hero in a melodrama--he" 1682"always comes upon the scene just, and only just, in the nick of time. It" 1683"is Providence, that is what it is." 1684"" 1685"Well, as I was saying, I was very ill and was ordered to Buxton for a" 1686"month, with strict injunctions to do nothing whatever all the while" 1687"that I was there. \"Rest is what you require,\" said the doctor, \"perfect" 1688"rest.\"" 1689"" 1690"It seemed a delightful prospect. \"This man evidently understands my" 1691"complaint,\" said I, and I pictured to myself a glorious time--a four" 1692"weeks' _dolce far niente_ with a dash of illness in it. Not too much" 1693"illness, but just illness enough--just sufficient to give it the flavor" 1694"of suffering and make it poetical. I should get up late, sip chocolate," 1695"and have my breakfast in slippers and a dressing-gown. I should lie out" 1696"in the garden in a hammock and read sentimental novels with a melancholy" 1697"ending, until the books should fall from my listless hand, and I should" 1698"recline there, dreamily gazing into the deep blue of the firmament," 1699"watching the fleecy clouds floating like white-sailed ships across" 1700"its depths, and listening to the joyous song of the birds and the low" 1701"rustling of the trees. Or, on becoming too weak to go out of doors," 1702"I should sit propped up with pillows at the open window of the" 1703"ground-floor front, and look wasted and interesting, so that all the" 1704"pretty girls would sigh as they passed by." 1705"" 1706"And twice a day I should go down in a Bath chair to the Colonnade to" 1707"drink the waters. Oh, those waters! I knew nothing about them then," 1708"and was rather taken with the idea. \"Drinking the waters\" sounded" 1709"fashionable and Queen Anne-fied, and I thought I should like them. But," 1710"ugh! after the first three or four mornings! Sam Weller's description of" 1711"them as \"having a taste of warm flat-irons\" conveys only a faint idea of" 1712"their hideous nauseousness. If anything could make a sick man get well" 1713"quickly, it would be the knowledge that he must drink a glassful of them" 1714"every day until he was recovered. I drank them neat for six consecutive" 1715"days, and they nearly killed me; but after then I adopted the plan of" 1716"taking a stiff glass of brandy-and-water immediately on the top of them," 1717"and found much relief thereby. I have been informed since, by various" 1718"eminent medical gentlemen, that the alcohol must have entirely" 1719"counteracted the effects of the chalybeate properties contained in the" 1720"water. I am glad I was lucky enough to hit upon the right thing." 1721; 1722 1723static void 1724init_buf (void *buf, size_t sz) 1725{ 1726 unsigned char *p = buf; 1727 unsigned char *const end = (unsigned char*)buf + sz; 1728 size_t n; 1729 1730 while (p < end) 1731 { 1732 n = end - p; 1733 if (sizeof(on_being_idle) - 1 < n) 1734 n = sizeof(on_being_idle) - 1; 1735 memcpy(p, on_being_idle, n); 1736 p +=n; 1737 } 1738 1739 assert(p == end); 1740} 1741