Skip to content

Commit 3664779

Browse files
committed
qir-failures
1 parent aeb22c2 commit 3664779

11 files changed

Lines changed: 114 additions & 38 deletions

File tree

.sai.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@
321321
},
322322

323323
"qir": {
324-
"cmake": "cd /opt/quic-interop-runner ; source venv/bin/activate ; rm -rf logs_* ; export QIR_LWS_BRANCH=main ; cd /opt/quic-interop-runner/lws ; if [ \"`docker ps -q`\" ] ; then docker kill $(docker ps -q) ; fi ; docker system prune -a --volumes -f && docker build --build-arg QIR_LWS_BRANCH=${QIR_LWS_BRANCH} -t lws-quic-interop . --no-cache && cd .. && python3 run.py -c lws -s lws --delete-successful-logs ; python3 run.py -p webtransport -c lws -s lws --delete-successful-logs -t handshake,transfer-unidirectional-receive,transfer-unidirectional-send,transfer-bidirectional-receive,transfer-bidirectional-send,transfer-datagram-receive,transfer-datagram-send",
324+
"cmake": "cd /opt/quic-interop-runner ; source venv/bin/activate ; rm -rf logs_* ; export QIR_LWS_BRANCH=main ; cd /opt/quic-interop-runner/lws ; if [ \"`docker ps -q`\" ] ; then docker kill $(docker ps -q) ; fi ; docker system prune && docker build --build-arg QIR_LWS_BRANCH=${QIR_LWS_BRANCH} -t lws-quic-interop . --no-cache && cd .. && python3 run.py -c lws -s lws --delete-successful-logs ; python3 run.py -p webtransport -c lws -s lws --delete-successful-logs -t handshake,transfer-unidirectional-receive,transfer-unidirectional-send,transfer-bidirectional-receive,transfer-bidirectional-send,transfer-datagram-receive,transfer-datagram-send",
325325
"platforms": "none, linux-debian13/x86_64-amd/gcc"
326326
}
327327

lib/roles/quic/crypto-quic.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin
259259
}
260260

261261
if (is_rx) {
262-
if (level == LWS_QUIC_LEVEL_APP && k->valid && k->secret_rx[0] && qn->handshake_done) {
262+
if (level == LWS_QUIC_LEVEL_APP && k->valid && k->secret_rx[0]) {
263263
lwsl_notice("%s: ignoring post-handshake TLS secret_rx update for APP level\n", __func__);
264264
return 0;
265265
}
@@ -270,7 +270,7 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin
270270
&k->el_aead_rx, k->key_aead_rx, &k->el_hp_rx, k->key_hp_rx))
271271
return -1;
272272
} else {
273-
if (level == LWS_QUIC_LEVEL_APP && k->valid && k->secret_tx[0] && qn->handshake_done) {
273+
if (level == LWS_QUIC_LEVEL_APP && k->valid && k->secret_tx[0]) {
274274
lwsl_notice("%s: ignoring post-handshake TLS secret_tx update for APP level\n", __func__);
275275
return 0;
276276
}

lib/roles/quic/ops-quic.c

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,28 +1148,6 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
11481148
break;
11491149
}
11501150

1151-
/* Check reserved bits AFTER unmasking! */
1152-
if (p[0] & 0x80) {
1153-
/* Long header: Bits 0x0c MUST be zero */
1154-
if (p[0] & 0x0c) {
1155-
lwsl_wsi_notice(wsi, "QUIC RX: Reserved bits non-zero in long header");
1156-
if (nwsi && nwsi != wsi) {
1157-
lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0);
1158-
goto next_packet;
1159-
}
1160-
return LWS_HPI_RET_PLEASE_CLOSE_ME;
1161-
}
1162-
} else {
1163-
/* Short header: Bits 0x18 MUST be zero */
1164-
if (p[0] & 0x18) {
1165-
lwsl_wsi_notice(wsi, "QUIC RX: Reserved bits non-zero in short header");
1166-
if (nwsi && nwsi != wsi) {
1167-
lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0);
1168-
goto next_packet;
1169-
}
1170-
return LWS_HPI_RET_PLEASE_CLOSE_ME;
1171-
}
1172-
}
11731151

11741152
/*
11751153
* Reconstruct full 62-bit PN.
@@ -1221,6 +1199,29 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12211199
goto next_packet;
12221200
}
12231201

1202+
/* Check reserved bits AFTER successful AEAD decryption (RFC 9000 5.4.1 & 12.2) */
1203+
if (p[0] & 0x80) {
1204+
/* Long header: Bits 0x0c MUST be zero */
1205+
if (p[0] & 0x0c) {
1206+
lwsl_wsi_notice(wsi, "QUIC RX: Reserved bits non-zero in long header");
1207+
if (nwsi && nwsi != wsi) {
1208+
lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0);
1209+
goto next_packet;
1210+
}
1211+
return LWS_HPI_RET_PLEASE_CLOSE_ME;
1212+
}
1213+
} else {
1214+
/* Short header: Bits 0x18 MUST be zero */
1215+
if (p[0] & 0x18) {
1216+
lwsl_wsi_notice(wsi, "QUIC RX: Reserved bits non-zero in short header");
1217+
if (nwsi && nwsi != wsi) {
1218+
lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0);
1219+
goto next_packet;
1220+
}
1221+
return LWS_HPI_RET_PLEASE_CLOSE_ME;
1222+
}
1223+
}
1224+
12241225
/* Decryption succeeded! Commit key update if pending */
12251226
if (is_key_update) {
12261227
lws_quic_keys_release_aead_rx(k);
@@ -1272,6 +1273,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12721273
}
12731274

12741275
/* Check for duplicate/replayed packet numbers (Security Fix) */
1276+
int is_out_of_order = 0;
12751277
if (nwsi->quic.qn) {
12761278
uint64_t highest = nwsi->quic.qn->highest_rx_pn[pn_space];
12771279
if ((nwsi->quic.qn->rx_pn_bitmask[pn_space] != 0 || highest != 0) && full_pn <= highest) {
@@ -1281,6 +1283,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12811283
goto next_packet;
12821284
}
12831285
nwsi->quic.qn->rx_pn_bitmask[pn_space] |= (1ULL << diff);
1286+
is_out_of_order = 1;
12841287
} else {
12851288
if (nwsi->quic.qn->rx_pn_bitmask[pn_space] != 0 || highest != 0) {
12861289
uint64_t diff = full_pn - highest;
@@ -1295,6 +1298,11 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12951298

12961299
/* Connection Migration: Execute pending migration now that the packet is cryptographically verified */
12971300
if (pending_migration) {
1301+
if (is_out_of_order) {
1302+
lwsl_notice("QUIC: Ignoring connection migration from out-of-order packet (PN %llu <= highest %llu)\n",
1303+
(unsigned long long)full_pn, (unsigned long long)highest);
1304+
pending_migration = 0;
1305+
} else {
12981306
pending_migration = 0;
12991307
#if (_LWS_ENABLED_LOGS & LLL_NOTICE)
13001308
char buf_old[64], buf_new[64];
@@ -1318,7 +1326,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
13181326
buf_old, (unsigned int)ntohs(port_old),
13191327
buf_new, (unsigned int)ntohs(port_new));
13201328
#endif
1321-
/* F-60: Do NOT commit nwsi->udp->sa46 yet! Wait for PATH_RESPONSE! */
1329+
nwsi->quic.qn->rx_has_non_probing = 0;
13221330
nwsi->quic.qn->probing_sa46 = migration_sa46;
13231331
nwsi->quic.qn->probing_sa46_valid = 1;
13241332

@@ -1381,6 +1389,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
13811389
lws_callback_on_writable(nwsi);
13821390
}
13831391
}
1392+
}
13841393
}
13851394

13861395
/* 5. Parse and handle all frames in the payload */
@@ -1400,6 +1409,46 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
14001409
nwsi = lws_get_quic_network_wsi(nwsi);
14011410
}
14021411

1412+
/* RFC 9000 Section 9.3: Receiving non-probing frames (STREAM, ACK, etc.) from a new address
1413+
* indicates that the peer has migrated (e.g. due to NAT rebinding or active client migration).
1414+
* The server MUST commit its active path to the new address. */
1415+
if (nwsi && nwsi->quic.qn && nwsi->quic.qn->is_server &&
1416+
nwsi->quic.qn->probing_sa46_valid && nwsi->quic.qn->rx_has_non_probing) {
1417+
#if (_LWS_ENABLED_LOGS & LLL_NOTICE)
1418+
char buf_old[64], buf_new[64];
1419+
uint16_t port_old, port_new;
1420+
lws_sa46_write_numeric_address(&nwsi->udp->sa46, buf_old, sizeof(buf_old));
1421+
lws_sa46_write_numeric_address(&nwsi->quic.qn->probing_sa46, buf_new, sizeof(buf_new));
1422+
#if defined(LWS_WITH_IPV6)
1423+
port_old = nwsi->udp->sa46.sa4.sin_family == AF_INET ? nwsi->udp->sa46.sa4.sin_port : nwsi->udp->sa46.sa6.sin6_port;
1424+
port_new = nwsi->quic.qn->probing_sa46.sa4.sin_family == AF_INET ? nwsi->quic.qn->probing_sa46.sa4.sin_port : nwsi->quic.qn->probing_sa46.sa6.sin6_port;
1425+
#else
1426+
port_old = nwsi->udp->sa46.sa4.sin_port;
1427+
port_new = nwsi->quic.qn->probing_sa46.sa4.sin_port;
1428+
#endif
1429+
lwsl_notice("QUIC Server: Connection Migration committed via non-probing packet! Peer address updated from %s:%u to %s:%u\n",
1430+
buf_old, (unsigned int)ntohs(port_old),
1431+
buf_new, (unsigned int)ntohs(port_new));
1432+
#endif
1433+
1434+
nwsi->udp->sa46 = nwsi->quic.qn->probing_sa46;
1435+
nwsi->quic.qn->probing_sa46_valid = 0;
1436+
1437+
/* Reset Congestion Control State (RFC 9000 9.3.3) */
1438+
if (nwsi->quic.qn->cc_ops && nwsi->quic.qn->cc_ops->init)
1439+
nwsi->quic.qn->cc_ops->init(nwsi);
1440+
1441+
/* Reset RTT estimator */
1442+
nwsi->quic.qn->smoothed_rtt = 0;
1443+
nwsi->quic.qn->rttvar = 0;
1444+
nwsi->quic.qn->latest_rtt = 0;
1445+
1446+
/* Reset PMTUD */
1447+
nwsi->quic.qn->current_mtu = 1280;
1448+
nwsi->quic.qn->probed_mtu = 1380;
1449+
nwsi->quic.qn->pmtud_state = 1;
1450+
}
1451+
14031452
if (nwsi) {
14041453
struct lws *w = nwsi->mux.child_list;
14051454
while (w) {

lib/roles/quic/parse-quic.c

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,21 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_
315315
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, owner->head) {
316316
struct lws_quic_rx_chunk *c = lws_container_of(d, struct lws_quic_rx_chunk, list);
317317

318+
if (c->offset < *expected_offset) {
319+
if (c->offset + c->len <= *expected_offset) {
320+
/* Completely obsolete chunk already delivered, discard */
321+
lws_dll2_remove(&c->list);
322+
lws_free(c);
323+
flushed = 1;
324+
break;
325+
}
326+
/* Trim overlapping prefix */
327+
size_t overlap = (size_t)(*expected_offset - c->offset);
328+
c->data += overlap;
329+
c->len -= overlap;
330+
c->offset = *expected_offset;
331+
}
332+
318333
if (c->offset == *expected_offset) {
319334
/* We found the next contiguous piece! */
320335
if (is_crypto) {
@@ -543,6 +558,14 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl
543558
if (!consumed) return -1;
544559
pos += consumed;
545560

561+
/* Track non-probing frames (RFC 9000 Section 9.1) */
562+
if (qn && type != LWS_QUIC_FT_PADDING &&
563+
type != LWS_QUIC_FT_PATH_CHALLENGE &&
564+
type != LWS_QUIC_FT_PATH_RESPONSE &&
565+
type != LWS_QUIC_FT_NEW_CONNECTION_ID) {
566+
qn->rx_has_non_probing = 1;
567+
}
568+
546569
/* Epoch Gating (RFC 9000 12.5) */
547570
int is_allowed = 0;
548571
switch (type) {
@@ -1672,13 +1695,16 @@ lws_quic_parse_transport_parameters(struct lws *wsi, const uint8_t *buf, size_t
16721695
}
16731696

16741697
if (port > 0) {
1698+
const char *host_str = (qn->nwsi && qn->nwsi->stash && qn->nwsi->stash->cis[CIS_HOST]) ?
1699+
qn->nwsi->stash->cis[CIS_HOST] : addr_str;
1700+
16751701
lwsl_wsi_notice(wsi, "QUIC TP: Migrating to preferred_address %s:%d", addr_str, port);
16761702
memset(&i, 0, sizeof(i));
16771703
i.context = wsi->a.context;
16781704
i.vhost = wsi->a.vhost;
16791705
i.address = addr_str;
1680-
i.host = addr_str;
1681-
i.origin = addr_str;
1706+
i.host = host_str;
1707+
i.origin = host_str;
16821708
i.port = port;
16831709
i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_ALLOW_INSECURE;
16841710
i.quic_migrate_from_wsi = qn->nwsi;

lib/roles/quic/private-lib-roles-quic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ struct lws_quic_netconn {
316316
struct lws *migration_probing_wsi;
317317
lws_sockaddr46 probing_sa46;
318318
uint8_t probing_sa46_valid:1;
319+
uint8_t rx_has_non_probing:1;
319320

320321
/* ECN (Explicit Congestion Notification) */
321322
uint64_t ecn_rx_ect0;

lib/tls/gnutls/gnutls-session.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ lws_tls_session_new_gnutls(struct lws *wsi)
174174
struct lws_vhost *vh;
175175
lws_tls_scm_t *ts;
176176
size_t nl;
177-
gnutls_datum_t gd;
177+
gnutls_datum_t gd = { NULL, 0 };
178178
#if (_LWS_ENABLED_LOGS & LLL_INFO)
179179
const char *disposition = "reuse";
180180
#endif

lib/tls/openssl/openssl-client.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,13 +708,11 @@ lws_tls_client_create_vhost_context(struct lws_vhost *vh,
708708
)
709709
{
710710
struct lws_tls_client_reuse *tcr;
711-
X509_STORE *x509_store;
712711
unsigned long error;
713712
SSL_METHOD *method;
714713
EVP_MD_CTX *mdctx;
715714
unsigned int len;
716715
uint8_t hash[32];
717-
X509 *client_CA;
718716
char c;
719717
int n;
720718

lib/tls/private-lib-tls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,14 @@ lws_tls_check_all_cert_lifetimes(struct lws_context *context);
249249
LWS_VISIBLE int
250250
lws_tls_cert_get_x509_remaining(struct lws_context *context, const char *filepath, int *days_left, int *total_days);
251251

252+
#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_CLIENT)
252253
int
253254
lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh, const uint8_t *der, size_t len);
254255

255256
int
256257
lws_tls_client_vhost_ca_mem_parse(struct lws_vhost *vh, const void *ca_mem,
257258
unsigned int ca_mem_len);
259+
#endif
258260

259261
int
260262
lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,

lib/tls/tls.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
597597
return 4;
598598
}
599599

600+
#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_CLIENT)
600601
int
601602
lws_tls_client_vhost_ca_mem_parse(struct lws_vhost *vh, const void *ca_mem,
602603
unsigned int ca_mem_len)
@@ -659,6 +660,7 @@ lws_tls_client_vhost_ca_mem_parse(struct lws_vhost *vh, const void *ca_mem,
659660
lwsl_info("%s: loaded %d CA cert(s) from ca_mem\n", __func__, count);
660661
return 0;
661662
}
663+
#endif
662664

663665
#endif
664666

minimal-examples/client/hello_world-policy/hello_world-ss.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ hello_world_state(void *userobj, void *h_src, lws_ss_constate_t state,
5656

5757
case LWSSSCS_QOS_ACK_REMOTE: /* server liked our request */
5858

59-
if (!lws_ss_get_metadata(g->ss, "ctype", (const void **)&ct, &ctl))
59+
if (!lws_ss_get_metadata(g->ss, "ctype", (const void **)&ct, &ctl)) {
6060
lwsl_ss_user(g->ss, "get_metadata ctype '%.*s'", (int)ctl, ct);
61-
else
61+
test_result &= ~1;
62+
} else
6263
lwsl_ss_user(g->ss, "get_metadata ctype missing");
6364

64-
test_result &= ~1;
6565
break;
6666

6767
case LWSSSCS_DISCONNECTED: /* for our example, disconnect = done */

0 commit comments

Comments
 (0)