Coverage Report

Created: 2025-12-04 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl32/ssl/quic/quic_fifd.c
Line
Count
Source
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_fifd.h"
11
#include "internal/quic_wire.h"
12
13
DEFINE_LIST_OF(tx_history, OSSL_ACKM_TX_PKT);
14
15
int ossl_quic_fifd_init(QUIC_FIFD *fifd,
16
                        QUIC_CFQ *cfq,
17
                        OSSL_ACKM *ackm,
18
                        QUIC_TXPIM *txpim,
19
                        /* stream_id is UINT64_MAX for the crypto stream */
20
                        QUIC_SSTREAM *(*get_sstream_by_id)(uint64_t stream_id,
21
                                                           uint32_t pn_space,
22
                                                           void *arg),
23
                        void *get_sstream_by_id_arg,
24
                        /* stream_id is UINT64_MAX if not applicable */
25
                        void (*regen_frame)(uint64_t frame_type,
26
                                            uint64_t stream_id,
27
                                            QUIC_TXPIM_PKT *pkt,
28
                                            void *arg),
29
                        void *regen_frame_arg,
30
                        void (*confirm_frame)(uint64_t frame_type,
31
                                              uint64_t stream_id,
32
                                              QUIC_TXPIM_PKT *pkt,
33
                                              void *arg),
34
                        void *confirm_frame_arg,
35
                        void (*sstream_updated)(uint64_t stream_id,
36
                                                void *arg),
37
                        void *sstream_updated_arg)
38
54.8k
{
39
54.8k
    if (cfq == NULL || ackm == NULL || txpim == NULL
40
54.8k
        || get_sstream_by_id == NULL || regen_frame == NULL)
41
0
        return 0;
42
43
54.8k
    fifd->cfq                   = cfq;
44
54.8k
    fifd->ackm                  = ackm;
45
54.8k
    fifd->txpim                 = txpim;
46
54.8k
    fifd->get_sstream_by_id     = get_sstream_by_id;
47
54.8k
    fifd->get_sstream_by_id_arg = get_sstream_by_id_arg;
48
54.8k
    fifd->regen_frame           = regen_frame;
49
54.8k
    fifd->regen_frame_arg       = regen_frame_arg;
50
54.8k
    fifd->confirm_frame         = confirm_frame;
51
54.8k
    fifd->confirm_frame_arg     = confirm_frame_arg;
52
54.8k
    fifd->sstream_updated       = sstream_updated;
53
54.8k
    fifd->sstream_updated_arg   = sstream_updated_arg;
54
54.8k
    return 1;
55
54.8k
}
56
57
void ossl_quic_fifd_cleanup(QUIC_FIFD *fifd)
58
54.8k
{
59
    /* No-op. */
60
54.8k
}
61
62
static void on_acked(void *arg)
63
906k
{
64
906k
    QUIC_TXPIM_PKT *pkt = arg;
65
906k
    QUIC_FIFD *fifd = pkt->fifd;
66
906k
    const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt);
67
906k
    size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);
68
906k
    QUIC_SSTREAM *sstream;
69
906k
    QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;
70
71
    /* STREAM and CRYPTO stream chunks, FINs and stream FC frames */
72
943k
    for (i = 0; i < num_chunks; ++i) {
73
37.3k
        sstream = fifd->get_sstream_by_id(chunks[i].stream_id,
74
37.3k
                                          pkt->ackm_pkt.pkt_space,
75
37.3k
                                          fifd->get_sstream_by_id_arg);
76
37.3k
        if (sstream == NULL)
77
1.00k
            continue;
78
79
36.3k
        if (chunks[i].end >= chunks[i].start)
80
            /* coverity[check_return]: Best effort - we cannot fail here. */
81
36.3k
            ossl_quic_sstream_mark_acked(sstream,
82
36.3k
                                         chunks[i].start, chunks[i].end);
83
84
36.3k
        if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX)
85
0
            ossl_quic_sstream_mark_acked_fin(sstream);
86
87
36.3k
        if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX)
88
0
            fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING,
89
0
                                chunks[i].stream_id, pkt,
90
0
                                fifd->confirm_frame_arg);
91
92
36.3k
        if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX)
93
0
            fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM,
94
0
                                chunks[i].stream_id, pkt,
95
0
                                fifd->confirm_frame_arg);
96
97
36.3k
        if (ossl_quic_sstream_is_totally_acked(sstream))
98
20.1k
            fifd->sstream_updated(chunks[i].stream_id, fifd->sstream_updated_arg);
99
36.3k
    }
100
101
    /* GCR */
102
929k
    for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {
103
22.6k
        cfq_item_next = cfq_item->pkt_next;
104
22.6k
        ossl_quic_cfq_release(fifd->cfq, cfq_item);
105
22.6k
    }
106
107
906k
    ossl_quic_txpim_pkt_release(fifd->txpim, pkt);
108
906k
}
109
110
static void on_lost(void *arg)
111
137k
{
112
137k
    QUIC_TXPIM_PKT *pkt = arg;
113
137k
    QUIC_FIFD *fifd = pkt->fifd;
114
137k
    const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt);
115
137k
    size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);
116
137k
    QUIC_SSTREAM *sstream;
117
137k
    QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;
118
137k
    int sstream_updated;
119
120
    /* STREAM and CRYPTO stream chunks, FIN and stream FC frames */
121
150k
    for (i = 0; i < num_chunks; ++i) {
122
12.8k
        sstream = fifd->get_sstream_by_id(chunks[i].stream_id,
123
12.8k
                                          pkt->ackm_pkt.pkt_space,
124
12.8k
                                          fifd->get_sstream_by_id_arg);
125
12.8k
        if (sstream == NULL)
126
1.86k
            continue;
127
128
10.9k
        sstream_updated = 0;
129
130
10.9k
        if (chunks[i].end >= chunks[i].start) {
131
            /*
132
             * Note: If the stream is being reset, we do not need to retransmit
133
             * old data as this is pointless. In this case this will be handled
134
             * by (sstream == NULL) above as the QSM will free the QUIC_SSTREAM
135
             * and our call to get_sstream_by_id above will return NULL.
136
             */
137
10.9k
            ossl_quic_sstream_mark_lost(sstream,
138
10.9k
                                        chunks[i].start, chunks[i].end);
139
10.9k
            sstream_updated = 1;
140
10.9k
        }
141
142
10.9k
        if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX) {
143
0
            ossl_quic_sstream_mark_lost_fin(sstream);
144
0
            sstream_updated = 1;
145
0
        }
146
147
10.9k
        if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX)
148
0
            fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING,
149
0
                              chunks[i].stream_id, pkt,
150
0
                              fifd->regen_frame_arg);
151
152
10.9k
        if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX)
153
0
            fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM,
154
0
                              chunks[i].stream_id, pkt,
155
0
                              fifd->regen_frame_arg);
156
157
        /*
158
         * Inform caller that stream needs an FC frame.
159
         *
160
         * Note: We could track whether an FC frame was sent originally for the
161
         * stream to determine if it really needs to be regenerated or not.
162
         * However, if loss has occurred, it's probably better to ensure the
163
         * peer has up-to-date flow control data for the stream. Given that
164
         * these frames are extremely small, we may as well always send it when
165
         * handling loss.
166
         */
167
10.9k
        fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA,
168
10.9k
                          chunks[i].stream_id,
169
10.9k
                          pkt,
170
10.9k
                          fifd->regen_frame_arg);
171
172
10.9k
        if (sstream_updated && chunks[i].stream_id != UINT64_MAX)
173
2.69k
            fifd->sstream_updated(chunks[i].stream_id,
174
2.69k
                                  fifd->sstream_updated_arg);
175
10.9k
    }
176
177
    /* GCR */
178
192k
    for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {
179
55.3k
        cfq_item_next = cfq_item->pkt_next;
180
55.3k
        ossl_quic_cfq_mark_lost(fifd->cfq, cfq_item, UINT32_MAX);
181
55.3k
    }
182
183
    /* Regenerate flag frames */
184
137k
    if (pkt->had_handshake_done_frame)
185
0
        fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE,
186
0
                          UINT64_MAX, pkt,
187
0
                          fifd->regen_frame_arg);
188
189
137k
    if (pkt->had_max_data_frame)
190
0
        fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_DATA,
191
0
                          UINT64_MAX, pkt,
192
0
                          fifd->regen_frame_arg);
193
194
137k
    if (pkt->had_max_streams_bidi_frame)
195
0
        fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI,
196
0
                          UINT64_MAX, pkt,
197
0
                          fifd->regen_frame_arg);
198
199
137k
    if (pkt->had_max_streams_uni_frame)
200
0
        fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI,
201
0
                          UINT64_MAX, pkt,
202
0
                          fifd->regen_frame_arg);
203
204
137k
    if (pkt->had_ack_frame)
205
        /*
206
         * We always use the ACK_WITH_ECN frame type to represent the ACK frame
207
         * type in our callback; we assume it is the caller's job to decide
208
         * whether it wants to send ECN data or not.
209
         */
210
18.0k
        fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN,
211
18.0k
                          UINT64_MAX, pkt,
212
18.0k
                          fifd->regen_frame_arg);
213
214
137k
    ossl_quic_txpim_pkt_release(fifd->txpim, pkt);
215
137k
}
216
217
static void on_discarded(void *arg)
218
891k
{
219
891k
    QUIC_TXPIM_PKT *pkt = arg;
220
891k
    QUIC_FIFD *fifd = pkt->fifd;
221
891k
    QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;
222
223
    /*
224
     * Don't need to do anything to SSTREAMs for STREAM and CRYPTO streams, as
225
     * we assume caller will clean them up.
226
     */
227
228
    /* GCR */
229
1.07M
    for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {
230
179k
        cfq_item_next = cfq_item->pkt_next;
231
179k
        ossl_quic_cfq_release(fifd->cfq, cfq_item);
232
179k
    }
233
234
891k
    ossl_quic_txpim_pkt_release(fifd->txpim, pkt);
235
891k
}
236
237
int ossl_quic_fifd_pkt_commit(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt)
238
1.93M
{
239
1.93M
    QUIC_CFQ_ITEM *cfq_item;
240
1.93M
    const QUIC_TXPIM_CHUNK *chunks;
241
1.93M
    size_t i, num_chunks;
242
1.93M
    QUIC_SSTREAM *sstream;
243
244
1.93M
    pkt->fifd                   = fifd;
245
246
1.93M
    pkt->ackm_pkt.on_lost       = on_lost;
247
1.93M
    pkt->ackm_pkt.on_acked      = on_acked;
248
1.93M
    pkt->ackm_pkt.on_discarded  = on_discarded;
249
1.93M
    pkt->ackm_pkt.cb_arg        = pkt;
250
251
1.93M
    ossl_list_tx_history_init_elem(&pkt->ackm_pkt);
252
1.93M
    pkt->ackm_pkt.anext = pkt->ackm_pkt.lnext = NULL;
253
254
    /*
255
     * Mark the CFQ items which have been added to this packet as having been
256
     * transmitted.
257
     */
258
1.93M
    for (cfq_item = pkt->retx_head;
259
2.19M
         cfq_item != NULL;
260
1.93M
         cfq_item = cfq_item->pkt_next)
261
257k
        ossl_quic_cfq_mark_tx(fifd->cfq, cfq_item);
262
263
    /*
264
     * Mark the send stream chunks which have been added to the packet as having
265
     * been transmitted.
266
     */
267
1.93M
    chunks = ossl_quic_txpim_pkt_get_chunks(pkt);
268
1.93M
    num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);
269
2.05M
    for (i = 0; i < num_chunks; ++i) {
270
115k
        sstream = fifd->get_sstream_by_id(chunks[i].stream_id,
271
115k
                                          pkt->ackm_pkt.pkt_space,
272
115k
                                          fifd->get_sstream_by_id_arg);
273
115k
        if (sstream == NULL)
274
6.59k
            continue;
275
276
108k
        if (chunks[i].end >= chunks[i].start
277
108k
            && !ossl_quic_sstream_mark_transmitted(sstream,
278
108k
                                                   chunks[i].start,
279
108k
                                                   chunks[i].end))
280
0
            return 0;
281
282
108k
        if (chunks[i].has_fin
283
0
            && !ossl_quic_sstream_mark_transmitted_fin(sstream,
284
0
                                                       chunks[i].end + 1))
285
0
                return 0;
286
108k
    }
287
288
    /* Inform the ACKM. */
289
1.93M
    return ossl_ackm_on_tx_packet(fifd->ackm, &pkt->ackm_pkt);
290
1.93M
}