Coverage Report

Created: 2025-06-13 06:58

/src/openssl32/ssl/quic/uint_set.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include "internal/uint_set.h"
11
#include "internal/common.h"
12
#include <assert.h>
13
14
/*
15
 * uint64_t Integer Sets
16
 * =====================
17
 *
18
 * This data structure supports the following operations:
19
 *
20
 *   Insert Range: Adds an inclusive range of integers [start, end]
21
 *                 to the set. Equivalent to Insert for each number
22
 *                 in the range.
23
 *
24
 *   Remove Range: Removes an inclusive range of integers [start, end]
25
 *                 from the set. Not all of the range need already be in
26
 *                 the set, but any part of the range in the set is removed.
27
 *
28
 *   Query:        Is an integer in the data structure?
29
 *
30
 * The data structure can be iterated.
31
 *
32
 * For greater efficiency in tracking large numbers of contiguous integers, we
33
 * track integer ranges rather than individual integers. The data structure
34
 * manages a list of integer ranges [[start, end]...]. Internally this is
35
 * implemented as a doubly linked sorted list of range structures, which are
36
 * automatically split and merged as necessary.
37
 *
38
 * This data structure requires O(n) traversal of the list for insertion,
39
 * removal and query when we are not adding/removing ranges which are near the
40
 * beginning or end of the set of ranges. For the applications for which this
41
 * data structure is used (e.g. QUIC PN tracking for ACK generation), it is
42
 * expected that the number of integer ranges needed at any given time will
43
 * generally be small and that most operations will be close to the beginning or
44
 * end of the range.
45
 *
46
 * Invariant: The data structure is always sorted in ascending order by value.
47
 *
48
 * Invariant: No two adjacent ranges ever 'border' one another (have no
49
 *            numerical gap between them) as the data structure always ensures
50
 *            such ranges are merged.
51
 *
52
 * Invariant: No two ranges ever overlap.
53
 *
54
 * Invariant: No range [a, b] ever has a > b.
55
 *
56
 * Invariant: Since ranges are represented using inclusive bounds, no range
57
 *            item inside the data structure can represent a span of zero
58
 *            integers.
59
 */
60
void ossl_uint_set_init(UINT_SET *s)
61
235k
{
62
235k
    ossl_list_uint_set_init(s);
63
235k
}
64
65
void ossl_uint_set_destroy(UINT_SET *s)
66
235k
{
67
235k
    UINT_SET_ITEM *x, *xnext;
68
69
321k
    for (x = ossl_list_uint_set_head(s); x != NULL; x = xnext) {
70
86.0k
        xnext = ossl_list_uint_set_next(x);
71
86.0k
        OPENSSL_free(x);
72
86.0k
    }
73
235k
}
74
75
/* Possible merge of x, prev(x) */
76
static void uint_set_merge_adjacent(UINT_SET *s, UINT_SET_ITEM *x)
77
5.55k
{
78
5.55k
    UINT_SET_ITEM *xprev = ossl_list_uint_set_prev(x);
79
80
5.55k
    if (xprev == NULL)
81
1.22k
        return;
82
83
4.32k
    if (x->range.start - 1 != xprev->range.end)
84
3.55k
        return;
85
86
778
    x->range.start = xprev->range.start;
87
778
    ossl_list_uint_set_remove(s, xprev);
88
778
    OPENSSL_free(xprev);
89
778
}
90
91
static uint64_t u64_min(uint64_t x, uint64_t y)
92
11.3M
{
93
11.3M
    return x < y ? x : y;
94
11.3M
}
95
96
static uint64_t u64_max(uint64_t x, uint64_t y)
97
11.3M
{
98
11.3M
    return x > y ? x : y;
99
11.3M
}
100
101
/*
102
 * Returns 1 if there exists an integer x which falls within both ranges a and
103
 * b.
104
 */
105
static int uint_range_overlaps(const UINT_RANGE *a,
106
                               const UINT_RANGE *b)
107
11.3M
{
108
11.3M
    return u64_min(a->end, b->end)
109
11.3M
        >= u64_max(a->start, b->start);
110
11.3M
}
111
112
static UINT_SET_ITEM *create_set_item(uint64_t start, uint64_t end)
113
348k
{
114
348k
    UINT_SET_ITEM *x = OPENSSL_malloc(sizeof(UINT_SET_ITEM));
115
116
348k
    if (x == NULL)
117
0
        return NULL;
118
119
348k
    ossl_list_uint_set_init_elem(x);
120
348k
    x->range.start = start;
121
348k
    x->range.end   = end;
122
348k
    return x;
123
348k
}
124
125
int ossl_uint_set_insert(UINT_SET *s, const UINT_RANGE *range)
126
363k
{
127
363k
    UINT_SET_ITEM *x, *xnext, *z, *zprev, *f;
128
363k
    uint64_t start = range->start, end = range->end;
129
130
363k
    if (!ossl_assert(start <= end))
131
0
        return 0;
132
133
363k
    if (ossl_list_uint_set_is_empty(s)) {
134
        /* Nothing in the set yet, so just add this range. */
135
90.1k
        x = create_set_item(start, end);
136
90.1k
        if (x == NULL)
137
0
            return 0;
138
90.1k
        ossl_list_uint_set_insert_head(s, x);
139
90.1k
        return 1;
140
90.1k
    }
141
142
273k
    z = ossl_list_uint_set_tail(s);
143
273k
    if (start > z->range.end) {
144
        /*
145
         * Range is after the latest range in the set, so append.
146
         *
147
         * Note: The case where the range is before the earliest range in the
148
         * set is handled as a degenerate case of the final case below. See
149
         * optimization note (*) below.
150
         */
151
197k
        if (z->range.end + 1 == start) {
152
9.53k
            z->range.end = end;
153
9.53k
            return 1;
154
9.53k
        }
155
156
187k
        x = create_set_item(start, end);
157
187k
        if (x == NULL)
158
0
            return 0;
159
187k
        ossl_list_uint_set_insert_tail(s, x);
160
187k
        return 1;
161
187k
    }
162
163
76.1k
    f = ossl_list_uint_set_head(s);
164
76.1k
    if (start <= f->range.start && end >= z->range.end) {
165
        /*
166
         * New range dwarfs all ranges in our set.
167
         *
168
         * Free everything except the first range in the set, which we scavenge
169
         * and reuse.
170
         */
171
0
        x = ossl_list_uint_set_head(s);
172
0
        x->range.start = start;
173
0
        x->range.end = end;
174
0
        for (x = ossl_list_uint_set_next(x); x != NULL; x = xnext) {
175
0
            xnext = ossl_list_uint_set_next(x);
176
0
            ossl_list_uint_set_remove(s, x);
177
0
        }
178
0
        return 1;
179
0
    }
180
181
    /*
182
     * Walk backwards since we will most often be inserting at the end. As an
183
     * optimization, test the head node first and skip iterating over the
184
     * entire list if we are inserting at the start. The assumption is that
185
     * insertion at the start and end of the space will be the most common
186
     * operations. (*)
187
     */
188
76.1k
    z = end < f->range.start ? f : z;
189
190
155k
    for (; z != NULL; z = zprev) {
191
155k
        zprev = ossl_list_uint_set_prev(z);
192
193
        /* An existing range dwarfs our new range (optimisation). */
194
155k
        if (z->range.start <= start && z->range.end >= end)
195
19
            return 1;
196
197
155k
        if (uint_range_overlaps(&z->range, range)) {
198
            /*
199
             * Our new range overlaps an existing range, or possibly several
200
             * existing ranges.
201
             */
202
0
            UINT_SET_ITEM *ovend = z;
203
204
0
            ovend->range.end = u64_max(end, z->range.end);
205
206
            /* Get earliest overlapping range. */
207
0
            while (zprev != NULL && uint_range_overlaps(&zprev->range, range)) {
208
0
                z = zprev;
209
0
                zprev = ossl_list_uint_set_prev(z);
210
0
            }
211
212
0
            ovend->range.start = u64_min(start, z->range.start);
213
214
            /* Replace sequence of nodes z..ovend with updated ovend only. */
215
0
            while (z != ovend) {
216
0
                z = ossl_list_uint_set_next(x = z);
217
0
                ossl_list_uint_set_remove(s, x);
218
0
                OPENSSL_free(x);
219
0
            }
220
0
            break;
221
155k
        } else if (end < z->range.start
222
155k
                    && (zprev == NULL || start > zprev->range.end)) {
223
76.1k
            if (z->range.start == end + 1) {
224
                /* We can extend the following range backwards. */
225
3.36k
                z->range.start = start;
226
227
                /*
228
                 * If this closes a gap we now need to merge
229
                 * consecutive nodes.
230
                 */
231
3.36k
                uint_set_merge_adjacent(s, z);
232
72.7k
            } else if (zprev != NULL && zprev->range.end + 1 == start) {
233
                /* We can extend the preceding range forwards. */
234
2.18k
                zprev->range.end = end;
235
236
                /*
237
                 * If this closes a gap we now need to merge
238
                 * consecutive nodes.
239
                 */
240
2.18k
                uint_set_merge_adjacent(s, z);
241
70.5k
            } else {
242
                /*
243
                 * The new interval is between intervals without overlapping or
244
                 * touching them, so insert between, preserving sort.
245
                 */
246
70.5k
                x = create_set_item(start, end);
247
70.5k
                if (x == NULL)
248
0
                    return 0;
249
70.5k
                ossl_list_uint_set_insert_before(s, z, x);
250
70.5k
            }
251
76.1k
            break;
252
76.1k
        }
253
155k
    }
254
255
76.1k
    return 1;
256
76.1k
}
257
258
int ossl_uint_set_remove(UINT_SET *s, const UINT_RANGE *range)
259
406k
{
260
406k
    UINT_SET_ITEM *z, *zprev, *y;
261
406k
    uint64_t start = range->start, end = range->end;
262
263
406k
    if (!ossl_assert(start <= end))
264
0
        return 0;
265
266
    /* Walk backwards since we will most often be removing at the end. */
267
11.8M
    for (z = ossl_list_uint_set_tail(s); z != NULL; z = zprev) {
268
11.4M
        zprev = ossl_list_uint_set_prev(z);
269
270
11.4M
        if (start > z->range.end)
271
            /* No overlapping ranges can exist beyond this point, so stop. */
272
51
            break;
273
274
11.4M
        if (start <= z->range.start && end >= z->range.end) {
275
            /*
276
             * The range being removed dwarfs this range, so it should be
277
             * removed.
278
             */
279
261k
            ossl_list_uint_set_remove(s, z);
280
261k
            OPENSSL_free(z);
281
11.2M
        } else if (start <= z->range.start && end >= z->range.start) {
282
            /*
283
             * The range being removed includes start of this range, but does
284
             * not cover the entire range (as this would be caught by the case
285
             * above). Shorten the range.
286
             */
287
12.7k
            assert(end < z->range.end);
288
12.7k
            z->range.start = end + 1;
289
11.1M
        } else if (end >= z->range.end) {
290
            /*
291
             * The range being removed includes the end of this range, but does
292
             * not cover the entire range (as this would be caught by the case
293
             * above). Shorten the range. We can also stop iterating.
294
             */
295
0
            assert(start > z->range.start);
296
0
            assert(start > 0);
297
0
            z->range.end = start - 1;
298
0
            break;
299
11.1M
        } else if (start > z->range.start && end < z->range.end) {
300
            /*
301
             * The range being removed falls entirely in this range, so cut it
302
             * into two. Cases where a zero-length range would be created are
303
             * handled by the above cases.
304
             */
305
0
            y = create_set_item(end + 1, z->range.end);
306
0
            ossl_list_uint_set_insert_after(s, z, y);
307
0
            z->range.end = start - 1;
308
0
            break;
309
11.1M
        } else {
310
            /* Assert no partial overlap; all cases should be covered above. */
311
11.1M
            assert(!uint_range_overlaps(&z->range, range));
312
11.1M
        }
313
11.4M
    }
314
315
406k
    return 1;
316
406k
}
317
318
int ossl_uint_set_query(const UINT_SET *s, uint64_t v)
319
856k
{
320
856k
    UINT_SET_ITEM *x;
321
322
856k
    if (ossl_list_uint_set_is_empty(s))
323
87.0k
        return 0;
324
325
1.14M
    for (x = ossl_list_uint_set_tail(s); x != NULL; x = ossl_list_uint_set_prev(x))
326
1.13M
        if (x->range.start <= v && x->range.end >= v)
327
63.5k
            return 1;
328
1.07M
        else if (x->range.end < v)
329
692k
            return 0;
330
331
13.6k
    return 0;
332
769k
}