mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-21 05:59:29 +00:00
feat(real-world-vfc-slice): add labeled OpenSSL/Firefox/Chromium/curl pair corpus
Harvest 176 isolated vuln-vs-fix functions (109 CVEs, 352 training rows) into benchmarks/pairs/vfc with provenance and a JSONL training manifest. Score the slice as an honesty dashboard (ousast pairs --slice vfc); keep the pair gate on the local fixtures only.
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
/* Provenance: openssl/openssl port_default_packet_handler (fixed).
|
||||
* repo: openssl/openssl
|
||||
* commit: 70cebd74d3592f5272945501b58a60374c4e13af
|
||||
* parent: 48f5785425ee6b5316f8e5f921978397ba632cc6
|
||||
* commit_url: https://github.com/openssl/openssl/commit/70cebd74d3592f5272945501b58a60374c4e13af
|
||||
* cve: CVE-2026-18798
|
||||
* license: Apache-2.0
|
||||
* function: port_default_packet_handler
|
||||
* relpath: ssl/quic/quic_port.c
|
||||
*/
|
||||
|
||||
static void port_default_packet_handler(QUIC_URXE *e, void *arg,
|
||||
const QUIC_CONN_ID *dcid)
|
||||
{
|
||||
QUIC_PORT *port = arg;
|
||||
PACKET pkt;
|
||||
QUIC_PKT_HDR hdr;
|
||||
QUIC_CHANNEL *ch = NULL, *new_ch = NULL;
|
||||
QUIC_CONN_ID odcid;
|
||||
uint8_t gen_new_token = 0;
|
||||
OSSL_QRX *qrx = NULL, *qrx_ref;
|
||||
OSSL_QRX *qrx_src = NULL;
|
||||
OSSL_QRX_ARGS qrx_args = { 0 };
|
||||
uint64_t cause_flags = 0;
|
||||
OSSL_QRX_PKT *qrx_pkt = NULL;
|
||||
|
||||
/* Don't handle anything if we are no longer running. */
|
||||
if (!ossl_quic_port_is_running(port))
|
||||
goto undesirable;
|
||||
|
||||
if (port_try_handle_stateless_reset(port, e))
|
||||
goto undesirable;
|
||||
|
||||
if (dcid != NULL
|
||||
&& ossl_quic_lcidm_lookup(port->lcidm, dcid, NULL,
|
||||
(void **)&ch)) {
|
||||
assert(ch != NULL);
|
||||
ossl_quic_channel_inject(ch, e);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have an incoming packet which doesn't match any existing connection
|
||||
* we assume this is an attempt to make a new connection.
|
||||
*/
|
||||
if (!port->allow_incoming)
|
||||
goto undesirable;
|
||||
|
||||
/*
|
||||
* packet without destination connection id is invalid/corrupted here.
|
||||
* stop wasting CPU cycles now.
|
||||
*/
|
||||
if (dcid == NULL)
|
||||
goto undesirable;
|
||||
|
||||
/*
|
||||
* We have got a packet for an unknown DCID. This might be an attempt to
|
||||
* open a new connection.
|
||||
*/
|
||||
if (e->data_len < QUIC_MIN_INITIAL_DGRAM_LEN)
|
||||
goto undesirable;
|
||||
|
||||
if (!PACKET_buf_init(&pkt, ossl_quic_urxe_data(e), e->data_len))
|
||||
goto undesirable;
|
||||
|
||||
/*
|
||||
* We set short_conn_id_len to SIZE_MAX here which will cause the decode
|
||||
* operation to fail if we get a 1-RTT packet. This is fine since we only
|
||||
* care about Initial packets.
|
||||
*/
|
||||
if (!ossl_quic_wire_decode_pkt_hdr(&pkt, SIZE_MAX, 1, 0, &hdr, NULL,
|
||||
&cause_flags)) {
|
||||
/*
|
||||
* If we fail due to a bad version, we know the packet up to the version
|
||||
* number was decoded, and we use it below to send a version
|
||||
* negotiation packet
|
||||
*/
|
||||
if ((cause_flags & QUIC_PKT_HDR_DECODE_BAD_VERSION) == 0)
|
||||
goto undesirable;
|
||||
}
|
||||
|
||||
switch (hdr.version) {
|
||||
case QUIC_VERSION_1:
|
||||
break;
|
||||
|
||||
case QUIC_VERSION_NONE:
|
||||
default:
|
||||
|
||||
/*
|
||||
* If we get here, then we have a bogus version, and might need
|
||||
* to send a version negotiation packet. According to
|
||||
* RFC 9000 s. 6 and 14.1, we only do so however, if the UDP datagram
|
||||
* is a minimum of 1200 bytes in size
|
||||
*/
|
||||
if (e->data_len < 1200)
|
||||
goto undesirable;
|
||||
|
||||
/*
|
||||
* If we don't get a supported version, respond with a ver
|
||||
* negotiation packet, and discard
|
||||
* TODO(QUIC FUTURE): Rate limit the reception of these
|
||||
*/
|
||||
port_send_version_negotiation(port, &e->peer, &hdr);
|
||||
goto undesirable;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only care about Initial packets which might be trying to establish a
|
||||
* connection.
|
||||
*/
|
||||
if (hdr.type != QUIC_PKT_TYPE_INITIAL)
|
||||
goto undesirable;
|
||||
|
||||
if (port->max_pending_channels > 0 && ossl_list_incoming_ch_num(&port->incoming_channel_list) >= port->max_pending_channels)
|
||||
goto undesirable;
|
||||
|
||||
odcid.id_len = 0;
|
||||
|
||||
/*
|
||||
* Create qrx now so we can check integrity of packet
|
||||
* which does not belong to any channel.
|
||||
*/
|
||||
qrx_args.libctx = port->engine->libctx;
|
||||
qrx_args.demux = port->demux;
|
||||
qrx_args.short_conn_id_len = dcid->id_len;
|
||||
qrx_args.max_deferred = 32;
|
||||
qrx = ossl_qrx_new(&qrx_args);
|
||||
if (qrx == NULL)
|
||||
goto undesirable;
|
||||
|
||||
/*
|
||||
* Derive secrets for qrx only.
|
||||
*/
|
||||
if (!ossl_quic_provide_initial_secret(port->engine->libctx,
|
||||
port->engine->propq,
|
||||
&hdr.dst_conn_id,
|
||||
/* is_server */ 1,
|
||||
qrx, NULL))
|
||||
goto undesirable;
|
||||
|
||||
if (ossl_qrx_validate_initial_packet(qrx, e, (const QUIC_CONN_ID *)dcid) == 0)
|
||||
goto undesirable;
|
||||
|
||||
if (port->validate_addr == 0) {
|
||||
/*
|
||||
* Forget qrx, because it becomes (almost) useless here. We must let
|
||||
* channel to create a new QRX for connection ID server chooses. The
|
||||
* validation keys for new DCID will be derived by
|
||||
* ossl_quic_channel_on_new_conn() when we will be creating channel.
|
||||
* See RFC 9000 section 7.2 negotiating connection id to better
|
||||
* understand what's going on here.
|
||||
*
|
||||
* Did we say qrx is almost useless? Why? Because qrx remembers packets
|
||||
* we just validated. Those packets must be injected to channel we are
|
||||
* going to create. We use qrx_src alias so we can read packets from
|
||||
* qrx and inject them to channel.
|
||||
*/
|
||||
qrx_src = qrx;
|
||||
qrx = NULL;
|
||||
}
|
||||
/*
|
||||
* TODO(QUIC FUTURE): there should be some logic similar to accounting half-open
|
||||
* states in TCP. If we reach certain threshold, then we want to
|
||||
* validate clients.
|
||||
*/
|
||||
if (port->validate_addr == 1 && hdr.token == NULL) {
|
||||
port_send_retry(port, &e->peer, &hdr);
|
||||
goto undesirable;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note, even if we don't enforce the sending of retry frames for
|
||||
* server address validation, we may still get a token if we sent
|
||||
* a NEW_TOKEN frame during a prior connection, which we should still
|
||||
* validate here
|
||||
*/
|
||||
if (hdr.token != NULL
|
||||
&& port_validate_token(&hdr, port, &e->peer,
|
||||
&odcid, &gen_new_token)
|
||||
== 0) {
|
||||
/*
|
||||
* RFC 9000 s 8.1.3
|
||||
* When a server receives an Initial packet with an address
|
||||
* validation token, it MUST attempt to validate the token,
|
||||
* unless it has already completed address validation.
|
||||
* If the token is invalid, then the server SHOULD proceed as
|
||||
* if the client did not have a validated address,
|
||||
* including potentially sending a Retry packet
|
||||
* Note: If address validation is disabled, just act like
|
||||
* the request is valid
|
||||
*/
|
||||
if (port->validate_addr == 1) {
|
||||
/*
|
||||
* Again: we should consider saving initial encryption level
|
||||
* secrets to token here to save some CPU cycles.
|
||||
*/
|
||||
port_send_retry(port, &e->peer, &hdr);
|
||||
goto undesirable;
|
||||
}
|
||||
|
||||
/*
|
||||
* client is under amplification limit, until it completes
|
||||
* handshake.
|
||||
*
|
||||
* forget qrx so channel can create a new one
|
||||
* with valid initial encryption level keys.
|
||||
*/
|
||||
if (qrx != NULL) {
|
||||
qrx_src = qrx;
|
||||
qrx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
qrx_ref = NULL;
|
||||
if (qrx != NULL) {
|
||||
/*
|
||||
* if we are here, then client is validated via retry packet
|
||||
* (client sent a valid token). In this case the qrx has valid
|
||||
* secrets set for QUIC initial level encryption. We can pass
|
||||
* reference to qrx to newly created channel.
|
||||
*
|
||||
* Note: port_bind_channel()/channel becomes owner of qrx_ref.
|
||||
*/
|
||||
qrx_ref = ossl_qrx_newref(qrx);
|
||||
if (qrx_ref == NULL)
|
||||
goto undesirable;
|
||||
}
|
||||
port_bind_channel(port, &e->peer, &hdr.dst_conn_id,
|
||||
&odcid, qrx_ref, &new_ch);
|
||||
|
||||
/*
|
||||
* if packet validates it gets moved to channel, we've just bound
|
||||
* to port.
|
||||
*/
|
||||
if (new_ch == NULL)
|
||||
goto undesirable;
|
||||
|
||||
/*
|
||||
* Generate a token for sending in a later NEW_TOKEN frame
|
||||
*/
|
||||
if (gen_new_token == 1)
|
||||
generate_new_token(new_ch, &e->peer);
|
||||
|
||||
if (qrx_src != NULL) {
|
||||
/*
|
||||
* Time to reinject packets from qrx to channel before
|
||||
* qrx will be destroyed here.
|
||||
*/
|
||||
while (ossl_qrx_read_pkt(qrx_src, &qrx_pkt) == 1)
|
||||
ossl_quic_channel_inject_pkt(new_ch, qrx_pkt);
|
||||
ossl_qrx_update_pn_space(qrx_src, new_ch->qrx);
|
||||
/*
|
||||
* transfer ownership back to qrx;
|
||||
*/
|
||||
qrx = qrx_src;
|
||||
qrx_src = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If function reaches this place, then packet got validated in
|
||||
* ossl_qrx_validate_initial_packet(). Keep in mind the function
|
||||
* ossl_qrx_validate_initial_packet() decrypts the packet to validate it.
|
||||
* If packet validation was successful (and it was because we are here),
|
||||
* then the function puts the packet to qrx->rx_pending. We must not call
|
||||
* ossl_qrx_inject_urxe() here now, because we don't want to insert
|
||||
* the packet to qrx->urx_pending which keeps packet waiting for decryption.
|
||||
*
|
||||
* We are going to call ossl_quic_demux_release_urxe() to dispose buffer
|
||||
* which still holds encrypted data.
|
||||
*/
|
||||
|
||||
undesirable:
|
||||
ossl_qrx_free(qrx); /* releases reference */
|
||||
ossl_qrx_free(qrx_src);
|
||||
ossl_quic_demux_release_urxe(port->demux, e);
|
||||
}
|
||||
Reference in New Issue
Block a user