Coverage Report

Created: 2025-12-04 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl33/ssl/quic/quic_lcidm.c
Line
Count
Source
1
/*
2
 * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include "internal/quic_lcidm.h"
11
#include "internal/quic_types.h"
12
#include "internal/quic_vlint.h"
13
#include "internal/common.h"
14
#include <openssl/lhash.h>
15
#include <openssl/rand.h>
16
#include <openssl/err.h>
17
18
/*
19
 * QUIC Local Connection ID Manager
20
 * ================================
21
 */
22
23
typedef struct quic_lcidm_conn_st QUIC_LCIDM_CONN;
24
25
enum {
26
    LCID_TYPE_ODCID,        /* This LCID is the ODCID from the peer */
27
    LCID_TYPE_INITIAL,      /* This is our Initial SCID */
28
    LCID_TYPE_NCID          /* This LCID was issued via a NCID frame */
29
};
30
31
typedef struct quic_lcid_st {
32
    QUIC_CONN_ID                cid;
33
    uint64_t                    seq_num;
34
35
    /* Back-pointer to the owning QUIC_LCIDM_CONN structure. */
36
    QUIC_LCIDM_CONN             *conn;
37
38
    /* LCID_TYPE_* */
39
    unsigned int                type                : 2;
40
} QUIC_LCID;
41
42
DEFINE_LHASH_OF_EX(QUIC_LCID);
43
DEFINE_LHASH_OF_EX(QUIC_LCIDM_CONN);
44
45
struct quic_lcidm_conn_st {
46
    size_t              num_active_lcid;
47
    LHASH_OF(QUIC_LCID) *lcids;
48
    void                *opaque;
49
    QUIC_LCID           *odcid_lcid_obj;
50
    uint64_t            next_seq_num;
51
52
    /* Have we enrolled an ODCID? */
53
    unsigned int        done_odcid          : 1;
54
};
55
56
struct quic_lcidm_st {
57
    OSSL_LIB_CTX                *libctx;
58
    LHASH_OF(QUIC_LCID)         *lcids; /* (QUIC_CONN_ID) -> (QUIC_LCID *)  */
59
    LHASH_OF(QUIC_LCIDM_CONN)   *conns; /* (void *opaque) -> (QUIC_LCIDM_CONN *) */
60
    size_t                      lcid_len; /* Length in bytes for all LCIDs */
61
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
62
    QUIC_CONN_ID                next_lcid;
63
#endif
64
};
65
66
static unsigned long bin_hash(const unsigned char *buf, size_t buf_len)
67
11.9M
{
68
11.9M
    unsigned long hash = 0;
69
11.9M
    size_t i;
70
71
93.4M
    for (i = 0; i < buf_len; ++i)
72
81.5M
        hash ^= ((unsigned long)buf[i]) << (8 * (i % sizeof(unsigned long)));
73
74
11.9M
    return hash;
75
11.9M
}
76
77
static unsigned long lcid_hash(const QUIC_LCID *lcid_obj)
78
11.9M
{
79
11.9M
    return bin_hash(lcid_obj->cid.id, lcid_obj->cid.id_len);
80
11.9M
}
81
82
static int lcid_comp(const QUIC_LCID *a, const QUIC_LCID *b)
83
9.55M
{
84
9.55M
    return !ossl_quic_conn_id_eq(&a->cid, &b->cid);
85
9.55M
}
86
87
static unsigned long lcidm_conn_hash(const QUIC_LCIDM_CONN *conn)
88
3.51M
{
89
3.51M
    return (unsigned long)(uintptr_t)conn->opaque;
90
3.51M
}
91
92
static int lcidm_conn_comp(const QUIC_LCIDM_CONN *a, const QUIC_LCIDM_CONN *b)
93
3.18M
{
94
3.18M
    return a->opaque != b->opaque;
95
3.18M
}
96
97
QUIC_LCIDM *ossl_quic_lcidm_new(OSSL_LIB_CTX *libctx, size_t lcid_len)
98
22.2k
{
99
22.2k
    QUIC_LCIDM *lcidm = NULL;
100
101
22.2k
    if (lcid_len > QUIC_MAX_CONN_ID_LEN)
102
0
        goto err;
103
104
22.2k
    if ((lcidm = OPENSSL_zalloc(sizeof(*lcidm))) == NULL)
105
0
        goto err;
106
107
22.2k
    if ((lcidm->lcids = lh_QUIC_LCID_new(lcid_hash, lcid_comp)) == NULL)
108
0
        goto err;
109
110
22.2k
    if ((lcidm->conns = lh_QUIC_LCIDM_CONN_new(lcidm_conn_hash,
111
22.2k
                                               lcidm_conn_comp)) == NULL)
112
0
        goto err;
113
114
22.2k
    lcidm->libctx   = libctx;
115
22.2k
    lcidm->lcid_len = lcid_len;
116
22.2k
    return lcidm;
117
118
0
err:
119
0
    if (lcidm != NULL) {
120
0
        lh_QUIC_LCID_free(lcidm->lcids);
121
0
        lh_QUIC_LCIDM_CONN_free(lcidm->conns);
122
0
        OPENSSL_free(lcidm);
123
0
    }
124
0
    return NULL;
125
22.2k
}
126
127
static void lcidm_delete_conn(QUIC_LCIDM *lcidm, QUIC_LCIDM_CONN *conn);
128
129
static void lcidm_delete_conn_(QUIC_LCIDM_CONN *conn, void *arg)
130
71.9k
{
131
71.9k
    lcidm_delete_conn((QUIC_LCIDM *)arg, conn);
132
71.9k
}
133
134
void ossl_quic_lcidm_free(QUIC_LCIDM *lcidm)
135
50.1k
{
136
50.1k
    if (lcidm == NULL)
137
16
        return;
138
139
    /*
140
     * Calling OPENSSL_lh_delete during a doall call is unsafe with our
141
     * current LHASH implementation for several reasons:
142
     *
143
     * - firstly, because deletes can cause the hashtable to be contracted,
144
     *   resulting in rehashing which might cause items in later buckets to
145
     *   move to earlier buckets, which might cause doall to skip an item,
146
     *   resulting in a memory leak;
147
     *
148
     * - secondly, because doall in general is not safe across hashtable
149
     *   size changes, as it caches hashtable size and pointer values
150
     *   while operating.
151
     *
152
     * The fix for this is to disable hashtable contraction using the following
153
     * call, which guarantees that no rehashing will occur so long as we only
154
     * call delete and not insert.
155
     */
156
50.0k
    lh_QUIC_LCIDM_CONN_set_down_load(lcidm->conns, 0);
157
158
50.0k
    lh_QUIC_LCIDM_CONN_doall_arg(lcidm->conns, lcidm_delete_conn_, lcidm);
159
160
50.0k
    lh_QUIC_LCID_free(lcidm->lcids);
161
50.0k
    lh_QUIC_LCIDM_CONN_free(lcidm->conns);
162
50.0k
    OPENSSL_free(lcidm);
163
50.0k
}
164
165
static QUIC_LCID *lcidm_get0_lcid(const QUIC_LCIDM *lcidm, const QUIC_CONN_ID *lcid)
166
3.36M
{
167
3.36M
    QUIC_LCID key;
168
169
3.36M
    key.cid = *lcid;
170
171
3.36M
    if (key.cid.id_len > QUIC_MAX_CONN_ID_LEN)
172
0
        return NULL;
173
174
3.36M
    return lh_QUIC_LCID_retrieve(lcidm->lcids, &key);
175
3.36M
}
176
177
static QUIC_LCIDM_CONN *lcidm_get0_conn(const QUIC_LCIDM *lcidm, void *opaque)
178
3.04M
{
179
3.04M
    QUIC_LCIDM_CONN key;
180
181
3.04M
    key.opaque = opaque;
182
183
3.04M
    return lh_QUIC_LCIDM_CONN_retrieve(lcidm->conns, &key);
184
3.04M
}
185
186
static QUIC_LCIDM_CONN *lcidm_upsert_conn(const QUIC_LCIDM *lcidm, void *opaque)
187
3.04M
{
188
3.04M
    QUIC_LCIDM_CONN *conn = lcidm_get0_conn(lcidm, opaque);
189
190
3.04M
    if (conn != NULL)
191
2.91M
        return conn;
192
193
135k
    if ((conn = OPENSSL_zalloc(sizeof(*conn))) == NULL)
194
0
        goto err;
195
196
135k
    if ((conn->lcids = lh_QUIC_LCID_new(lcid_hash, lcid_comp)) == NULL)
197
0
        goto err;
198
199
135k
    conn->opaque = opaque;
200
201
135k
    lh_QUIC_LCIDM_CONN_insert(lcidm->conns, conn);
202
135k
    if (lh_QUIC_LCIDM_CONN_error(lcidm->conns))
203
0
        goto err;
204
205
135k
    return conn;
206
207
0
err:
208
0
    if (conn != NULL) {
209
0
        lh_QUIC_LCID_free(conn->lcids);
210
0
        OPENSSL_free(conn);
211
0
    }
212
0
    return NULL;
213
135k
}
214
215
static void lcidm_delete_conn_lcid(QUIC_LCIDM *lcidm, QUIC_LCID *lcid_obj)
216
2.68M
{
217
2.68M
    lh_QUIC_LCID_delete(lcidm->lcids, lcid_obj);
218
2.68M
    lh_QUIC_LCID_delete(lcid_obj->conn->lcids, lcid_obj);
219
2.68M
    assert(lcid_obj->conn->num_active_lcid > 0);
220
2.68M
    --lcid_obj->conn->num_active_lcid;
221
2.68M
    OPENSSL_free(lcid_obj);
222
2.68M
}
223
224
/* doall_arg wrapper */
225
static void lcidm_delete_conn_lcid_(QUIC_LCID *lcid_obj, void *arg)
226
2.65M
{
227
2.65M
    lcidm_delete_conn_lcid((QUIC_LCIDM *)arg, lcid_obj);
228
2.65M
}
229
230
static void lcidm_delete_conn(QUIC_LCIDM *lcidm, QUIC_LCIDM_CONN *conn)
231
135k
{
232
    /* See comment in ossl_quic_lcidm_free */
233
135k
    lh_QUIC_LCID_set_down_load(conn->lcids, 0);
234
235
135k
    lh_QUIC_LCID_doall_arg(conn->lcids, lcidm_delete_conn_lcid_, lcidm);
236
135k
    lh_QUIC_LCIDM_CONN_delete(lcidm->conns, conn);
237
135k
    lh_QUIC_LCID_free(conn->lcids);
238
135k
    OPENSSL_free(conn);
239
135k
}
240
241
static QUIC_LCID *lcidm_conn_new_lcid(QUIC_LCIDM *lcidm, QUIC_LCIDM_CONN *conn,
242
                                      const QUIC_CONN_ID *lcid)
243
2.68M
{
244
2.68M
    QUIC_LCID *lcid_obj = NULL;
245
246
2.68M
    if (lcid->id_len > QUIC_MAX_CONN_ID_LEN)
247
0
        return NULL;
248
249
2.68M
    if ((lcid_obj = OPENSSL_zalloc(sizeof(*lcid_obj))) == NULL)
250
0
        goto err;
251
252
2.68M
    lcid_obj->cid = *lcid;
253
2.68M
    lcid_obj->conn = conn;
254
255
2.68M
    lh_QUIC_LCID_insert(conn->lcids, lcid_obj);
256
2.68M
    if (lh_QUIC_LCID_error(conn->lcids))
257
0
        goto err;
258
259
2.68M
    lh_QUIC_LCID_insert(lcidm->lcids, lcid_obj);
260
2.68M
    if (lh_QUIC_LCID_error(lcidm->lcids)) {
261
0
        lh_QUIC_LCID_delete(conn->lcids, lcid_obj);
262
0
        goto err;
263
0
    }
264
265
2.68M
    ++conn->num_active_lcid;
266
2.68M
    return lcid_obj;
267
268
0
err:
269
0
    OPENSSL_free(lcid_obj);
270
0
    return NULL;
271
2.68M
}
272
273
size_t ossl_quic_lcidm_get_lcid_len(const QUIC_LCIDM *lcidm)
274
0
{
275
0
    return lcidm->lcid_len;
276
0
}
277
278
size_t ossl_quic_lcidm_get_num_active_lcid(const QUIC_LCIDM *lcidm,
279
                                           void *opaque)
280
0
{
281
0
    QUIC_LCIDM_CONN *conn;
282
283
0
    conn = lcidm_get0_conn(lcidm, opaque);
284
0
    if (conn == NULL)
285
0
        return 0;
286
287
0
    return conn->num_active_lcid;
288
0
}
289
290
static int lcidm_generate_cid(QUIC_LCIDM *lcidm,
291
                              QUIC_CONN_ID *cid)
292
3.74M
{
293
3.74M
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
294
3.74M
    int i;
295
296
3.74M
    lcidm->next_lcid.id_len = (unsigned char)lcidm->lcid_len;
297
3.74M
    *cid = lcidm->next_lcid;
298
299
3.76M
    for (i = lcidm->lcid_len - 1; i >= 0; --i)
300
3.25M
        if (++lcidm->next_lcid.id[i] != 0)
301
3.24M
            break;
302
303
3.74M
    return 1;
304
#else
305
    return ossl_quic_gen_rand_conn_id(lcidm->libctx, lcidm->lcid_len, cid);
306
#endif
307
3.74M
}
308
309
static int lcidm_generate(QUIC_LCIDM *lcidm,
310
                          void *opaque,
311
                          unsigned int type,
312
                          QUIC_CONN_ID *lcid_out,
313
                          uint64_t *seq_num)
314
2.97M
{
315
2.97M
    QUIC_LCIDM_CONN *conn;
316
2.97M
    QUIC_LCID key, *lcid_obj;
317
2.97M
    size_t i;
318
3.88M
#define MAX_RETRIES 8
319
320
2.97M
    if ((conn = lcidm_upsert_conn(lcidm, opaque)) == NULL)
321
0
        return 0;
322
323
2.97M
    if ((type == LCID_TYPE_INITIAL && conn->next_seq_num > 0)
324
2.80M
        || conn->next_seq_num > OSSL_QUIC_VLINT_MAX)
325
163k
        return 0;
326
327
2.80M
    i = 0;
328
3.88M
    do {
329
3.88M
        if (i++ >= MAX_RETRIES)
330
            /*
331
             * Too many retries; should not happen but if it does, don't loop
332
             * endlessly.
333
             */
334
132k
            return 0;
335
336
3.74M
        if (!lcidm_generate_cid(lcidm, lcid_out))
337
0
            return 0;
338
339
3.74M
        key.cid = *lcid_out;
340
        /* If a collision occurs, retry. */
341
3.74M
    } while (lh_QUIC_LCID_retrieve(lcidm->lcids, &key) != NULL);
342
343
2.67M
    if ((lcid_obj = lcidm_conn_new_lcid(lcidm, conn, lcid_out)) == NULL)
344
0
        return 0;
345
346
2.67M
    lcid_obj->seq_num   = conn->next_seq_num;
347
2.67M
    lcid_obj->type      = type;
348
349
2.67M
    if (seq_num != NULL)
350
2.60M
        *seq_num = lcid_obj->seq_num;
351
352
2.67M
    ++conn->next_seq_num;
353
2.67M
    return 1;
354
2.67M
}
355
356
int ossl_quic_lcidm_enrol_odcid(QUIC_LCIDM *lcidm,
357
                                void *opaque,
358
                                const QUIC_CONN_ID *initial_odcid)
359
671k
{
360
671k
    QUIC_LCIDM_CONN *conn;
361
671k
    QUIC_LCID key, *lcid_obj;
362
363
671k
    if (initial_odcid == NULL || initial_odcid->id_len < QUIC_MIN_ODCID_LEN
364
29.4k
        || initial_odcid->id_len > QUIC_MAX_CONN_ID_LEN)
365
641k
        return 0;
366
367
29.4k
    if ((conn = lcidm_upsert_conn(lcidm, opaque)) == NULL)
368
0
        return 0;
369
370
29.4k
    if (conn->done_odcid)
371
10.9k
        return 0;
372
373
18.5k
    key.cid = *initial_odcid;
374
18.5k
    if (lh_QUIC_LCID_retrieve(lcidm->lcids, &key) != NULL)
375
4.68k
        return 0;
376
377
13.8k
    if ((lcid_obj = lcidm_conn_new_lcid(lcidm, conn, initial_odcid)) == NULL)
378
0
        return 0;
379
380
13.8k
    lcid_obj->seq_num       = LCIDM_ODCID_SEQ_NUM;
381
13.8k
    lcid_obj->type          = LCID_TYPE_ODCID;
382
383
13.8k
    conn->odcid_lcid_obj    = lcid_obj;
384
13.8k
    conn->done_odcid        = 1;
385
13.8k
    return 1;
386
13.8k
}
387
388
int ossl_quic_lcidm_generate_initial(QUIC_LCIDM *lcidm,
389
                                     void *opaque,
390
                                     QUIC_CONN_ID *initial_lcid)
391
254k
{
392
254k
    return lcidm_generate(lcidm, opaque, LCID_TYPE_INITIAL,
393
254k
                          initial_lcid, NULL);
394
254k
}
395
396
int ossl_quic_lcidm_generate(QUIC_LCIDM *lcidm,
397
                             void *opaque,
398
                             OSSL_QUIC_FRAME_NEW_CONN_ID *ncid_frame)
399
2.71M
{
400
2.71M
    ncid_frame->seq_num         = 0;
401
2.71M
    ncid_frame->retire_prior_to = 0;
402
403
2.71M
    return lcidm_generate(lcidm, opaque, LCID_TYPE_NCID,
404
2.71M
                          &ncid_frame->conn_id,
405
2.71M
                          &ncid_frame->seq_num);
406
2.71M
}
407
408
int ossl_quic_lcidm_retire_odcid(QUIC_LCIDM *lcidm, void *opaque)
409
49.1k
{
410
49.1k
    QUIC_LCIDM_CONN *conn;
411
412
49.1k
    if ((conn = lcidm_upsert_conn(lcidm, opaque)) == NULL)
413
0
        return 0;
414
415
49.1k
    if (conn->odcid_lcid_obj == NULL)
416
40.2k
        return 0;
417
418
8.94k
    lcidm_delete_conn_lcid(lcidm, conn->odcid_lcid_obj);
419
8.94k
    conn->odcid_lcid_obj = NULL;
420
8.94k
    return 1;
421
49.1k
}
422
423
struct retire_args {
424
    QUIC_LCID           *earliest_seq_num_lcid_obj;
425
    uint64_t            earliest_seq_num, retire_prior_to;
426
};
427
428
static void retire_for_conn(QUIC_LCID *lcid_obj, void *arg)
429
12.0M
{
430
12.0M
    struct retire_args *args = arg;
431
432
    /* ODCID LCID cannot be retired via this API */
433
12.0M
    if (lcid_obj->type == LCID_TYPE_ODCID
434
12.0M
        || lcid_obj->seq_num >= args->retire_prior_to)
435
2.22M
        return;
436
437
9.80M
    if (lcid_obj->seq_num < args->earliest_seq_num) {
438
290k
        args->earliest_seq_num          = lcid_obj->seq_num;
439
290k
        args->earliest_seq_num_lcid_obj = lcid_obj;
440
290k
    }
441
9.80M
}
442
443
int ossl_quic_lcidm_retire(QUIC_LCIDM *lcidm,
444
                           void *opaque,
445
                           uint64_t retire_prior_to,
446
                           const QUIC_CONN_ID *containing_pkt_dcid,
447
                           QUIC_CONN_ID *retired_lcid,
448
                           uint64_t *retired_seq_num,
449
                           int *did_retire)
450
93.0k
{
451
93.0k
    QUIC_LCIDM_CONN key, *conn;
452
93.0k
    struct retire_args args = {0};
453
454
93.0k
    key.opaque = opaque;
455
456
93.0k
    if (did_retire == NULL)
457
0
        return 0;
458
459
93.0k
    *did_retire = 0;
460
93.0k
    if ((conn = lh_QUIC_LCIDM_CONN_retrieve(lcidm->conns, &key)) == NULL)
461
24.9k
        return 1;
462
463
68.0k
    args.retire_prior_to    = retire_prior_to;
464
68.0k
    args.earliest_seq_num   = UINT64_MAX;
465
466
68.0k
    lh_QUIC_LCID_doall_arg(conn->lcids, retire_for_conn, &args);
467
68.0k
    if (args.earliest_seq_num_lcid_obj == NULL)
468
44.2k
        return 1;
469
470
23.8k
    if (containing_pkt_dcid != NULL
471
0
        && ossl_quic_conn_id_eq(&args.earliest_seq_num_lcid_obj->cid,
472
0
                                containing_pkt_dcid))
473
0
        return 0;
474
475
23.8k
    *did_retire = 1;
476
23.8k
    if (retired_lcid != NULL)
477
23.8k
        *retired_lcid = args.earliest_seq_num_lcid_obj->cid;
478
23.8k
    if (retired_seq_num != NULL)
479
23.8k
        *retired_seq_num = args.earliest_seq_num_lcid_obj->seq_num;
480
481
23.8k
    lcidm_delete_conn_lcid(lcidm, args.earliest_seq_num_lcid_obj);
482
23.8k
    return 1;
483
23.8k
}
484
485
int ossl_quic_lcidm_cull(QUIC_LCIDM *lcidm, void *opaque)
486
100k
{
487
100k
    QUIC_LCIDM_CONN key, *conn;
488
489
100k
    key.opaque = opaque;
490
491
100k
    if ((conn = lh_QUIC_LCIDM_CONN_retrieve(lcidm->conns, &key)) == NULL)
492
37.5k
        return 0;
493
494
63.4k
    lcidm_delete_conn(lcidm, conn);
495
63.4k
    return 1;
496
100k
}
497
498
int ossl_quic_lcidm_lookup(QUIC_LCIDM *lcidm,
499
                           const QUIC_CONN_ID *lcid,
500
                           uint64_t *seq_num,
501
                           void **opaque)
502
3.36M
{
503
3.36M
    QUIC_LCID *lcid_obj;
504
505
3.36M
    if (lcid == NULL)
506
0
        return 0;
507
508
3.36M
    if ((lcid_obj = lcidm_get0_lcid(lcidm, lcid)) == NULL)
509
352k
        return 0;
510
511
3.01M
    if (seq_num != NULL)
512
19.6k
        *seq_num        = lcid_obj->seq_num;
513
514
3.01M
    if (opaque != NULL)
515
3.01M
        *opaque         = lcid_obj->conn->opaque;
516
517
3.01M
    return 1;
518
3.36M
}
519
520
int ossl_quic_lcidm_debug_remove(QUIC_LCIDM *lcidm,
521
                                 const QUIC_CONN_ID *lcid)
522
0
{
523
0
    QUIC_LCID key, *lcid_obj;
524
525
0
    key.cid = *lcid;
526
0
    if ((lcid_obj = lh_QUIC_LCID_retrieve(lcidm->lcids, &key)) == NULL)
527
0
        return 0;
528
529
0
    lcidm_delete_conn_lcid(lcidm, lcid_obj);
530
0
    return 1;
531
0
}
532
533
int ossl_quic_lcidm_debug_add(QUIC_LCIDM *lcidm, void *opaque,
534
                              const QUIC_CONN_ID *lcid,
535
                              uint64_t seq_num)
536
0
{
537
0
    QUIC_LCIDM_CONN *conn;
538
0
    QUIC_LCID key, *lcid_obj;
539
540
0
    if (lcid == NULL || lcid->id_len > QUIC_MAX_CONN_ID_LEN)
541
0
        return 0;
542
543
0
    if ((conn = lcidm_upsert_conn(lcidm, opaque)) == NULL)
544
0
        return 0;
545
546
0
    key.cid = *lcid;
547
0
    if (lh_QUIC_LCID_retrieve(lcidm->lcids, &key) != NULL)
548
0
        return 0;
549
550
0
    if ((lcid_obj = lcidm_conn_new_lcid(lcidm, conn, lcid)) == NULL)
551
0
        return 0;
552
553
0
    lcid_obj->seq_num   = seq_num;
554
0
    lcid_obj->type      = LCID_TYPE_NCID;
555
0
    return 1;
556
0
}