From bc12dad041594366a232a0022a04024b6254390d Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 8 Jan 2025 18:14:58 +0100 Subject: [PATCH] quic_record_append: return correct code 0-return from quic_record_append is an error. `quic_record_complete(qr) || len == 0` is not an error condition. We should return as normal on success. The issue is that passing in buffers with length 1 then 3 causes `qr_length` (in `quic_record_make`) to return 0. Then when `quic_record_append` gets called the `len` gets consumed by the first `if` and `len == 0` is true. This causes the error return which is not correct behaviour. Reported in https://github.com/wolfSSL/wolfssl/issues/8156. Reproducing is a bit tricky. I couldn't get the docker to work. First setup ngtcp2 as described in https://github.com/ngtcp2/ngtcp2/pkgs/container/ngtcp2-interop. The Relevant steps are (I tested with master/main branches of all libs): ``` $ git clone --depth 1 -b v5.7.4-stable https://github.com/wolfSSL/wolfssl $ cd wolfssl $ autoreconf -i $ # For wolfSSL < v5.6.6, append --enable-quic. $ ./configure --prefix=$PWD/build \ --enable-all --enable-aesni --enable-harden --enable-keylog-export \ --disable-ech $ make -j$(nproc) $ make install $ cd .. $ git clone --recursive https://github.com/ngtcp2/nghttp3 $ cd nghttp3 $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only $ make -j$(nproc) check $ make install $ cd .. $ git clone --recursive https://github.com/ngtcp2/ngtcp2 $ cd ngtcp2 $ autoreconf -i $ # For Mac users who have installed libev with MacPorts, append $ # LIBEV_CFLAGS="-I/opt/local/include" LIBEV_LIBS="-L/opt/local/lib -lev" $ ./configure PKG_CONFIG_PATH=$PWD/../wolfssl/build/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig \ --with-wolfssl $ make -j$(nproc) check ``` Download and unzip https://github.com/user-attachments/files/17621329/failing.pcap.zip From the ngtcp2 dir: ``` ./examples/wsslserver 127.0.0.1 44433 /path/to/wolfssl/certs/server-key.pem /path/to/wolfssl/certs/server-cert.pem ``` Then run the following python script (`failing.pcap` has to be available in the running dir) (probably needs to be run as `sudo`): ``` from scapy.utils import rdpcap, PcapNgReader from scapy.all import * reader = PcapNgReader("failing.pcap") for i in reader: p = i[IP] p.dport = 44433 p.dst = "127.0.0.1" p[UDP].chksum=0 p.display() send(p) ``` Then observe the log line: ``` I00000000 0xa48accb7b49ec1556ac7111c64d3a4572a81 frm tx 625216795 Initial CONNECTION_CLOSE(0x1c) error_code=CRYPTO_ERROR(0x100) frame_type=0 reason_len=0 reason=[] ``` You can also use `gdb` and place a break inside the following section in `wolfssl/src/quic.c`. ``` if (quic_record_complete(qr) || len == 0) { return 0; } ``` --- src/quic.c | 18 ++++++++---------- tests/quic.c | 5 ++++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/quic.c b/src/quic.c index 64cf14fc86..918b96751d 100644 --- a/src/quic.c +++ b/src/quic.c @@ -154,17 +154,15 @@ static int quic_record_append(WOLFSSL *ssl, QuicRecord *qr, const uint8_t *data, } } - if (quic_record_complete(qr) || len == 0) { - return 0; - } - - missing = qr->len - qr->end; - if (len > missing) { - len = missing; + if (!quic_record_complete(qr) && len != 0) { + missing = qr->len - qr->end; + if (len > missing) { + len = missing; + } + XMEMCPY(qr->data + qr->end, data, len); + qr->end += (word32)len; + consumed += len; } - XMEMCPY(qr->data + qr->end, data, len); - qr->end += (word32)len; - consumed += len; cleanup: *pconsumed = (ret == WOLFSSL_SUCCESS) ? consumed : 0; diff --git a/tests/quic.c b/tests/quic.c index c58625db48..1b1f7556cd 100644 --- a/tests/quic.c +++ b/tests/quic.c @@ -287,7 +287,10 @@ static int test_provide_quic_data(void) { */ AssertNotNull(ssl = wolfSSL_new(ctx)); len = fake_record(1, 100, lbuffer); - AssertTrue(provide_data(ssl, wolfssl_encryption_initial, lbuffer, len, 0)); + AssertTrue(provide_data(ssl, wolfssl_encryption_initial, lbuffer, 1, 0)); + AssertTrue(provide_data(ssl, wolfssl_encryption_initial, lbuffer+1, 3, 0)); + AssertTrue(provide_data(ssl, wolfssl_encryption_initial, lbuffer+4, len, 0) + ); len = fake_record(2, 1523, lbuffer); AssertTrue(provide_data(ssl, wolfssl_encryption_handshake, lbuffer, len, 0)); len = fake_record(2, 1, lbuffer);