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