Coverage Report

Created: 2025-06-13 06:58

/src/openssl32/ssl/quic/quic_fc.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2022-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_fc.h"
11
#include "internal/quic_error.h"
12
#include "internal/common.h"
13
#include "internal/safe_math.h"
14
#include <assert.h>
15
16
OSSL_SAFE_MATH_UNSIGNED(uint64_t, uint64_t)
17
18
/*
19
 * TX Flow Controller (TXFC)
20
 * =========================
21
 */
22
23
int ossl_quic_txfc_init(QUIC_TXFC *txfc, QUIC_TXFC *conn_txfc)
24
83.2k
{
25
83.2k
    if (conn_txfc != NULL && conn_txfc->parent != NULL)
26
0
        return 0;
27
28
83.2k
    txfc->swm                   = 0;
29
83.2k
    txfc->cwm                   = 0;
30
83.2k
    txfc->parent                = conn_txfc;
31
83.2k
    txfc->has_become_blocked    = 0;
32
83.2k
    return 1;
33
83.2k
}
34
35
QUIC_TXFC *ossl_quic_txfc_get_parent(QUIC_TXFC *txfc)
36
0
{
37
0
    return txfc->parent;
38
0
}
39
40
int ossl_quic_txfc_bump_cwm(QUIC_TXFC *txfc, uint64_t cwm)
41
43.2k
{
42
43.2k
    if (cwm <= txfc->cwm)
43
17.2k
        return 0;
44
45
26.0k
    txfc->cwm = cwm;
46
26.0k
    return 1;
47
43.2k
}
48
49
uint64_t ossl_quic_txfc_get_credit_local(QUIC_TXFC *txfc, uint64_t consumed)
50
38.6k
{
51
38.6k
    assert((txfc->swm + consumed) <= txfc->cwm);
52
38.6k
    return txfc->cwm - (consumed + txfc->swm);
53
38.6k
}
54
55
uint64_t ossl_quic_txfc_get_credit(QUIC_TXFC *txfc, uint64_t consumed)
56
17.6k
{
57
17.6k
    uint64_t r, conn_r;
58
59
17.6k
    r = ossl_quic_txfc_get_credit_local(txfc, 0);
60
61
17.6k
    if (txfc->parent != NULL) {
62
17.6k
        assert(txfc->parent->parent == NULL);
63
17.6k
        conn_r = ossl_quic_txfc_get_credit_local(txfc->parent, consumed);
64
17.6k
        if (conn_r < r)
65
9.14k
            r = conn_r;
66
17.6k
    }
67
68
17.6k
    return r;
69
17.6k
}
70
71
int ossl_quic_txfc_consume_credit_local(QUIC_TXFC *txfc, uint64_t num_bytes)
72
3.35k
{
73
3.35k
    int ok = 1;
74
3.35k
    uint64_t credit = ossl_quic_txfc_get_credit_local(txfc, 0);
75
76
3.35k
    if (num_bytes > credit) {
77
0
        ok = 0;
78
0
        num_bytes = credit;
79
0
    }
80
81
3.35k
    if (num_bytes > 0 && num_bytes == credit)
82
368
        txfc->has_become_blocked = 1;
83
84
3.35k
    txfc->swm += num_bytes;
85
3.35k
    return ok;
86
3.35k
}
87
88
int ossl_quic_txfc_consume_credit(QUIC_TXFC *txfc, uint64_t num_bytes)
89
1.67k
{
90
1.67k
    int ok = ossl_quic_txfc_consume_credit_local(txfc, num_bytes);
91
92
1.67k
    if (txfc->parent != NULL) {
93
1.67k
        assert(txfc->parent->parent == NULL);
94
1.67k
        if (!ossl_quic_txfc_consume_credit_local(txfc->parent, num_bytes))
95
0
            return 0;
96
1.67k
    }
97
98
1.67k
    return ok;
99
1.67k
}
100
101
int ossl_quic_txfc_has_become_blocked(QUIC_TXFC *txfc, int clear)
102
0
{
103
0
    int r = txfc->has_become_blocked;
104
105
0
    if (clear)
106
0
        txfc->has_become_blocked = 0;
107
108
0
    return r;
109
0
}
110
111
uint64_t ossl_quic_txfc_get_cwm(QUIC_TXFC *txfc)
112
6.08k
{
113
6.08k
    return txfc->cwm;
114
6.08k
}
115
116
uint64_t ossl_quic_txfc_get_swm(QUIC_TXFC *txfc)
117
35.3k
{
118
35.3k
    return txfc->swm;
119
35.3k
}
120
121
/*
122
 * RX Flow Controller (RXFC)
123
 * =========================
124
 */
125
126
int ossl_quic_rxfc_init(QUIC_RXFC *rxfc, QUIC_RXFC *conn_rxfc,
127
                        uint64_t initial_window_size,
128
                        uint64_t max_window_size,
129
                        OSSL_TIME (*now)(void *now_arg),
130
                        void *now_arg)
131
193k
{
132
193k
    if (conn_rxfc != NULL && conn_rxfc->parent != NULL)
133
0
        return 0;
134
135
193k
    rxfc->swm               = 0;
136
193k
    rxfc->cwm               = initial_window_size;
137
193k
    rxfc->rwm               = 0;
138
193k
    rxfc->esrwm             = 0;
139
193k
    rxfc->hwm               = 0;
140
193k
    rxfc->cur_window_size   = initial_window_size;
141
193k
    rxfc->max_window_size   = max_window_size;
142
193k
    rxfc->parent            = conn_rxfc;
143
193k
    rxfc->error_code        = 0;
144
193k
    rxfc->has_cwm_changed   = 0;
145
193k
    rxfc->epoch_start       = ossl_time_zero();
146
193k
    rxfc->now               = now;
147
193k
    rxfc->now_arg           = now_arg;
148
193k
    rxfc->is_fin            = 0;
149
193k
    rxfc->standalone        = 0;
150
193k
    return 1;
151
193k
}
152
153
int ossl_quic_rxfc_init_standalone(QUIC_RXFC *rxfc,
154
                                   uint64_t initial_window_size,
155
                                   OSSL_TIME (*now)(void *arg),
156
                                   void *now_arg)
157
110k
{
158
110k
    if (!ossl_quic_rxfc_init(rxfc, NULL,
159
110k
                             initial_window_size, initial_window_size,
160
110k
                             now, now_arg))
161
0
        return 0;
162
163
110k
    rxfc->standalone = 1;
164
110k
    return 1;
165
110k
}
166
167
QUIC_RXFC *ossl_quic_rxfc_get_parent(QUIC_RXFC *rxfc)
168
0
{
169
0
    return rxfc->parent;
170
0
}
171
172
void ossl_quic_rxfc_set_max_window_size(QUIC_RXFC *rxfc,
173
                                        size_t max_window_size)
174
0
{
175
0
    rxfc->max_window_size = max_window_size;
176
0
}
177
178
static void rxfc_start_epoch(QUIC_RXFC *rxfc)
179
29.4k
{
180
29.4k
    rxfc->epoch_start   = rxfc->now(rxfc->now_arg);
181
29.4k
    rxfc->esrwm         = rxfc->rwm;
182
29.4k
}
183
184
static int on_rx_controlled_bytes(QUIC_RXFC *rxfc, uint64_t num_bytes)
185
50.5k
{
186
50.5k
    int ok = 1;
187
50.5k
    uint64_t credit = rxfc->cwm - rxfc->swm;
188
189
50.5k
    if (num_bytes > credit) {
190
246
        ok = 0;
191
246
        num_bytes = credit;
192
246
        rxfc->error_code = QUIC_ERR_FLOW_CONTROL_ERROR;
193
246
    }
194
195
50.5k
    rxfc->swm += num_bytes;
196
50.5k
    return ok;
197
50.5k
}
198
199
int ossl_quic_rxfc_on_rx_stream_frame(QUIC_RXFC *rxfc, uint64_t end, int is_fin)
200
130k
{
201
130k
    uint64_t delta;
202
203
130k
    if (!rxfc->standalone && rxfc->parent == NULL)
204
0
        return 0;
205
206
130k
    if (rxfc->is_fin && ((is_fin && rxfc->hwm != end) || end > rxfc->hwm)) {
207
        /* Stream size cannot change after the stream is finished */
208
169
        rxfc->error_code = QUIC_ERR_FINAL_SIZE_ERROR;
209
169
        return 1; /* not a caller error */
210
169
    }
211
212
130k
    if (is_fin)
213
27.7k
        rxfc->is_fin = 1;
214
215
130k
    if (end > rxfc->hwm) {
216
44.8k
        delta = end - rxfc->hwm;
217
44.8k
        rxfc->hwm = end;
218
219
44.8k
        on_rx_controlled_bytes(rxfc, delta);             /* result ignored */
220
44.8k
        if (rxfc->parent != NULL)
221
5.63k
            on_rx_controlled_bytes(rxfc->parent, delta); /* result ignored */
222
85.7k
    } else if (end < rxfc->hwm && is_fin) {
223
10
        rxfc->error_code = QUIC_ERR_FINAL_SIZE_ERROR;
224
10
        return 1; /* not a caller error */
225
10
    }
226
227
130k
    return 1;
228
130k
}
229
230
/* threshold = 3/4 */
231
37.6k
#define WINDOW_THRESHOLD_NUM 3
232
37.6k
#define WINDOW_THRESHOLD_DEN 4
233
234
static int rxfc_cwm_bump_desired(QUIC_RXFC *rxfc)
235
37.6k
{
236
37.6k
    int err = 0;
237
37.6k
    uint64_t window_rem = rxfc->cwm - rxfc->rwm;
238
37.6k
    uint64_t threshold
239
37.6k
        = safe_muldiv_uint64_t(rxfc->cur_window_size,
240
37.6k
                               WINDOW_THRESHOLD_NUM, WINDOW_THRESHOLD_DEN, &err);
241
242
37.6k
    if (err)
243
        /*
244
         * Extremely large window should never occur, but if it does, just use
245
         * 1/2 as the threshold.
246
         */
247
0
        threshold = rxfc->cur_window_size / 2;
248
249
    /*
250
     * No point emitting a new MAX_STREAM_DATA frame if the stream has a final
251
     * size.
252
     */
253
37.6k
    return !rxfc->is_fin && window_rem <= threshold;
254
37.6k
}
255
256
static int rxfc_should_bump_window_size(QUIC_RXFC *rxfc, OSSL_TIME rtt)
257
0
{
258
    /*
259
     * dt: time since start of epoch
260
     * b:  bytes of window consumed since start of epoch
261
     * dw: proportion of window consumed since start of epoch
262
     * T_window: time it will take to use up the entire window, based on dt, dw
263
     * RTT: The current estimated RTT.
264
     *
265
     * b        = rwm - esrwm
266
     * dw       = b / window_size
267
     * T_window = dt / dw
268
     * T_window = dt / (b / window_size)
269
     * T_window = (dt * window_size) / b
270
     *
271
     * We bump the window size if T_window < 4 * RTT.
272
     *
273
     * We leave the division by b on the LHS to reduce the risk of overflowing
274
     * our 64-bit nanosecond representation, which will afford plenty of
275
     * precision left over after the division anyway.
276
     */
277
0
    uint64_t  b = rxfc->rwm - rxfc->esrwm;
278
0
    OSSL_TIME now, dt, t_window;
279
280
0
    if (b == 0)
281
0
        return 0;
282
283
0
    now      = rxfc->now(rxfc->now_arg);
284
0
    dt       = ossl_time_subtract(now, rxfc->epoch_start);
285
0
    t_window = ossl_time_muldiv(dt, rxfc->cur_window_size, b);
286
287
0
    return ossl_time_compare(t_window, ossl_time_multiply(rtt, 4)) < 0;
288
0
}
289
290
static void rxfc_adjust_window_size(QUIC_RXFC *rxfc, uint64_t min_window_size,
291
                                    OSSL_TIME rtt)
292
0
{
293
    /* Are we sending updates too often? */
294
0
    uint64_t new_window_size;
295
296
0
    new_window_size = rxfc->cur_window_size;
297
298
0
    if (rxfc_should_bump_window_size(rxfc, rtt))
299
0
        new_window_size *= 2;
300
301
0
    if (new_window_size < min_window_size)
302
0
        new_window_size = min_window_size;
303
0
    if (new_window_size > rxfc->max_window_size) /* takes precedence over min size */
304
0
        new_window_size = rxfc->max_window_size;
305
306
0
    rxfc->cur_window_size = new_window_size;
307
0
    rxfc_start_epoch(rxfc);
308
0
}
309
310
static void rxfc_update_cwm(QUIC_RXFC *rxfc, uint64_t min_window_size,
311
                            OSSL_TIME rtt)
312
37.6k
{
313
37.6k
    uint64_t new_cwm;
314
315
37.6k
    if (!rxfc_cwm_bump_desired(rxfc))
316
37.6k
        return;
317
318
0
    rxfc_adjust_window_size(rxfc, min_window_size, rtt);
319
320
0
    new_cwm = rxfc->rwm + rxfc->cur_window_size;
321
0
    if (new_cwm > rxfc->cwm) {
322
0
        rxfc->cwm = new_cwm;
323
0
        rxfc->has_cwm_changed = 1;
324
0
    }
325
0
}
326
327
static int rxfc_on_retire(QUIC_RXFC *rxfc, uint64_t num_bytes,
328
                          uint64_t min_window_size,
329
                          OSSL_TIME rtt)
330
37.6k
{
331
37.6k
    if (ossl_time_is_zero(rxfc->epoch_start))
332
        /* This happens when we retire our first ever bytes. */
333
29.4k
        rxfc_start_epoch(rxfc);
334
335
37.6k
    rxfc->rwm += num_bytes;
336
37.6k
    rxfc_update_cwm(rxfc, min_window_size, rtt);
337
37.6k
    return 1;
338
37.6k
}
339
340
int ossl_quic_rxfc_on_retire(QUIC_RXFC *rxfc,
341
                             uint64_t num_bytes,
342
                             OSSL_TIME rtt)
343
35.6k
{
344
35.6k
    if (rxfc->parent == NULL && !rxfc->standalone)
345
0
        return 0;
346
347
35.6k
    if (num_bytes == 0)
348
0
        return 1;
349
350
35.6k
    if (rxfc->rwm + num_bytes > rxfc->swm)
351
        /* Impossible for us to retire more bytes than we have received. */
352
0
        return 0;
353
354
35.6k
    rxfc_on_retire(rxfc, num_bytes, 0, rtt);
355
356
35.6k
    if (!rxfc->standalone)
357
2.00k
        rxfc_on_retire(rxfc->parent, num_bytes, rxfc->cur_window_size, rtt);
358
359
35.6k
    return 1;
360
35.6k
}
361
362
uint64_t ossl_quic_rxfc_get_cwm(QUIC_RXFC *rxfc)
363
66.9k
{
364
66.9k
    return rxfc->cwm;
365
66.9k
}
366
367
uint64_t ossl_quic_rxfc_get_swm(QUIC_RXFC *rxfc)
368
0
{
369
0
    return rxfc->swm;
370
0
}
371
372
uint64_t ossl_quic_rxfc_get_rwm(QUIC_RXFC *rxfc)
373
0
{
374
0
    return rxfc->rwm;
375
0
}
376
377
int ossl_quic_rxfc_has_cwm_changed(QUIC_RXFC *rxfc, int clear)
378
8.24M
{
379
8.24M
    int r = rxfc->has_cwm_changed;
380
381
8.24M
    if (clear)
382
563
        rxfc->has_cwm_changed = 0;
383
384
8.24M
    return r;
385
8.24M
}
386
387
int ossl_quic_rxfc_get_error(QUIC_RXFC *rxfc, int clear)
388
130k
{
389
130k
    int r = rxfc->error_code;
390
391
130k
    if (clear)
392
0
        rxfc->error_code = 0;
393
394
130k
    return r;
395
130k
}
396
397
int ossl_quic_rxfc_get_final_size(const QUIC_RXFC *rxfc, uint64_t *final_size)
398
16.6k
{
399
16.6k
    if (!rxfc->is_fin)
400
0
        return 0;
401
402
16.6k
    if (final_size != NULL)
403
6
        *final_size = rxfc->hwm;
404
405
16.6k
    return 1;
406
16.6k
}