Release 1.17.0

- [API Change] Packet out Memory Interface (PMI) update:
  - Split PMI pool return into pmi_release and pmi_return
  - PMI callbacks take peer_ctx and is_ipv6 arguments
- [BUGFIX] Fix use-after-free when certificate is updated
- Silence gcc warning in optimized mode by performing useless
  initialization
- cmake: use the standard variable CMAKE_BUILD_TYPE instead of
  DEVEL_MODE
This commit is contained in:
Dmitri Tikhonov 2018-10-16 09:03:33 -04:00
parent 66f9afccd0
commit 1e75f9380e
15 changed files with 178 additions and 64 deletions

View file

@ -6,6 +6,7 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@ -244,14 +245,14 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
static void
free_packet (void *ctx, unsigned char *packet_data)
free_packet (void *ctx, void *conn_ctx, void *packet_data, char is_ipv6)
{
free(packet_data);
}
static void *
malloc_buf (void *ctx, size_t size)
malloc_buf (void *ctx, void *conn_ctx, unsigned short size, char is_ipv6)
{
return malloc(size);
}
@ -259,7 +260,7 @@ malloc_buf (void *ctx, size_t size)
static const struct lsquic_packout_mem_if stock_pmi =
{
malloc_buf, (void(*)(void *, void *)) free_packet,
malloc_buf, free_packet, free_packet,
};
@ -821,6 +822,13 @@ really_encrypt_packet (const lsquic_conn_t *conn,
}
static int
conn_peer_ipv6 (const struct lsquic_conn *conn)
{
return AF_INET6 == ((struct sockaddr *) conn->cn_peer_addr)->sa_family;
}
static enum { ENCPA_OK, ENCPA_NOMEM, ENCPA_BADCRYPT, }
encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
lsquic_packet_out_t *packet_out)
@ -829,10 +837,15 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
size_t bufsz;
unsigned sent_sz;
unsigned char *buf;
int ipv6;
bufsz = conn->cn_pf->pf_packout_header_size(conn, packet_out->po_flags) +
packet_out->po_data_sz + QUIC_PACKET_HASH_SZ;
buf = engine->pub.enp_pmi->pmi_allocate(engine->pub.enp_pmi_ctx, bufsz);
if (bufsz > USHRT_MAX)
return ENCPA_BADCRYPT; /* To cause connection to close */
ipv6 = conn_peer_ipv6(conn);
buf = engine->pub.enp_pmi->pmi_allocate(engine->pub.enp_pmi_ctx,
conn->cn_peer_ctx, bufsz, ipv6);
if (!buf)
{
LSQ_DEBUG("could not allocate memory for outgoing packet of size %zd",
@ -847,19 +860,51 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
if (enc_sz < 0)
{
engine->pub.enp_pmi->pmi_release(engine->pub.enp_pmi_ctx, buf);
engine->pub.enp_pmi->pmi_return(engine->pub.enp_pmi_ctx,
conn->cn_peer_ctx, buf, ipv6);
return ENCPA_BADCRYPT;
}
packet_out->po_enc_data = buf;
packet_out->po_enc_data_sz = enc_sz;
packet_out->po_sent_sz = sent_sz;
packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ;
packet_out->po_flags &= ~PO_IPv6;
packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ|(ipv6 << POIPv6_SHIFT);
return ENCPA_OK;
}
static void
release_or_return_enc_data (struct lsquic_engine *engine,
void (*pmi_rel_or_ret) (void *, void *, void *, char),
struct lsquic_conn *conn, struct lsquic_packet_out *packet_out)
{
pmi_rel_or_ret(engine->pub.enp_pmi_ctx, conn->cn_peer_ctx,
packet_out->po_enc_data, lsquic_packet_out_ipv6(packet_out));
packet_out->po_flags &= ~PO_ENCRYPTED;
packet_out->po_enc_data = NULL;
}
static void
release_enc_data (struct lsquic_engine *engine, struct lsquic_conn *conn,
struct lsquic_packet_out *packet_out)
{
release_or_return_enc_data(engine, engine->pub.enp_pmi->pmi_release,
conn, packet_out);
}
static void
return_enc_data (struct lsquic_engine *engine, struct lsquic_conn *conn,
struct lsquic_packet_out *packet_out)
{
release_or_return_enc_data(engine, engine->pub.enp_pmi->pmi_return,
conn, packet_out);
}
STAILQ_HEAD(conns_stailq, lsquic_conn);
TAILQ_HEAD(conns_tailq, lsquic_conn);
@ -1006,12 +1051,7 @@ send_batch (lsquic_engine_t *engine, struct conns_out_iter *conns_iter,
* or until it times out and regenerated.
*/
if (batch->packets[i]->po_flags & PO_ENCRYPTED)
{
batch->packets[i]->po_flags &= ~PO_ENCRYPTED;
engine->pub.enp_pmi->pmi_release(engine->pub.enp_pmi_ctx,
batch->packets[i]->po_enc_data);
batch->packets[i]->po_enc_data = NULL; /* JIC */
}
release_enc_data(engine, batch->conns[i], batch->packets[i]);
}
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT))
for ( ; i < (int) n_to_send; ++i)
@ -1074,6 +1114,14 @@ send_packets_out (struct lsquic_engine *engine,
coi_deactivate(&conns_iter, conn);
continue;
}
if ((packet_out->po_flags & PO_ENCRYPTED)
&& lsquic_packet_out_ipv6(packet_out) != conn_peer_ipv6(conn))
{
/* Peer address changed since the packet was encrypted. Need to
* reallocate.
*/
return_enc_data(engine, conn, packet_out);
}
if (!(packet_out->po_flags & (PO_ENCRYPTED|PO_NOENCRYPT)))
{
switch (encrypt_packet(engine, conn, packet_out))

View file

@ -173,6 +173,7 @@ static void free_info (lsquic_session_cache_info_t *);
/* client */
static cert_hash_item_t *make_cert_hash_item(struct lsquic_str *domain, struct lsquic_str **certs, int count);
static int c_insert_certs(cert_hash_item_t *item);
static void c_erase_certs(struct lsquic_hash_elem *el);
static void c_free_cert_hash_item (cert_hash_item_t *item);
static int get_tag_val_u32 (unsigned char *v, int len, uint32_t *val);
@ -265,16 +266,23 @@ static int init_hs_hash_tables(int flags)
/* client */
cert_hash_item_t *
c_find_certs (const lsquic_str_t *domain)
struct lsquic_hash_elem *
c_get_certs_elem (const lsquic_str_t *domain)
{
struct lsquic_hash_elem *el;
if (!s_cached_client_certs)
return NULL;
el = lsquic_hash_find(s_cached_client_certs, lsquic_str_cstr(domain),
return lsquic_hash_find(s_cached_client_certs, lsquic_str_cstr(domain),
lsquic_str_len(domain));
}
/* client */
cert_hash_item_t *
c_find_certs (const lsquic_str_t *domain)
{
struct lsquic_hash_elem *el = c_get_certs_elem(domain);
if (el == NULL)
return NULL;
@ -335,6 +343,15 @@ c_insert_certs (cert_hash_item_t *item)
}
/* client */
static void
c_erase_certs (struct lsquic_hash_elem *el)
{
if (s_cached_client_certs && el)
lsquic_hash_erase(s_cached_client_certs, el);
}
static int save_session_info_entry(lsquic_str_t *key, lsquic_session_cache_info_t *entry)
{
lsquic_str_setto(&entry->sni_key, lsquic_str_cstr(key), lsquic_str_len(key));
@ -1364,7 +1381,11 @@ lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session,
int ret;
lsquic_session_cache_info_t *info = enc_session->info;
hs_ctx_t * hs_ctx = &enc_session->hs_ctx;
cert_hash_item_t *cached_certs_item = c_find_certs(&hs_ctx->sni);
cert_hash_item_t *cached_certs_item = NULL;
struct lsquic_hash_elem *el = c_get_certs_elem(&hs_ctx->sni);
if (el)
cached_certs_item = lsquic_hashelem_getdata(el);
/* FIXME get the number first */
lsquic_str_t **out_certs = NULL;
@ -1427,6 +1448,8 @@ lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session,
;
else
{
if (el)
c_erase_certs(el);
if (cached_certs_item)
c_free_cert_hash_item(cached_certs_item);

View file

@ -279,7 +279,7 @@ lsquic_packet_out_new (struct lsquic_mm *mm, struct malo *malo, int use_cid,
void
lsquic_packet_out_destroy (lsquic_packet_out_t *packet_out,
struct lsquic_engine_public *enpub)
struct lsquic_engine_public *enpub, void *peer_ctx)
{
if (packet_out->po_flags & PO_SREC_ARR)
{
@ -292,8 +292,8 @@ lsquic_packet_out_destroy (lsquic_packet_out_t *packet_out,
}
}
if (packet_out->po_flags & PO_ENCRYPTED)
enpub->enp_pmi->pmi_release(enpub->enp_pmi_ctx,
packet_out->po_enc_data);
enpub->enp_pmi->pmi_release(enpub->enp_pmi_ctx, peer_ctx,
packet_out->po_enc_data, lsquic_packet_out_ipv6(packet_out));
if (packet_out->po_nonce)
free(packet_out->po_nonce);
lsquic_mm_put_packet_out(&enpub->enp_mm, packet_out);

View file

@ -90,6 +90,10 @@ typedef struct lsquic_packet_out
#define POLEV_SHIFT 18
PO_BITS_2 = (1 <<18), /* PO_BITS_2 and PO_BITS_3 encode the */
PO_BITS_3 = (1 <<19), /* crypto level. Used for logging. */
#define POIPv6_SHIFT 20
PO_IPv6 = (1 <<20), /* Set if pmi_allocate was passed is_ipv6=1,
* otherwise unset.
*/
} po_flags;
enum quic_ft_bit po_frame_types:16; /* Bitmask of QUIC_FRAME_* */
unsigned short po_data_sz; /* Number of usable bytes in data */
@ -142,6 +146,13 @@ typedef struct lsquic_packet_out
(p)->po_flags |= ((b) & 0x3) << POBIT_SHIFT; \
} while (0)
#define lsquic_packet_out_ipv6(p) ((int)(((p)->po_flags >> POIPv6_SHIFT) & 1))
#define lsquic_packet_out_set_ipv6(p, b) do { \
(p)->po_flags &= ~(1 << POIPv6_SHIFT); \
(p)->po_flags |= ((b) & 1) << POIPv6_SHIFT; \
} while (0)
#define lsquic_po_header_length(lconn, po_flags) ( \
lconn->cn_pf->pf_packout_header_size(lconn, po_flags))
@ -198,7 +209,7 @@ lsquic_packet_out_new (struct lsquic_mm *, struct malo *, int use_cid,
void
lsquic_packet_out_destroy (lsquic_packet_out_t *,
struct lsquic_engine_public *);
struct lsquic_engine_public *, void *peer_ctx);
int
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,

View file

@ -528,12 +528,22 @@ send_ctl_release_enc_data (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
packet_out->po_enc_data);
ctl->sc_conn_pub->lconn->cn_peer_ctx, packet_out->po_enc_data,
lsquic_packet_out_ipv6(packet_out));
packet_out->po_flags &= ~PO_ENCRYPTED;
packet_out->po_enc_data = NULL;
}
static void
send_ctl_destroy_packet (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub,
ctl->sc_conn_pub->lconn->cn_peer_ctx);
}
/* Returns true if packet was rescheduled, false otherwise. In the latter
* case, you should not dereference packet_out after the function returns.
*/
@ -564,7 +574,7 @@ send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
{
LSQ_DEBUG("lost unretransmittable packet %"PRIu64,
packet_out->po_packno);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
return 0;
}
}
@ -754,7 +764,7 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
lsquic_cubic_ack(&ctl->sc_cubic, now, now - packet_out->po_sent,
app_limited, packet_sz);
lsquic_packet_out_ack_streams(packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
}
packet_out = next;
}
@ -853,7 +863,7 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
while ((packet_out = TAILQ_FIRST(&ctl->sc_scheduled_packets)))
{
send_ctl_sched_remove(ctl, packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
}
assert(0 == ctl->sc_n_scheduled);
assert(0 == ctl->sc_bytes_scheduled);
@ -861,7 +871,7 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
{
TAILQ_REMOVE(&ctl->sc_unacked_packets, packet_out, po_next);
ctl->sc_bytes_unacked_all -= packet_out_total_sz(packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
--ctl->sc_n_in_flight_all;
}
assert(0 == ctl->sc_n_in_flight_all);
@ -869,7 +879,7 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
while ((packet_out = TAILQ_FIRST(&ctl->sc_lost_packets)))
{
TAILQ_REMOVE(&ctl->sc_lost_packets, packet_out, po_next);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
}
pacer_cleanup(&ctl->sc_pacer);
#if LSQUIC_SEND_STATS
@ -1100,7 +1110,7 @@ lsquic_send_ctl_next_packet_to_send (lsquic_send_ctl_t *ctl)
{
LSQ_DEBUG("Dropping packet %"PRIu64" from scheduled queue",
packet_out->po_packno);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
goto get_packet;
}
}
@ -1166,7 +1176,7 @@ send_ctl_allocate_packet (lsquic_send_ctl_t *ctl, enum lsquic_packno_bits bits,
LSQ_ERROR("wanted to allocate packet with at least %u bytes of "
"payload, but only got %u bytes (mtu: %u bytes)", need_at_least,
lsquic_packet_out_avail(packet_out), ctl->sc_pack_size);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
return NULL;
}
@ -1311,7 +1321,7 @@ lsquic_send_ctl_reschedule_packets (lsquic_send_ctl_t *ctl)
{
LSQ_DEBUG("Dropping packet %"PRIu64" from unacked queue",
packet_out->po_packno);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
}
}
@ -1373,7 +1383,7 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
LSQ_DEBUG("cancel packet %"PRIu64" after eliding frames for "
"stream %"PRIu32, packet_out->po_packno, stream_id);
send_ctl_sched_remove(ctl, packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
++dropped;
}
}
@ -1398,7 +1408,7 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
TAILQ_REMOVE(&ctl->sc_buffered_packets[n].bpq_packets,
packet_out, po_next);
--ctl->sc_buffered_packets[n].bpq_count;
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
LSQ_DEBUG("Elide packet from buffered queue #%u; count: %u",
n, ctl->sc_buffered_packets[n].bpq_count);
}
@ -1508,7 +1518,7 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
send_ctl_sched_remove(ctl, packet_out);
LSQ_DEBUG("Dropping packet %"PRIu64" from scheduled queue",
packet_out->po_packno);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
++dropped;
}
}
@ -1560,7 +1570,7 @@ lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *ctl)
while ((packet_out = TAILQ_FIRST(&ctl->sc_scheduled_packets)))
{
send_ctl_sched_remove(ctl, packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
}
assert(0 == ctl->sc_n_scheduled);
ctl->sc_cur_packno = lsquic_senhist_largest(&ctl->sc_senhist);
@ -1743,7 +1753,7 @@ split_buffered_packet (lsquic_send_ctl_t *ctl,
}
else
{
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
send_ctl_destroy_packet(ctl, packet_out);
return -1;
}
}