/src/openssl35/ssl/quic/quic_strm_reas.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2022-2026 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/uint_set.h" |
11 | | #include "internal/common.h" |
12 | | #include "internal/quic_stream.h" |
13 | | #include "internal/quic_strm_reas.h" |
14 | | #include "internal/list.h" |
15 | | #include "internal/quic_channel.h" |
16 | | |
17 | 144M | #define STDERR NULL |
18 | | /* ARGSUSED */ |
19 | | static void print_foo(void *f, ...) |
20 | 58.6M | { |
21 | 58.6M | } |
22 | | |
23 | 144M | #define DEBUG_PRINT print_foo |
24 | | |
25 | 0 | #define DIRECT_STORAGE_SZ (2 * sizeof(void *)) |
26 | | |
27 | | /* |
28 | | * Maximal allocation overhead in packet buffers is ~64kB for |
29 | | * connection. If ~64kB limit is exceeded, then the newly received |
30 | | * chunks are moved from the packet to the stream buffer. |
31 | | */ |
32 | 53.0k | #define PKT_BUFFER_OVERHEAD_TRESHOLD (65535) |
33 | | |
34 | | /* |
35 | | * storage type indicates where stream data bytes |
36 | | * are stored. |
37 | | */ |
38 | | enum { |
39 | | ST_TYPE_DIRECT, /* in chunk structure itself (sc_dstorage) */ |
40 | | ST_TYPE_PKT, /* bytes are stored in attached pkt (sc_pkt) */ |
41 | | ST_TYPE_HEAP /* data are stored on memory heap buffer */ |
42 | | }; |
43 | | |
44 | | /* |
45 | | * Stream chunk keeps stream bytes as received from QUIC STREAM_FRAME. |
46 | | * Each chunk of stream data by [start, end). |
47 | | */ |
48 | | struct stream_chunk_t { |
49 | | OSSL_LIST_MEMBER(sc, struct stream_chunk_t); |
50 | | UINT_RANGE sc_range; |
51 | | int sc_st; /* storage type */ |
52 | | union { |
53 | | const unsigned char *u_data; |
54 | | unsigned char *u_data_w; |
55 | | } sc_data_u; |
56 | | union { |
57 | | OSSL_QRX_PKT *u_sc_pkt; |
58 | | unsigned char *u_sc_buf; |
59 | | unsigned char u_sc_dstorage[DIRECT_STORAGE_SZ]; |
60 | | } sc_storage_u; |
61 | | }; |
62 | | |
63 | | struct quic_rstream_qparm_st { |
64 | | size_t rsqp_pkt_overhead_treshold; |
65 | | size_t rsqp_pkt_overhead_sz; |
66 | | QUIC_CHANNEL *rsqp_ch; |
67 | | }; |
68 | | |
69 | 186k | #define sc_data sc_data_u.u_data |
70 | 0 | #define sc_data_w sc_data_u.u_data_w |
71 | | |
72 | 207k | #define sc_pkt sc_storage_u.u_sc_pkt |
73 | 0 | #define sc_buf sc_storage_u.u_sc_buf |
74 | 0 | #define sc_dstorage sc_storage_u.u_sc_dstorage |
75 | | |
76 | 239M | DEFINE_LIST_OF(sc, struct stream_chunk_t); quic_strm_reas.c:ossl_list_sc_insert_head Line | Count | Source | 76 | | DEFINE_LIST_OF(sc, struct stream_chunk_t); |
quic_strm_reas.c:ossl_list_sc_tail Line | Count | Source | 76 | | DEFINE_LIST_OF(sc, struct stream_chunk_t); |
quic_strm_reas.c:ossl_list_sc_insert_tail Line | Count | Source | 76 | | DEFINE_LIST_OF(sc, struct stream_chunk_t); |
quic_strm_reas.c:ossl_list_sc_join Line | Count | Source | 76 | | DEFINE_LIST_OF(sc, struct stream_chunk_t); |
quic_strm_reas.c:ossl_list_sc_head Line | Count | Source | 76 | | DEFINE_LIST_OF(sc, struct stream_chunk_t); |
quic_strm_reas.c:ossl_list_sc_next Line | Count | Source | 76 | | DEFINE_LIST_OF(sc, struct stream_chunk_t); |
quic_strm_reas.c:ossl_list_sc_remove Line | Count | Source | 76 | | DEFINE_LIST_OF(sc, struct stream_chunk_t); |
|
77 | 239M | |
78 | 239M | #define SCHUNK_SIZE(_sc) ((_sc)->sc_range.end - (_sc)->sc_range.start) |
79 | | #define SRANGE_SIZE(_sr) ((_sr)->sr_range.end - (_sr)->sr_range.start) |
80 | 414k | #define SCHUNK_OVERHEAD(_pkt, _sc) ((_pkt)->datagram_len - SCHUNK_SIZE(_sc)) |
81 | | |
82 | | /* |
83 | | * Stream range keeps list of continuous stream chunks. The range |
84 | | * is also defined by [start, end) interval. For every chunk |
85 | | * in range this assertion must hold: |
86 | | * sc->sc_range.end == sc->sc_next->sc_range.start |
87 | | * |
88 | | * If newly arriving stream chunk can not be inserted to existing |
89 | | * stream range, then new range must be created. |
90 | | */ |
91 | | struct stream_range_t { |
92 | | OSSL_LIST(sc) |
93 | | sr_chunks; |
94 | | OSSL_RBT_ENTRY(stream_range_t) |
95 | | sr_rbe; |
96 | | UINT_RANGE sr_range; |
97 | | struct stream_chunk_t *sr_it_sc; /* iterator */ |
98 | | }; |
99 | | |
100 | | static int srange_cmp(const struct stream_range_t *, const struct stream_range_t *); |
101 | | |
102 | | OSSL_RBT_PROTOTYPE(srange, stream_range_t, sr_rbe, srange_cmp) |
103 | | |
104 | | OSSL_RBT_GENERATE(srange, stream_range_t, sr_rbe, srange_cmp); |
105 | | |
106 | 212k | #define UINT64_TO_SIZE_T(_x) ((size_t)(((_x) > SIZE_MAX) ? SIZE_MAX : (_x))) |
107 | | |
108 | | static void rsqp_add_overhead(QUIC_RSTREAM_QPARM *rsqp, size_t sc_overhead) |
109 | 107k | { |
110 | 107k | rsqp->rsqp_pkt_overhead_sz += sc_overhead; |
111 | 107k | } |
112 | | |
113 | | static void rsqp_sub_overhead(QUIC_RSTREAM_QPARM *rsqp, size_t sc_overhead) |
114 | 103k | { |
115 | 103k | rsqp->rsqp_pkt_overhead_sz -= sc_overhead; |
116 | 103k | } |
117 | | |
118 | | /* |
119 | | * Cleansing (SSL_OP_CLEANSE_PLAINTEXT) must write through the const |
120 | | * data pointers received from ossl_sframe_set_insert(), which may |
121 | | * point into a shared packet buffer. That is safe: each chunk |
122 | | * references the disjoint payload slice of its own frame and a |
123 | | * processed packet is kept alive only by the chunks stored on it, |
124 | | * so nobody else reads the wiped bytes. |
125 | | * |
126 | | * The const should eventually be dropped from the prototypes |
127 | | * instead; until then deconst() is used. |
128 | | */ |
129 | | static unsigned char *deconst(const unsigned char *data) |
130 | 0 | { |
131 | 0 | union { |
132 | 0 | const unsigned char *u_data; |
133 | 0 | unsigned char *u_data_w; |
134 | 0 | } data_u; |
135 | |
|
136 | 0 | data_u.u_data = data; |
137 | |
|
138 | 0 | return data_u.u_data_w; |
139 | 0 | } |
140 | | |
141 | | static void sc_data_trim_left(struct stream_chunk_t *sc, size_t trim_sz, |
142 | | int cleanse) |
143 | 3.57k | { |
144 | 3.57k | if (sc->sc_st == ST_TYPE_DIRECT) { |
145 | 0 | assert(SCHUNK_SIZE(sc) >= trim_sz); |
146 | 0 | memmove(sc->sc_data_w, &sc->sc_data[trim_sz], |
147 | 0 | UINT64_TO_SIZE_T((SCHUNK_SIZE(sc) - trim_sz))); |
148 | 0 | if (cleanse && trim_sz > 0) { |
149 | 0 | OPENSSL_cleanse( |
150 | 0 | sc->sc_data_w + UINT64_TO_SIZE_T(SCHUNK_SIZE(sc) - trim_sz), |
151 | 0 | UINT64_TO_SIZE_T(trim_sz)); |
152 | 0 | } |
153 | 3.57k | } else { |
154 | 3.57k | if (cleanse && trim_sz > 0) { |
155 | 0 | OPENSSL_cleanse(sc->sc_data_w, trim_sz); |
156 | 0 | } |
157 | 3.57k | sc->sc_data += trim_sz; |
158 | 3.57k | } |
159 | 3.57k | } |
160 | | |
161 | | static void sc_data_trim_right(struct stream_chunk_t *sc, size_t trim_sz, |
162 | | int cleanse) |
163 | 570 | { |
164 | 570 | unsigned char *data_realloc; |
165 | 570 | size_t w_offset; |
166 | | |
167 | 570 | assert(SCHUNK_SIZE(sc) >= trim_sz); |
168 | | |
169 | 570 | if (cleanse) |
170 | 0 | OPENSSL_cleanse( |
171 | 0 | &sc->sc_data_w[sc->sc_range.end - trim_sz - sc->sc_range.start], |
172 | 0 | trim_sz); |
173 | | |
174 | 570 | if (sc->sc_st == ST_TYPE_HEAP) { |
175 | | /* |
176 | | * this is a shrinking realloc() here, so it should not fail. |
177 | | * even if it fails, we still don't care, the worst outcome |
178 | | * of such failure is waste of memory. |
179 | | */ |
180 | 0 | assert(sc->sc_data_w >= sc->sc_buf); |
181 | 0 | w_offset = sc->sc_data_w - sc->sc_buf; |
182 | 0 | if (trim_sz > 0) { |
183 | 0 | assert(SCHUNK_SIZE(sc) > trim_sz); |
184 | 0 | data_realloc = OPENSSL_realloc(sc->sc_buf, |
185 | 0 | w_offset + UINT64_TO_SIZE_T(SCHUNK_SIZE(sc)) - trim_sz); |
186 | 0 | if (data_realloc != NULL) { |
187 | 0 | sc->sc_buf = data_realloc; |
188 | 0 | sc->sc_data_w = sc->sc_buf + w_offset; |
189 | 0 | } |
190 | 0 | } |
191 | 0 | } |
192 | 570 | } |
193 | | |
194 | | static int srange_cmp(const struct stream_range_t *a_sr, |
195 | | const struct stream_range_t *b_sr) |
196 | 190k | { |
197 | 190k | assert(a_sr->sr_range.start < a_sr->sr_range.end); |
198 | 190k | assert(b_sr->sr_range.start < b_sr->sr_range.end); |
199 | | /* |
200 | | * no overlap, A precedes B |
201 | | */ |
202 | 190k | if (a_sr->sr_range.end < b_sr->sr_range.start) |
203 | 66.0k | return -1; |
204 | | |
205 | | /* |
206 | | * no overlap, A follows B |
207 | | */ |
208 | 124k | if (a_sr->sr_range.start > b_sr->sr_range.end) |
209 | 29.1k | return 1; |
210 | | |
211 | | /* |
212 | | * partial or full overlap or ranges are adjacent. |
213 | | * the program needs to do close examination on |
214 | | * how to add new chunk to existing stream range. |
215 | | */ |
216 | 95.2k | return 0; |
217 | 124k | } |
218 | | |
219 | | static int keep_schunk_data_on_packet(SFRAME_SET *fs, OSSL_QRX_PKT *pkt, |
220 | | size_t overhead) |
221 | 103k | { |
222 | 103k | if ((fs->rsqp->rsqp_pkt_overhead_sz + overhead) >= fs->rsqp->rsqp_pkt_overhead_treshold) |
223 | 0 | return 0; |
224 | | |
225 | 103k | return 1; |
226 | 103k | } |
227 | | |
228 | | static struct stream_chunk_t *new_schunk(SFRAME_SET *fs, OSSL_QRX_PKT *pkt, |
229 | | UINT_RANGE *r, const unsigned char *data) |
230 | 103k | { |
231 | 103k | struct stream_chunk_t *sc; |
232 | 103k | uint64_t rsize; |
233 | 103k | size_t overhead; |
234 | | |
235 | 103k | if (pkt == NULL) |
236 | 0 | return NULL; |
237 | | |
238 | 103k | sc = OPENSSL_zalloc(sizeof(*sc)); |
239 | 103k | if (sc == NULL) |
240 | 0 | return NULL; |
241 | | |
242 | 103k | rsize = r->end - r->start; |
243 | 103k | assert(rsize <= pkt->datagram_len); |
244 | 103k | overhead = UINT64_TO_SIZE_T(pkt->datagram_len - rsize); |
245 | | |
246 | 103k | if (keep_schunk_data_on_packet(fs, pkt, overhead) == 1) { |
247 | 103k | sc->sc_st = ST_TYPE_PKT; |
248 | 103k | sc->sc_pkt = pkt; |
249 | 103k | ossl_qrx_pkt_up_ref(pkt); |
250 | 103k | rsqp_add_overhead(fs->rsqp, overhead); |
251 | 103k | sc->sc_data = data; |
252 | 103k | sc->sc_range = *r; |
253 | 103k | DEBUG_PRINT(STDERR, |
254 | 103k | "%s sc: %p sc overhead: %llu pkt_buf_overhead_sz: %llu -> %zu\n", |
255 | 103k | OPENSSL_FUNC, (void *)sc, SCHUNK_OVERHEAD(pkt, sc), |
256 | 103k | fs->rsqp->rsqp_pkt_overhead_sz - SCHUNK_OVERHEAD(pkt, sc), |
257 | 103k | fs->rsqp->rsqp_pkt_overhead_sz); |
258 | 103k | } else { |
259 | 0 | if (rsize <= DIRECT_STORAGE_SZ) { |
260 | 0 | DEBUG_PRINT(STDERR, "%s ST_TYPE_DIRECT sc: %p %llu\n", OPENSSL_FUNC, |
261 | 0 | (void *)sc, rsize); |
262 | 0 | sc->sc_st = ST_TYPE_DIRECT; |
263 | 0 | sc->sc_data_w = sc->sc_dstorage; |
264 | 0 | } else { |
265 | 0 | DEBUG_PRINT(STDERR, "%s ST_TYPE_HEAP sc: %p %llu\n", OPENSSL_FUNC, |
266 | 0 | (void *)sc, rsize); |
267 | 0 | sc->sc_st = ST_TYPE_HEAP; |
268 | 0 | sc->sc_buf = OPENSSL_malloc(UINT64_TO_SIZE_T(rsize)); |
269 | 0 | if (sc->sc_buf == NULL) { |
270 | 0 | OPENSSL_free(sc); |
271 | 0 | return NULL; |
272 | 0 | } |
273 | 0 | sc->sc_data_w = sc->sc_buf; |
274 | 0 | } |
275 | 0 | sc->sc_range = *r; |
276 | 0 | memcpy(sc->sc_data_w, data, UINT64_TO_SIZE_T(rsize)); |
277 | |
|
278 | 0 | if (fs->cleanse) |
279 | 0 | OPENSSL_cleanse(deconst(data), UINT64_TO_SIZE_T(rsize)); |
280 | 0 | } |
281 | | |
282 | 103k | return sc; |
283 | 103k | } |
284 | | |
285 | | static void destroy_schunk(SFRAME_SET *fs, struct stream_chunk_t *sc) |
286 | 103k | { |
287 | 103k | if (sc == NULL) |
288 | 0 | return; |
289 | | |
290 | 103k | if (fs->cleanse) |
291 | 0 | OPENSSL_cleanse(sc->sc_data_w, |
292 | 0 | UINT64_TO_SIZE_T(sc->sc_st == ST_TYPE_DIRECT |
293 | 0 | ? DIRECT_STORAGE_SZ |
294 | 0 | : SCHUNK_SIZE(sc))); |
295 | | |
296 | 103k | switch (sc->sc_st) { |
297 | 103k | case ST_TYPE_PKT: |
298 | 103k | assert(fs->rsqp->rsqp_pkt_overhead_sz >= SCHUNK_OVERHEAD(sc->sc_pkt, sc)); |
299 | 103k | DEBUG_PRINT(STDERR, |
300 | 103k | "%s sc: %p sc overhead: %llu pkt_buf_overhead_sz: %zu -> %llu\n", |
301 | 103k | OPENSSL_FUNC, (void *)sc, SCHUNK_OVERHEAD(sc->sc_pkt, sc), |
302 | 103k | fs->rsqp->rsqp_pkt_overhead_sz, |
303 | 103k | fs->rsqp->rsqp_pkt_overhead_sz - SCHUNK_OVERHEAD(sc->sc_pkt, sc)); |
304 | 103k | rsqp_sub_overhead(fs->rsqp, |
305 | 103k | UINT64_TO_SIZE_T(SCHUNK_OVERHEAD(sc->sc_pkt, sc))); |
306 | 103k | ossl_qrx_pkt_release(sc->sc_pkt); |
307 | 103k | break; |
308 | 0 | case ST_TYPE_HEAP: |
309 | 0 | OPENSSL_free(sc->sc_buf); |
310 | 0 | break; |
311 | 0 | default: |
312 | 0 | assert(sc->sc_st == ST_TYPE_DIRECT); |
313 | 103k | } |
314 | | |
315 | 103k | OPENSSL_free(sc); |
316 | 103k | } |
317 | | |
318 | | static struct stream_range_t *new_srange(void) |
319 | 96.3k | { |
320 | 96.3k | struct stream_range_t *sr; |
321 | | |
322 | 96.3k | sr = OPENSSL_zalloc(sizeof(*sr)); |
323 | 96.3k | if (sr != NULL) { |
324 | 96.3k | ossl_list_sc_init(&sr->sr_chunks); |
325 | 96.3k | } |
326 | | |
327 | 96.3k | return sr; |
328 | 96.3k | } |
329 | | |
330 | | static void destroy_srange(SFRAME_SET *fs, struct stream_range_t *sr) |
331 | 96.3k | { |
332 | 96.3k | struct stream_chunk_t *sc; |
333 | | |
334 | 96.3k | if (sr == NULL) |
335 | 0 | return; |
336 | | |
337 | 96.3k | assert(sr->sr_rbe.rb_parent == NULL); |
338 | 96.3k | assert(sr->sr_rbe.rb_left == NULL); |
339 | 96.3k | assert(sr->sr_rbe.rb_right == NULL); |
340 | | |
341 | 119k | while ((sc = ossl_list_sc_head(&sr->sr_chunks)) != NULL) { |
342 | 23.1k | ossl_list_sc_remove(&sr->sr_chunks, sc); |
343 | 23.1k | fs->stream_chunks--; |
344 | 23.1k | destroy_schunk(fs, sc); |
345 | 23.1k | } |
346 | | |
347 | 96.3k | OPENSSL_free(sr); |
348 | 96.3k | } |
349 | | |
350 | | static struct stream_range_t *create_range(SFRAME_SET *fs, |
351 | | struct stream_chunk_t *sc) |
352 | 96.3k | { |
353 | 96.3k | struct stream_range_t *sr; |
354 | | |
355 | 96.3k | assert(sc != NULL); |
356 | | |
357 | 96.3k | sr = new_srange(); |
358 | 96.3k | if (sr != NULL) { |
359 | 96.3k | ossl_list_sc_insert_head(&sr->sr_chunks, sc); |
360 | 96.3k | sr->sr_range = sc->sc_range; |
361 | 96.3k | fs->stream_chunks++; |
362 | 96.3k | } |
363 | | |
364 | 96.3k | return sr; |
365 | 96.3k | } |
366 | | |
367 | | void ossl_sframe_set_init(SFRAME_SET *fs, QUIC_RSTREAM_QPARM *rsqp) |
368 | 299k | { |
369 | 299k | assert(rsqp != NULL); |
370 | | |
371 | 299k | memset(fs, 0, sizeof(*fs)); |
372 | 299k | OSSL_RBT_INIT(srange, &fs->ranges); |
373 | 299k | fs->rsqp = rsqp; |
374 | 299k | } |
375 | | |
376 | | static uint64_t get_sc_dstorage_sz(struct stream_chunk_t *sc) |
377 | 0 | { |
378 | 0 | uint64_t sz = 0; |
379 | |
|
380 | 0 | if (sc->sc_st == ST_TYPE_DIRECT && SCHUNK_SIZE(sc) < DIRECT_STORAGE_SZ) |
381 | 0 | sz = DIRECT_STORAGE_SZ - SCHUNK_SIZE(sc); |
382 | |
|
383 | 0 | return sz; |
384 | 0 | } |
385 | | |
386 | | static int try_dstorage(SFRAME_SET *fs, OSSL_QRX_PKT *pkt, |
387 | | struct stream_range_t *sr, UINT_RANGE *r, const unsigned char **data) |
388 | 7.30k | { |
389 | 7.30k | struct stream_chunk_t *head_sc, *tail_sc, *new_sc; |
390 | 7.30k | uint64_t rsize; |
391 | 7.30k | uint64_t offset; |
392 | 7.30k | uint64_t dsize; |
393 | | |
394 | | /* |
395 | | * full overlap which spans over more range with more than 1 chunk, |
396 | | * nothing to be done here, caller will handle that. |
397 | | */ |
398 | 7.30k | rsize = r->end - r->start; |
399 | 7.30k | if (r->start < sr->sr_range.start && r->end > sr->sr_range.end |
400 | 1.39k | && ossl_list_sc_num(&sr->sr_chunks) > 1) |
401 | 144 | return 0; |
402 | | |
403 | 7.15k | head_sc = ossl_list_sc_head(&sr->sr_chunks); |
404 | 7.15k | assert(head_sc != NULL); |
405 | 7.15k | tail_sc = ossl_list_sc_tail(&sr->sr_chunks); |
406 | 7.15k | assert(tail_sc != NULL); |
407 | | |
408 | | /* |
409 | | * full overlap of direct storage can be treated when range contains |
410 | | * exactly one chunk. |
411 | | */ |
412 | 7.15k | if (head_sc == tail_sc |
413 | 5.64k | && head_sc->sc_range.start > r->start |
414 | 3.78k | && head_sc->sc_range.end < r->end) { |
415 | | /* |
416 | | * can deal with full overlap |
417 | | */ |
418 | 1.25k | if (head_sc->sc_st != ST_TYPE_DIRECT) |
419 | 1.25k | return 0; |
420 | | |
421 | 0 | DEBUG_PRINT(STDERR, "%s @in %p [ %llu, %llu ]\n", |
422 | 0 | OPENSSL_FUNC, *data, r->start, r->end); |
423 | 0 | rsize = r->end - r->start; |
424 | 0 | if (rsize <= DIRECT_STORAGE_SZ) { |
425 | | /* |
426 | | * update existing chunk |
427 | | */ |
428 | 0 | DEBUG_PRINT(STDERR, |
429 | 0 | "%s overwrite dstorage %p [ %llu, %llu ] -> [ %llu, %llu ] " |
430 | 0 | "sr: %p [ %llu, %llu ]\n", |
431 | 0 | OPENSSL_FUNC, (void *)head_sc, |
432 | 0 | head_sc->sc_range.start, head_sc->sc_range.end, |
433 | 0 | r->start, r->end, |
434 | 0 | (void *)sr, sr->sr_range.start, sr->sr_range.end); |
435 | 0 | memcpy(head_sc->sc_data_w, *data, UINT64_TO_SIZE_T(rsize)); |
436 | |
|
437 | 0 | if (fs->cleanse) |
438 | 0 | OPENSSL_cleanse(deconst(*data), UINT64_TO_SIZE_T(rsize)); |
439 | |
|
440 | 0 | *data += rsize; |
441 | 0 | head_sc->sc_range.start = r->start; |
442 | 0 | head_sc->sc_range.end = r->end; |
443 | 0 | } else { |
444 | | /* |
445 | | * try to replace existing chunk |
446 | | */ |
447 | 0 | new_sc = new_schunk(fs, pkt, r, *data); |
448 | 0 | if (new_sc == NULL) { |
449 | 0 | DEBUG_PRINT(STDERR, "%s new_chunk() alloc failed\n", |
450 | 0 | OPENSSL_FUNC); |
451 | 0 | return -1; |
452 | 0 | } |
453 | | |
454 | 0 | DEBUG_PRINT(STDERR, |
455 | 0 | "%s replace chunk %p [ %llu, %llu ] -> %p [ %llu, %llu ]\n", |
456 | 0 | OPENSSL_FUNC, |
457 | 0 | (void *)head_sc, head_sc->sc_range.start, head_sc->sc_range.end, |
458 | 0 | (void *)new_sc, new_sc->sc_range.start, new_sc->sc_range.end); |
459 | 0 | ossl_list_sc_remove(&sr->sr_chunks, head_sc); |
460 | 0 | ossl_list_sc_insert_head(&sr->sr_chunks, new_sc); |
461 | 0 | destroy_schunk(fs, head_sc); |
462 | 0 | } |
463 | 0 | DEBUG_PRINT(STDERR, "\trange: %p [ %llu, %llu ] -> [ %llu, %llu ]\n", |
464 | 0 | (void *)sr, sr->sr_range.start, sr->sr_range.end, r->start, r->end); |
465 | 0 | sr->sr_range.start = r->start; |
466 | 0 | sr->sr_range.end = r->end; |
467 | | /* |
468 | | * indicate that while range got consumed. |
469 | | */ |
470 | 0 | r->start = 0; |
471 | 0 | r->end = 0; |
472 | 5.90k | } else if (tail_sc->sc_range.end < r->end) { |
473 | | /* |
474 | | * append only |
475 | | */ |
476 | 2.76k | if (tail_sc->sc_st != ST_TYPE_DIRECT) |
477 | 2.76k | return 0; |
478 | | |
479 | 0 | dsize = get_sc_dstorage_sz(tail_sc); |
480 | 0 | if (dsize == 0) |
481 | 0 | return 0; |
482 | | |
483 | 0 | DEBUG_PRINT(STDERR, "%s append: @in %p [ %llu, %llu ] dsize: %llu " |
484 | 0 | "tail_sc: %p [ %llu, %llu] sr: %p [ %llu, %llu ]\n", |
485 | 0 | OPENSSL_FUNC, *data, r->start, r->end, dsize, |
486 | 0 | (void *)tail_sc, tail_sc->sc_range.start, tail_sc->sc_range.end, |
487 | 0 | (void *)sr, sr->sr_range.start, sr->sr_range.end); |
488 | |
|
489 | 0 | if (r->start < tail_sc->sc_range.end) { |
490 | 0 | rsize = tail_sc->sc_range.end - r->start; |
491 | |
|
492 | 0 | if (fs->cleanse) |
493 | 0 | OPENSSL_cleanse(deconst(*data), UINT64_TO_SIZE_T(rsize)); |
494 | |
|
495 | 0 | *data += rsize; |
496 | |
|
497 | 0 | r->start = tail_sc->sc_range.end; |
498 | 0 | } |
499 | |
|
500 | 0 | rsize = r->end - r->start; |
501 | | /* |
502 | | * earlier check done in ossl_sframe_set_insert() ensures |
503 | | * there is at least some data to append. |
504 | | */ |
505 | 0 | assert(rsize > 0); |
506 | 0 | rsize = (rsize < dsize) ? rsize : dsize; |
507 | 0 | offset = SCHUNK_SIZE(tail_sc); |
508 | 0 | memcpy(&tail_sc->sc_data_w[offset], *data, UINT64_TO_SIZE_T(rsize)); |
509 | 0 | DEBUG_PRINT(STDERR, "%s append tail_sc: %p [ %llu, %llu ] -> ", |
510 | 0 | OPENSSL_FUNC, (void *)tail_sc, |
511 | 0 | tail_sc->sc_range.start, tail_sc->sc_range.end); |
512 | 0 | tail_sc->sc_range.end += rsize; |
513 | 0 | DEBUG_PRINT(STDERR, "[ %llu, %llu ]\n", |
514 | 0 | tail_sc->sc_range.start, tail_sc->sc_range.end); |
515 | 0 | assert(SCHUNK_SIZE(tail_sc) <= DIRECT_STORAGE_SZ); |
516 | 0 | DEBUG_PRINT(STDERR, "\trange: %p [ %llu, %llu ] -> ", |
517 | 0 | (void *)sr, sr->sr_range.start, sr->sr_range.end); |
518 | 0 | sr->sr_range.end = tail_sc->sc_range.end; |
519 | 0 | DEBUG_PRINT(STDERR, "[ %llu, %llu ]\n", |
520 | 0 | sr->sr_range.start, sr->sr_range.end); |
521 | |
|
522 | 0 | if (fs->cleanse) |
523 | 0 | OPENSSL_cleanse(deconst(*data), UINT64_TO_SIZE_T(rsize)); |
524 | |
|
525 | 0 | *data += rsize; |
526 | 0 | r->start = tail_sc->sc_range.end; |
527 | 3.14k | } else if (head_sc->sc_range.start > r->start) { |
528 | 3.14k | const unsigned char *data_buf; |
529 | | /* |
530 | | * prepend only |
531 | | */ |
532 | 3.14k | if (head_sc->sc_st != ST_TYPE_DIRECT) |
533 | 3.14k | return 0; |
534 | | |
535 | 0 | dsize = get_sc_dstorage_sz(head_sc); |
536 | 0 | if (dsize == 0) |
537 | 0 | return 0; |
538 | | |
539 | 0 | DEBUG_PRINT(STDERR, "%s prepend: @in %p [ %llu, %llu ] dsize: %llu " |
540 | 0 | "sr: %p [ %llu, %llu ]\n", |
541 | 0 | OPENSSL_FUNC, *data, r->start, r->end, dsize, |
542 | 0 | (void *)sr, sr->sr_range.start, sr->sr_range.end); |
543 | |
|
544 | 0 | if (r->end > head_sc->sc_range.start) { |
545 | 0 | if (fs->cleanse) |
546 | 0 | OPENSSL_cleanse( |
547 | 0 | deconst(*data + (head_sc->sc_range.start - r->start)), |
548 | 0 | UINT64_TO_SIZE_T(r->end - head_sc->sc_range.start)); |
549 | 0 | r->end = head_sc->sc_range.start; |
550 | 0 | } |
551 | |
|
552 | 0 | rsize = r->end - r->start; |
553 | | /* |
554 | | * earlier check done in ossl_sframe_set_insert() ensures |
555 | | * there is at least some data to append. |
556 | | */ |
557 | 0 | assert(rsize > 0); |
558 | 0 | rsize = (rsize < dsize) ? rsize : dsize; |
559 | 0 | memmove(&head_sc->sc_data_w[rsize], head_sc->sc_data, |
560 | 0 | UINT64_TO_SIZE_T(SCHUNK_SIZE(head_sc))); |
561 | 0 | offset = r->end - rsize - r->start; |
562 | 0 | assert(offset < r->end - r->start); |
563 | 0 | data_buf = *data; |
564 | 0 | memcpy(head_sc->sc_data_w, &data_buf[offset], UINT64_TO_SIZE_T(rsize)); |
565 | 0 | DEBUG_PRINT(STDERR, "%s prepend head_sc: %p [ %llu, %llu ] -> ", |
566 | 0 | OPENSSL_FUNC, (void *)head_sc, |
567 | 0 | head_sc->sc_range.start, head_sc->sc_range.end); |
568 | 0 | head_sc->sc_range.start -= rsize; |
569 | 0 | DEBUG_PRINT(STDERR, "[ %llu, %llu ]\n", |
570 | 0 | head_sc->sc_range.start, head_sc->sc_range.end); |
571 | 0 | assert(SCHUNK_SIZE(head_sc) <= DIRECT_STORAGE_SZ); |
572 | 0 | DEBUG_PRINT(STDERR, "\trange: %p [ %llu, %llu ] -> ", |
573 | 0 | (void *)sr, sr->sr_range.start, sr->sr_range.end); |
574 | 0 | sr->sr_range.start = head_sc->sc_range.start; |
575 | 0 | DEBUG_PRINT(STDERR, "[ %llu, %llu ]\n", |
576 | 0 | sr->sr_range.start, sr->sr_range.end); |
577 | 0 | assert(r->end - r->start >= rsize); |
578 | |
|
579 | 0 | if (fs->cleanse) |
580 | 0 | OPENSSL_cleanse(deconst(&data_buf[offset]), |
581 | 0 | UINT64_TO_SIZE_T(rsize)); |
582 | |
|
583 | 0 | r->end -= rsize; |
584 | 0 | } else { |
585 | 0 | assert(0); |
586 | 0 | return -1; |
587 | 0 | } |
588 | | |
589 | | /* |
590 | | * returns 1 if all data were consumed |
591 | | */ |
592 | 7.15k | assert(r->end >= r->start); |
593 | 0 | DEBUG_PRINT(STDERR, "%s @out %p [ %llu, %llu ]\n", |
594 | 0 | OPENSSL_FUNC, *data, r->start, r->end); |
595 | |
|
596 | 0 | return ((r->end - r->start) == 0) ? 1 : 0; |
597 | 0 | } |
598 | | |
599 | | /* |
600 | | * If there is partial overlap between newly received data `r` and |
601 | | * existing stream range `sr`, then this function trims overlapping |
602 | | * bytes from `r`. |
603 | | * sr - pointer to stream range |
604 | | * r - pointer to range of bytes received in stream frame |
605 | | * data - pointer to data bytes delivered in stream frame |
606 | | * function updates r so there is no partial overlap between and sr |
607 | | * after function returns. Function returns pointer to the first |
608 | | * data byte in stream after `r` is adjusted. Function returns `data` |
609 | | * when no trimming happened. |
610 | | */ |
611 | | static const unsigned char *trim_partial_overlap(SFRAME_SET *fs, |
612 | | struct stream_range_t *sr, UINT_RANGE *r, const unsigned char *data) |
613 | 7.30k | { |
614 | 7.30k | uint64_t unused_sz; |
615 | | |
616 | 7.30k | if (!(r->start < sr->sr_range.start && r->end > sr->sr_range.end)) { |
617 | 5.90k | if (r->end > sr->sr_range.end && r->start < sr->sr_range.end) { |
618 | 2.06k | unused_sz = sr->sr_range.end - r->start; |
619 | 2.06k | if (fs->cleanse) |
620 | 0 | OPENSSL_cleanse(deconst(data), UINT64_TO_SIZE_T(unused_sz)); |
621 | | |
622 | 2.06k | DEBUG_PRINT(STDERR, "%s right overlap %p [ %llu, %llu ]:\n\t" |
623 | 2.06k | "r: [ %llu, %llu ] -> [ %llu, %llu ]\n", |
624 | 2.06k | OPENSSL_FUNC, |
625 | 2.06k | (void *)sr, sr->sr_range.start, sr->sr_range.end, |
626 | 2.06k | r->start, r->end, |
627 | 2.06k | sr->sr_range.end, r->end); |
628 | | |
629 | 2.06k | data += unused_sz; |
630 | 2.06k | r->start = sr->sr_range.end; |
631 | 3.84k | } else if (r->start < sr->sr_range.start |
632 | 3.14k | && r->end > sr->sr_range.start) { |
633 | 1.34k | unused_sz = r->end - sr->sr_range.start; |
634 | 1.34k | if (fs->cleanse) |
635 | 0 | OPENSSL_cleanse(deconst(data |
636 | 0 | + (sr->sr_range.start - r->start)), |
637 | 0 | UINT64_TO_SIZE_T(unused_sz)); |
638 | | |
639 | 1.34k | DEBUG_PRINT(STDERR, "%s left overlap %p [ %llu, %llu]:\n\t" |
640 | 1.34k | "r: [ %llu, %llu ] -> [ %llu, %llu ]\n", |
641 | 1.34k | OPENSSL_FUNC, |
642 | 1.34k | (void *)sr, sr->sr_range.start, sr->sr_range.end, |
643 | 1.34k | r->start, r->end, |
644 | 1.34k | r->start, sr->sr_range.start); |
645 | 1.34k | r->end = sr->sr_range.start; |
646 | 1.34k | } |
647 | 5.90k | } |
648 | | |
649 | 7.30k | return data; |
650 | 7.30k | } |
651 | | |
652 | | /* |
653 | | * Inserts a newly received chunk to the head of the chunk list. |
654 | | */ |
655 | | static void prepend_chunk(SFRAME_SET *fs, struct stream_range_t *sr, |
656 | | struct stream_chunk_t *sc) |
657 | 3.14k | { |
658 | 3.14k | DEBUG_PRINT(STDERR, "%s %p [ %llu, %llu ] add to head %p [ %llu, %llu ] " |
659 | 3.14k | "-> [ %llu, %llu ]\n", |
660 | 3.14k | OPENSSL_FUNC, |
661 | 3.14k | (void *)sc, sc->sc_range.start, sc->sc_range.end, |
662 | 3.14k | (void *)sr, sr->sr_range.start, sr->sr_range.end, |
663 | 3.14k | sc->sc_range.start, sr->sr_range.end); |
664 | | |
665 | | /* |
666 | | * the new chunk must not be empty |
667 | | */ |
668 | 3.14k | assert(sc->sc_range.end > sc->sc_range.start); |
669 | | /* |
670 | | * and must not overlap range. |
671 | | */ |
672 | 3.14k | assert(sc->sc_range.end == sr->sr_range.start); |
673 | | |
674 | 3.14k | ossl_list_sc_insert_head(&sr->sr_chunks, sc); |
675 | 3.14k | sr->sr_range.start = sc->sc_range.start; |
676 | 3.14k | fs->stream_chunks++; |
677 | 3.14k | } |
678 | | |
679 | | /* |
680 | | * Inserts a newly received chunk to the tail of the chunk list. |
681 | | */ |
682 | | static void append_chunk(SFRAME_SET *fs, struct stream_range_t *sr, |
683 | | struct stream_chunk_t *sc) |
684 | 2.76k | { |
685 | 2.76k | DEBUG_PRINT(STDERR, "%s %p [ %llu, %llu ] add to tail %p [ %llu, %llu ] " |
686 | 2.76k | "-> [ %llu, %llu ]\n", |
687 | 2.76k | OPENSSL_FUNC, |
688 | 2.76k | (void *)sc, sc->sc_range.start, sc->sc_range.end, |
689 | 2.76k | (void *)sr, sr->sr_range.start, sr->sr_range.end, |
690 | 2.76k | sr->sr_range.start, sc->sc_range.end); |
691 | | |
692 | | /* |
693 | | * the new chunk must not be empty |
694 | | */ |
695 | 2.76k | assert(sc->sc_range.end > sc->sc_range.start); |
696 | | /* |
697 | | * and must not overlap range |
698 | | */ |
699 | 2.76k | assert(sc->sc_range.start == sr->sr_range.end); |
700 | | |
701 | 2.76k | ossl_list_sc_insert_tail(&sr->sr_chunks, sc); |
702 | 2.76k | sr->sr_range.end = sc->sc_range.end; |
703 | 2.76k | fs->stream_chunks++; |
704 | 2.76k | } |
705 | | |
706 | | static void replace_chunks_in_range(SFRAME_SET *fs, struct stream_range_t *sr, |
707 | | struct stream_chunk_t *sc) |
708 | 1.39k | { |
709 | 1.39k | struct stream_chunk_t *destroy_sc; |
710 | | |
711 | 3.09k | while ((destroy_sc = ossl_list_sc_head(&sr->sr_chunks)) != NULL) { |
712 | 1.69k | ossl_list_sc_remove(&sr->sr_chunks, destroy_sc); |
713 | 1.69k | fs->stream_chunks--; |
714 | 1.69k | destroy_schunk(fs, destroy_sc); |
715 | 1.69k | } |
716 | | |
717 | 1.39k | ossl_list_sc_insert_head(&sr->sr_chunks, sc); |
718 | 1.39k | fs->stream_chunks++; |
719 | 1.39k | DEBUG_PRINT(STDERR, "%s range: %p [ %llu, %llu ] -> [ %llu, %llu ]\n", |
720 | 1.39k | OPENSSL_FUNC, (void *)sr, sr->sr_range.start, sr->sr_range.end, |
721 | 1.39k | sc->sc_range.start, sc->sc_range.end); |
722 | 1.39k | sr->sr_range.start = sc->sc_range.start; |
723 | 1.39k | sr->sr_range.end = sc->sc_range.end; |
724 | 1.39k | } |
725 | | |
726 | | static struct stream_range_t *find_range(SFRAME_SET *fs, |
727 | | struct stream_range_t *key) |
728 | 189k | { |
729 | 189k | struct stream_range_t *sr = NULL; |
730 | | |
731 | 189k | if (!OSSL_RBT_EMPTY(srange, &fs->ranges)) |
732 | 101k | sr = OSSL_RBT_FIND(srange, &fs->ranges, key); |
733 | | |
734 | 189k | return sr; |
735 | 189k | } |
736 | | |
737 | | /* |
738 | | * This function help us to merge two ranges (list of chunks) |
739 | | * into single range. Function moves the end of the range |
740 | | * towards start. It effectively chops n last chunks until |
741 | | * new_end is found. |
742 | | */ |
743 | | static int chop_range(SFRAME_SET *fs, struct stream_range_t *sr, |
744 | | uint64_t new_end) |
745 | 1.13k | { |
746 | 1.13k | struct stream_chunk_t *sc; |
747 | 1.13k | size_t unused_sz; |
748 | | |
749 | 1.13k | assert(sr->sr_range.end >= new_end); |
750 | | |
751 | 1.73k | while ((sc = ossl_list_sc_tail(&sr->sr_chunks)) != NULL) { |
752 | 1.73k | if (sc->sc_range.start >= new_end) { |
753 | 601 | ossl_list_sc_remove(&sr->sr_chunks, sc); |
754 | 601 | fs->stream_chunks--; |
755 | 601 | destroy_schunk(fs, sc); |
756 | 1.13k | } else { |
757 | 1.13k | break; |
758 | 1.13k | } |
759 | 1.73k | } |
760 | | |
761 | 1.13k | if (sc == NULL) |
762 | 0 | return 0; |
763 | | |
764 | 1.13k | assert(new_end <= sc->sc_range.end); |
765 | 1.13k | assert(sc->sc_range.start < new_end); |
766 | | |
767 | 1.13k | unused_sz = UINT64_TO_SIZE_T(sc->sc_range.end - new_end); |
768 | 1.13k | if (unused_sz == 0) { |
769 | 561 | sr->sr_range.end = new_end; |
770 | 561 | return 1; |
771 | 561 | } |
772 | | |
773 | 570 | sc_data_trim_right(sc, unused_sz, fs->cleanse); |
774 | 570 | sc->sc_range.end = new_end; |
775 | 570 | sr->sr_range.end = new_end; |
776 | | |
777 | 570 | if (sc->sc_st == ST_TYPE_PKT) { |
778 | 570 | rsqp_add_overhead(fs->rsqp, unused_sz); |
779 | 570 | DEBUG_PRINT(STDERR, "%s sc: %p unused_sz: %zu %zu -> %zu\n", |
780 | 570 | OPENSSL_FUNC, (void *)sc, unused_sz, |
781 | 570 | fs->rsqp->rsqp_pkt_overhead_sz - unused_sz, |
782 | 570 | fs->rsqp->rsqp_pkt_overhead_sz); |
783 | 570 | } |
784 | | |
785 | 570 | return 1; |
786 | 1.13k | } |
787 | | |
788 | | /* |
789 | | * function merges two with full overlap. The super_sr range |
790 | | * contains the whole sub_sr range. The function destroys |
791 | | * sub_sr and returns super_sr. |
792 | | */ |
793 | | static struct stream_range_t *merge_ranges(SFRAME_SET *fs, |
794 | | struct stream_range_t *super_sr, struct stream_range_t *sub_sr) |
795 | 859 | { |
796 | | /* |
797 | | * both ranges must not be empty |
798 | | */ |
799 | 859 | assert(super_sr->sr_range.start < super_sr->sr_range.end); |
800 | 859 | assert(sub_sr->sr_range.start < sub_sr->sr_range.end); |
801 | | /* |
802 | | * sub_sr and super_sr are equal ranges (sets) super_sr |
803 | | * sub_sr is subset of super_sr (super_sr includes sub_sr). |
804 | | */ |
805 | 859 | assert(super_sr->sr_range.start <= sub_sr->sr_range.start |
806 | 859 | && super_sr->sr_range.end >= sub_sr->sr_range.end); |
807 | | |
808 | 859 | DEBUG_PRINT(STDERR, "%s super: %p [ %llu, %llu ], sub: %p [ %llu, %llu]\n", |
809 | 859 | OPENSSL_FUNC, (void *)super_sr, super_sr->sr_range.start, |
810 | 859 | super_sr->sr_range.end, (void *)sub_sr, sub_sr->sr_range.start, |
811 | 859 | sub_sr->sr_range.end); |
812 | 859 | destroy_srange(fs, sub_sr); |
813 | | |
814 | 859 | return super_sr; |
815 | 859 | } |
816 | | |
817 | | /* |
818 | | * The ranges are either adjacent |
819 | | * (left_sr->sr_range.end == right_sr->sr_range.end) or there |
820 | | * is partial overlap between left_sr and right_sr( |
821 | | * (left_sr->sr_range.end >= right_sr->sr_range.start). |
822 | | * If there is partial overlap, then the left range is chopped |
823 | | * so its end is aligned with start of right_sr. |
824 | | */ |
825 | | static struct stream_range_t *append_range(SFRAME_SET *fs, |
826 | | struct stream_range_t *left_sr, struct stream_range_t *right_sr) |
827 | 1.13k | { |
828 | | /* |
829 | | * both ranges must not be empty |
830 | | */ |
831 | 1.13k | assert(left_sr->sr_range.start < left_sr->sr_range.end); |
832 | 1.13k | assert(right_sr->sr_range.start < right_sr->sr_range.end); |
833 | | /* |
834 | | * right range follows left range (left < right) |
835 | | */ |
836 | 1.13k | assert(left_sr->sr_range.end >= right_sr->sr_range.start); |
837 | | |
838 | 1.13k | DEBUG_PRINT(STDERR, "%s %p [ %llu, %llu ] + %p [ %llu, %llu ] = %p " |
839 | 1.13k | "[ %llu, %llu ]\n", |
840 | 1.13k | OPENSSL_FUNC, (void *)left_sr, left_sr->sr_range.start, |
841 | 1.13k | left_sr->sr_range.end, (void *)right_sr, right_sr->sr_range.start, |
842 | 1.13k | right_sr->sr_range.end, (void *)left_sr, |
843 | 1.13k | left_sr->sr_range.start, right_sr->sr_range.end); |
844 | | |
845 | | /* |
846 | | * make sure there is no overlap between ranges |
847 | | * (right_sr->sr_range.start == left_sr->sr_range.end) |
848 | | */ |
849 | 1.13k | if (chop_range(fs, left_sr, right_sr->sr_range.start) == 0) |
850 | 0 | return NULL; |
851 | | |
852 | 1.13k | ossl_list_sc_join(&left_sr->sr_chunks, &right_sr->sr_chunks); |
853 | 1.13k | left_sr->sr_range.end = right_sr->sr_range.end; |
854 | | |
855 | 1.13k | destroy_srange(fs, right_sr); |
856 | | |
857 | 1.13k | return left_sr; |
858 | 1.13k | } |
859 | | |
860 | | /* |
861 | | * receives a chunk of data from stream frame. |
862 | | * note there is a tri-state return value: |
863 | | */ |
864 | | int ossl_sframe_set_insert(SFRAME_SET *fs, UINT_RANGE *r, OSSL_QRX_PKT *pkt, |
865 | | const unsigned char *data, int fin) |
866 | 215k | { |
867 | 215k | struct stream_range_t *sr = NULL; |
868 | 215k | struct stream_range_t *adjacent_sr = NULL; |
869 | 215k | struct stream_range_t *joined_sr = NULL; |
870 | 215k | struct stream_chunk_t *sc = NULL; |
871 | 215k | struct stream_range_t key_sr = { 0 }; |
872 | | |
873 | 215k | assert(r->start <= r->end); |
874 | | |
875 | | /* |
876 | | * receive the FIN frame. If FIN was not seen yet, then record |
877 | | * FIN's offset (r->end). If FIN was received then verify FIN's |
878 | | * offset match, error out on mismatch. |
879 | | */ |
880 | 215k | if (fin != 0) { |
881 | 25.2k | if (fs->fin == 0) { |
882 | 7.40k | sr = OSSL_RBT_MAX(srange, &fs->ranges); |
883 | 7.40k | if (r->end < fs->offset |
884 | 7.40k | || (sr != NULL && sr->sr_range.end > r->end)) { |
885 | 0 | ossl_quic_channel_raise_protocol_error(fs->rsqp->rsqp_ch, |
886 | 0 | OSSL_QUIC_ERR_FINAL_SIZE_ERROR, |
887 | 0 | OSSL_QUIC_FRAME_TYPE_STREAM_FIN, |
888 | 0 | "stream final size error"); |
889 | 0 | return 0; |
890 | 0 | } |
891 | 7.40k | fs->fin = 1; |
892 | 7.40k | fs->fin_off = r->end; |
893 | 17.8k | } else if (fs->fin_off != r->end) { |
894 | 0 | ossl_quic_channel_raise_protocol_error(fs->rsqp->rsqp_ch, |
895 | 0 | OSSL_QUIC_ERR_FINAL_SIZE_ERROR, |
896 | 0 | OSSL_QUIC_FRAME_TYPE_STREAM_FIN, |
897 | 0 | "stream final size error"); |
898 | 0 | return 0; |
899 | 0 | } |
900 | 25.2k | } |
901 | | |
902 | | /* |
903 | | * reject any data at or past the FIN offset (if FIN offset is set). |
904 | | */ |
905 | 215k | if (fs->fin != 0) { |
906 | 26.0k | if (fs->fin_off < r->end) { |
907 | 0 | ossl_quic_channel_raise_protocol_error(fs->rsqp->rsqp_ch, |
908 | 0 | OSSL_QUIC_ERR_FINAL_SIZE_ERROR, |
909 | 0 | (fin == 0) ? OSSL_QUIC_FRAME_TYPE_STREAM |
910 | 0 | : OSSL_QUIC_FRAME_TYPE_STREAM_FIN, |
911 | 0 | "stream final size error"); |
912 | 0 | return 0; |
913 | 0 | } |
914 | 26.0k | } |
915 | | |
916 | 215k | if (r->end <= fs->offset) { |
917 | | /* |
918 | | * retransmitted range got consumed already. |
919 | | */ |
920 | 26.0k | DEBUG_PRINT(STDERR, "%s [ %llu, %llu ] <= %llu\n", OPENSSL_FUNC, |
921 | 26.0k | r->start, r->end, fs->offset); |
922 | 26.0k | if (fs->cleanse && data != NULL) |
923 | 0 | OPENSSL_cleanse(deconst(data), UINT64_TO_SIZE_T(r->end - r->start)); |
924 | 26.0k | return 1; |
925 | 26.0k | } |
926 | | |
927 | 189k | if (r->start < fs->offset) { |
928 | | /* |
929 | | * Make sure retransmitted chunk does not reintroduce |
930 | | * bytes which were consumed already. |
931 | | * Make sure retransmitted chunk does not reintroduce |
932 | | * bytes which were consumed already. |
933 | | */ |
934 | 658 | DEBUG_PRINT(STDERR, "%s [ %llu, %llu ] -> [ %llu, %llu ]\n", OPENSSL_FUNC, |
935 | 658 | r->start, r->end, fs->offset, r->end); |
936 | 658 | if (fs->cleanse) |
937 | 0 | OPENSSL_cleanse(deconst(data), |
938 | 0 | UINT64_TO_SIZE_T(fs->offset - r->start)); |
939 | 658 | data += fs->offset - r->start; |
940 | 658 | r->start = fs->offset; |
941 | 658 | } |
942 | | |
943 | 189k | key_sr.sr_range = *r; |
944 | | |
945 | | /* |
946 | | * Empty, 0 size chunk can carry the FIN bit only, |
947 | | * and that has been just handled above. |
948 | | */ |
949 | 189k | if (r->start == r->end) |
950 | 108 | return 1; |
951 | | |
952 | 189k | assert(r->start < r->end); |
953 | | |
954 | 189k | if ((sr = find_range(fs, &key_sr)) == NULL) { |
955 | 96.3k | sc = new_schunk(fs, pkt, r, data); |
956 | 96.3k | if (sc == NULL) |
957 | 0 | goto err; |
958 | | |
959 | 96.3k | sr = create_range(fs, sc); |
960 | 96.3k | if (sr == NULL) |
961 | 0 | goto err; |
962 | 96.3k | DEBUG_PRINT(STDERR, "%s chunk: %p [ %llu, %llu ] new range: %p\n", |
963 | 96.3k | OPENSSL_FUNC, (void *)sc, sc->sc_range.start, sc->sc_range.end, |
964 | 96.3k | (void *)sr); |
965 | 96.3k | sc = NULL; |
966 | 96.3k | OSSL_RBT_INSERT(srange, &fs->ranges, sr); |
967 | 96.3k | fs->stream_ranges++; |
968 | 96.3k | } else { |
969 | | /* |
970 | | * retransmission, the whole chunk is found in existing range already |
971 | | */ |
972 | 93.2k | if (r->start >= sr->sr_range.start && r->end <= sr->sr_range.end) { |
973 | 85.9k | DEBUG_PRINT(STDERR, |
974 | 85.9k | "%s [ %llu, %llu ] found in %p [ %llu, %llu ]\n", OPENSSL_FUNC, |
975 | 85.9k | r->start, r->end, (void *)sr, sr->sr_range.start, |
976 | 85.9k | sr->sr_range.end); |
977 | 85.9k | if (fs->cleanse) |
978 | 0 | OPENSSL_cleanse(deconst(data), |
979 | 0 | UINT64_TO_SIZE_T(r->end - r->start)); |
980 | 85.9k | goto done; /* Range is present already. */ |
981 | 85.9k | } |
982 | | |
983 | 7.30k | switch (try_dstorage(fs, pkt, sr, r, &data)) { |
984 | 7.30k | case 0: |
985 | 7.30k | break; |
986 | 0 | case 1: |
987 | | /* |
988 | | * all data were consumed, range is updated. |
989 | | */ |
990 | 0 | goto range_updated; |
991 | 0 | default: |
992 | | /* |
993 | | * malloc error. forget the range we found. |
994 | | */ |
995 | 0 | sr = NULL; |
996 | 0 | goto err; |
997 | 7.30k | } |
998 | | |
999 | | /* |
1000 | | * full overlap between sr and r is handled by replace_chunks_in_range() |
1001 | | * we call after we allocate stream chunk sc for newly received range r. |
1002 | | */ |
1003 | 7.30k | data = trim_partial_overlap(fs, sr, r, data); |
1004 | | |
1005 | 7.30k | sc = new_schunk(fs, pkt, r, data); |
1006 | 7.30k | if (sc == NULL) { |
1007 | 0 | sr = NULL; |
1008 | 0 | goto err; |
1009 | 0 | } |
1010 | 7.30k | DEBUG_PRINT(STDERR, "%s %p [ %llu, %llu ] -> %p [ %llu, %llu ]\n", |
1011 | 7.30k | OPENSSL_FUNC, (void *)sc, sc->sc_range.start, sc->sc_range.end, |
1012 | 7.30k | (void *)sr, sr->sr_range.start, sr->sr_range.end); |
1013 | | |
1014 | | /* |
1015 | | * Following calls can still be improved to handle |
1016 | | * chunks with direct storage better, but I don't think it's |
1017 | | * worth the effort. out of order short data chunks (less |
1018 | | * than DIRECT_STORAGE_SZ) should be considered exceptional. |
1019 | | */ |
1020 | 7.30k | if (sc->sc_range.start < sr->sr_range.start |
1021 | 4.53k | && sc->sc_range.end > sr->sr_range.end) { |
1022 | | /* new chunk includes the whole range */ |
1023 | 1.39k | replace_chunks_in_range(fs, sr, sc); |
1024 | 1.39k | sc = NULL; /* chunk got consumed */ |
1025 | 5.90k | } else if (sc->sc_range.end > sr->sr_range.end |
1026 | 2.76k | && sc->sc_range.start <= sr->sr_range.end) { |
1027 | 2.76k | append_chunk(fs, sr, sc); |
1028 | 2.76k | sc = NULL; /* chunk got consumed */ |
1029 | 3.14k | } else if (sc->sc_range.start < sr->sr_range.start |
1030 | 3.14k | && sc->sc_range.end >= sr->sr_range.start) { |
1031 | 3.14k | prepend_chunk(fs, sr, sc); |
1032 | 3.14k | sc = NULL; /* chunk got consumed */ |
1033 | 3.14k | } else { |
1034 | 0 | assert(NULL); /* unreachable */ |
1035 | 0 | sr = NULL; |
1036 | 0 | goto err; |
1037 | 0 | } |
1038 | | |
1039 | 7.30k | range_updated: |
1040 | | /* |
1041 | | * Range got updated we may need to join updated range with |
1042 | | * another ranges which exist in tree. The current range |
1043 | | * is removed here and used as a search key. If nothing is found |
1044 | | * range is inserted back to tree. |
1045 | | * |
1046 | | * If another range is found the ranges are merged to single |
1047 | | * range. The process repeats (merging ranges may be cascade effect, |
1048 | | * where more ranges collapse to single range). The merge result is |
1049 | | * removed from tree and used as a search key to find another range. |
1050 | | * If nothing is found then update is done. otherwise the ranges |
1051 | | * are merged again. |
1052 | | */ |
1053 | 7.30k | OSSL_RBT_REMOVE(srange, &fs->ranges, sr); |
1054 | 7.30k | fs->stream_ranges--; |
1055 | | /* |
1056 | | * _INSERT() returns range where sr needs to be joined |
1057 | | */ |
1058 | 7.30k | adjacent_sr = OSSL_RBT_INSERT(srange, &fs->ranges, sr); |
1059 | | |
1060 | 9.29k | while (adjacent_sr != NULL) { |
1061 | 1.99k | OSSL_RBT_REMOVE(srange, &fs->ranges, adjacent_sr); |
1062 | 1.99k | DEBUG_PRINT(STDERR, "%s %p [ %llu, %llu ] >< %p [ %llu, %llu ]\n", |
1063 | 1.99k | OPENSSL_FUNC, (void *)sr, sr->sr_range.start, sr->sr_range.end, |
1064 | 1.99k | (void *)adjacent_sr, adjacent_sr->sr_range.start, |
1065 | 1.99k | adjacent_sr->sr_range.end); |
1066 | 1.99k | fs->stream_ranges--; |
1067 | | |
1068 | 1.99k | if (sr->sr_range.start <= adjacent_sr->sr_range.start |
1069 | 1.39k | && sr->sr_range.end >= adjacent_sr->sr_range.end) { |
1070 | | /* |
1071 | | * adjacent_sr subset of sr |
1072 | | */ |
1073 | 859 | joined_sr = merge_ranges(fs, sr, adjacent_sr); |
1074 | 1.13k | } else if (sr->sr_range.start >= adjacent_sr->sr_range.start |
1075 | 594 | && sr->sr_range.end <= adjacent_sr->sr_range.end) { |
1076 | | /* |
1077 | | * sr subset of adjacent_sr |
1078 | | */ |
1079 | 0 | joined_sr = merge_ranges(fs, adjacent_sr, sr); |
1080 | 1.13k | } else if (sr->sr_range.start < adjacent_sr->sr_range.start |
1081 | 537 | && sr->sr_range.end >= adjacent_sr->sr_range.start) { |
1082 | | /* |
1083 | | * adjacent_sr follows sr |
1084 | | */ |
1085 | 537 | assert(sr->sr_range.end < adjacent_sr->sr_range.end); |
1086 | 537 | joined_sr = append_range(fs, sr, adjacent_sr); |
1087 | 594 | } else if (sr->sr_range.start <= adjacent_sr->sr_range.end |
1088 | 594 | && sr->sr_range.end > adjacent_sr->sr_range.end) { |
1089 | | /* |
1090 | | * sr follows adjacent_sr |
1091 | | */ |
1092 | 594 | assert(sr->sr_range.end > adjacent_sr->sr_range.end); |
1093 | 594 | joined_sr = append_range(fs, adjacent_sr, sr); |
1094 | 594 | } else { |
1095 | 0 | assert(NULL); /* never happens */ |
1096 | 0 | joined_sr = NULL; |
1097 | 0 | } |
1098 | 1.99k | if (joined_sr == NULL) |
1099 | 0 | goto err; |
1100 | | |
1101 | 1.99k | sr = joined_sr; |
1102 | 1.99k | adjacent_sr = OSSL_RBT_INSERT(srange, &fs->ranges, sr); |
1103 | 1.99k | } |
1104 | 7.30k | fs->stream_ranges++; |
1105 | 7.30k | } |
1106 | | |
1107 | 189k | done: |
1108 | 189k | return 1; |
1109 | | |
1110 | 0 | err: |
1111 | 0 | destroy_schunk(fs, sc); |
1112 | 0 | destroy_srange(fs, sr); |
1113 | 0 | destroy_srange(fs, adjacent_sr); |
1114 | | /* |
1115 | | * not enough memory (or another serious error) has occurred, |
1116 | | * any error here is fatal as some stream chunks could be ACKed |
1117 | | * already (RFC 9000, 31.1 Packet processing). At least stream |
1118 | | * needs to be reset. Preferred action is to close connection. |
1119 | | */ |
1120 | |
|
1121 | 0 | return 0; |
1122 | 189k | } |
1123 | | |
1124 | | /* |
1125 | | * peeks over the continuous range which is ready to |
1126 | | * read. ready to read means the fs->offset must be |
1127 | | * found in range. Also fs->offset can not reach past |
1128 | | * the first gap in stream data received so far, thus |
1129 | | * the only range we can use for peek operation is |
1130 | | * OSSL_RBT_MIN(&fs->ranges). |
1131 | | * |
1132 | | * NOTE: it is unsafe to carry more _peek() operations |
1133 | | * over single SFRMAE_SET. |
1134 | | */ |
1135 | | int ossl_sframe_set_peek(SFRAME_SET *fs, void **iterator, |
1136 | | UINT_RANGE *range, const unsigned char **data, |
1137 | | int *fin) |
1138 | 114M | { |
1139 | 114M | uint64_t start; |
1140 | 114M | struct stream_range_t *sr = (struct stream_range_t *)*iterator; |
1141 | 114M | struct stream_chunk_t *sc = NULL; |
1142 | | |
1143 | 114M | if (sr == NULL) { |
1144 | 114M | sr = OSSL_RBT_MIN(srange, &fs->ranges); |
1145 | 114M | start = fs->offset; |
1146 | 114M | if (sr != NULL) { |
1147 | 29.4M | sc = ossl_list_sc_head(&sr->sr_chunks); |
1148 | 29.4M | sr->sr_it_sc = NULL; |
1149 | 29.4M | assert(sc->sc_range.start == sr->sr_range.start); |
1150 | 29.4M | } |
1151 | | /* |
1152 | | * no chunks are ready to be consumed, if there is a gap. |
1153 | | */ |
1154 | 114M | if (sc != NULL && sc->sc_range.start > start) { |
1155 | 29.4M | DEBUG_PRINT(STDERR, "%s sc: %p sr: %p sc->start %llu, fs->offset: %llu\n", |
1156 | 29.4M | OPENSSL_FUNC, (void *)sc, (void *)sr, sc->sc_range.start, start); |
1157 | 29.4M | sc = NULL; |
1158 | 29.4M | } |
1159 | 114M | } else if (sr == OSSL_RBT_MIN(srange, &fs->ranges) && sr->sr_it_sc != NULL) { |
1160 | | /* |
1161 | | * sr == _RB_MIN(), revalidates iterator in case the range we |
1162 | | * work with disappears because it's got joined with other range |
1163 | | * after new chunk arrival. perhaps not issue now as those operations |
1164 | | * are mutually exclusive now. |
1165 | | * |
1166 | | * sr->sr_it_sc becomes NULL on _move() or _flatten() operation. |
1167 | | * |
1168 | | * We may need to revisit iterator implementation as current |
1169 | | * iterator supports one caller only. |
1170 | | */ |
1171 | 3.62k | start = sr->sr_it_sc->sc_range.end; |
1172 | 3.62k | sc = ossl_list_sc_next(sr->sr_it_sc); |
1173 | 3.62k | assert(sc == NULL || sc->sc_range.start == start); |
1174 | 3.62k | assert(sc == NULL || sc->sc_range.start < sc->sc_range.end); |
1175 | 3.62k | } else { |
1176 | | /* iterator got invalidated by move/flatten operation on range */ |
1177 | 0 | DEBUG_PRINT(STDERR, "%s iterator got invalidated\n", OPENSSL_FUNC); |
1178 | 0 | return 0; |
1179 | 0 | } |
1180 | | |
1181 | 114M | range->start = start; |
1182 | | |
1183 | 114M | if (sc == NULL) { |
1184 | 114M | range->end = start; |
1185 | 114M | *data = NULL; |
1186 | 114M | *iterator = NULL; |
1187 | | |
1188 | | /* |
1189 | | * set fin only if we are at the end of the stream and application |
1190 | | * can read from the stream. In other words: there must be no gap |
1191 | | * between FIN offset and offset where application reads from stream. |
1192 | | */ |
1193 | 114M | if (fs->fin && start == fs->fin_off) |
1194 | 859 | *fin = fs->fin; |
1195 | 114M | else |
1196 | 114M | *fin = 0; |
1197 | | |
1198 | 114M | DEBUG_PRINT(STDERR, "%s no more chunks\n", OPENSSL_FUNC); |
1199 | | |
1200 | 114M | return 0; |
1201 | 114M | } |
1202 | | |
1203 | 79.6k | range->end = sc->sc_range.end; |
1204 | | /* chunk keeps data always attached, data dies with chunk */ |
1205 | 79.6k | assert(sc->sc_data != NULL); |
1206 | 79.6k | assert(sc->sc_range.start <= start); |
1207 | 79.6k | *data = sc->sc_data + (start - sc->sc_range.start); |
1208 | 79.6k | *fin = fs->fin && sc->sc_range.end == fs->fin_off; |
1209 | | |
1210 | 79.6k | if (sr->sr_it_sc != NULL) |
1211 | 146 | DEBUG_PRINT(STDERR, "%s %p [ %llu, %llu ] %p [ %llu, %llu ]\n", |
1212 | 146 | OPENSSL_FUNC, (void *)sr->sr_it_sc, |
1213 | 146 | sr->sr_it_sc->sc_range.start, sr->sr_it_sc->sc_range.end, |
1214 | 146 | (void *)sc, sc->sc_range.start, sc->sc_range.end); |
1215 | | |
1216 | 79.6k | sr->sr_it_sc = sc; |
1217 | 79.6k | *iterator = sr; |
1218 | | |
1219 | | /* |
1220 | | * peek operation indicates error if there are no data to read |
1221 | | * in range. |
1222 | | */ |
1223 | 79.6k | DEBUG_PRINT(STDERR, |
1224 | 79.6k | "%s peek range: [ %llu, %llu ] range: %p [ %llu, %llu ]\n", OPENSSL_FUNC, |
1225 | 79.6k | range->start, range->end, (void *)sr, sr->sr_range.start, |
1226 | 79.6k | sr->sr_range.end); |
1227 | | |
1228 | 79.6k | return (range->start == range->end) ? 0 : 1; |
1229 | 79.6k | } |
1230 | | |
1231 | | void ossl_sframe_set_destroy_ranges(SFRAME_SET *fs) |
1232 | 299k | { |
1233 | 299k | struct stream_range_t *sr, *save_sr; |
1234 | | |
1235 | 299k | OSSL_RBT_FOREACH_SAFE (sr, srange, &fs->ranges, save_sr) { |
1236 | 18.4k | OSSL_RBT_REMOVE(srange, &fs->ranges, sr); |
1237 | 18.4k | fs->stream_ranges--; |
1238 | 18.4k | destroy_srange(fs, sr); |
1239 | 18.4k | } |
1240 | 299k | } |
1241 | | |
1242 | | /* |
1243 | | * moves the read offset, freeing all chunks which end offset |
1244 | | * is less than new_offset |
1245 | | * sc->sc_range.end < new_offset |
1246 | | */ |
1247 | | int ossl_sframe_set_move_offset(SFRAME_SET *fs, uint64_t new_offset) |
1248 | 79.5k | { |
1249 | 79.5k | struct stream_range_t *sr = OSSL_RBT_MIN(srange, &fs->ranges); |
1250 | 79.5k | struct stream_chunk_t *sc, *save_sc; |
1251 | 79.5k | size_t unused_sz; |
1252 | | |
1253 | 79.5k | if (new_offset == fs->offset) |
1254 | 0 | return 1; |
1255 | | |
1256 | | /* |
1257 | | * Offset can move forward within the continuous head range only. |
1258 | | * It can not move backward, into a gap or past the head range end. |
1259 | | */ |
1260 | 79.5k | if (sr == NULL || new_offset < fs->offset |
1261 | 79.5k | || new_offset < sr->sr_range.start || new_offset > sr->sr_range.end) |
1262 | 0 | return 0; |
1263 | | |
1264 | 79.5k | fs->offset = new_offset; |
1265 | | |
1266 | 81.7k | OSSL_LIST_FOREACH_DELSAFE (sc, save_sc, sc, &sr->sr_chunks) { |
1267 | 81.7k | if (new_offset >= sc->sc_range.end) { |
1268 | 78.1k | ossl_list_sc_remove(&sr->sr_chunks, sc); |
1269 | 78.1k | fs->stream_chunks--; |
1270 | 78.1k | if (sr->sr_it_sc == sc) |
1271 | 78.0k | sr->sr_it_sc = NULL; /* invalidate iterator chunk */ |
1272 | 78.1k | destroy_schunk(fs, sc); |
1273 | 78.1k | } else { |
1274 | 3.57k | break; |
1275 | 3.57k | } |
1276 | 81.7k | } |
1277 | | |
1278 | 79.5k | DEBUG_PRINT(STDERR, "%s offset: %llu -> %llu range: %p [ %llu, %llu ] -> ", |
1279 | 79.5k | OPENSSL_FUNC, fs->offset - new_offset, new_offset, |
1280 | 79.5k | (void *)sr, sr->sr_range.start, sr->sr_range.end); |
1281 | | |
1282 | 79.5k | if (sc == NULL) { |
1283 | | /* |
1284 | | * the whole range was consumed. |
1285 | | * this step invalidates iterator we use in ossl_sframe_peek() |
1286 | | */ |
1287 | 75.9k | OSSL_RBT_REMOVE(srange, &fs->ranges, sr); |
1288 | 75.9k | destroy_srange(fs, sr); |
1289 | 75.9k | fs->stream_ranges--; |
1290 | 75.9k | DEBUG_PRINT(STDERR, "[ NULL ]\n"); |
1291 | 75.9k | } else { |
1292 | 3.57k | unused_sz = UINT64_TO_SIZE_T(new_offset - sc->sc_range.start); |
1293 | 3.57k | sc_data_trim_left(sc, unused_sz, fs->cleanse); |
1294 | 3.57k | sc->sc_range.start = new_offset; |
1295 | 3.57k | sr->sr_range.start = new_offset; |
1296 | 3.57k | DEBUG_PRINT(STDERR, "[ %lli, %llu ]\n", |
1297 | 3.57k | sr->sr_range.start, sr->sr_range.end); |
1298 | | |
1299 | 3.57k | if (sc->sc_st == ST_TYPE_PKT) { |
1300 | 3.57k | rsqp_add_overhead(fs->rsqp, unused_sz); |
1301 | 3.57k | DEBUG_PRINT(STDERR, "%s sc: %p unused_sz: %zu %zu -> %zu\n", |
1302 | 3.57k | OPENSSL_FUNC, (void *)sc, unused_sz, |
1303 | 3.57k | fs->rsqp->rsqp_pkt_overhead_sz - unused_sz, |
1304 | 3.57k | fs->rsqp->rsqp_pkt_overhead_sz); |
1305 | 3.57k | } |
1306 | 3.57k | } |
1307 | | |
1308 | 79.5k | return 1; |
1309 | 79.5k | } |
1310 | | |
1311 | | /* Contiguous bytes available from fs->offset, in O(log n): the first range. */ |
1312 | | int ossl_sframe_set_avail(SFRAME_SET *fs, uint64_t *avail, int *fin) |
1313 | 17.9M | { |
1314 | 17.9M | struct stream_range_t *sr = OSSL_RBT_MIN(srange, &fs->ranges); |
1315 | | |
1316 | 17.9M | if (sr != NULL && sr->sr_range.start <= fs->offset |
1317 | 3.25k | && sr->sr_range.end > fs->offset) |
1318 | 3.25k | *avail = sr->sr_range.end - fs->offset; |
1319 | 17.9M | else |
1320 | 17.9M | *avail = 0; |
1321 | 17.9M | *fin = (fs->fin && fs->offset + *avail == fs->fin_off) ? 1 : 0; |
1322 | 17.9M | return 1; |
1323 | 17.9M | } |
1324 | | |
1325 | | QUIC_RSTREAM_QPARM *ossl_quic_rstream_qparm_new(QUIC_CHANNEL *ch) |
1326 | 53.0k | { |
1327 | 53.0k | QUIC_RSTREAM_QPARM *rsqp; |
1328 | | |
1329 | 53.0k | rsqp = OPENSSL_malloc(sizeof(QUIC_RSTREAM_QPARM)); |
1330 | 53.0k | if (rsqp != NULL) { |
1331 | 53.0k | rsqp->rsqp_pkt_overhead_treshold = PKT_BUFFER_OVERHEAD_TRESHOLD; |
1332 | 53.0k | rsqp->rsqp_pkt_overhead_sz = 0; |
1333 | 53.0k | rsqp->rsqp_ch = ch; |
1334 | 53.0k | } |
1335 | | |
1336 | 53.0k | return rsqp; |
1337 | 53.0k | } |
1338 | | |
1339 | | void ossl_quic_rstream_qparm_destroy(QUIC_RSTREAM_QPARM *rsqp) |
1340 | 53.0k | { |
1341 | 53.0k | if (rsqp != NULL) { |
1342 | 53.0k | assert(rsqp->rsqp_pkt_overhead_sz == 0); |
1343 | 53.0k | OPENSSL_free(rsqp); |
1344 | 53.0k | } |
1345 | 53.0k | } |