Coverage Report

Created: 2026-10-28 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/uri/uriparser/src/UriQuery.c
Line
Count
Source
1
/*
2
 * uriparser - RFC 3986 URI parsing library
3
 *
4
 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5
 * Copyright (C) 2007, Sebastian Pipping <sebastian@pipping.org>
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source  and binary forms, with or without
9
 * modification, are permitted provided  that the following conditions
10
 * are met:
11
 *
12
 *     1. Redistributions  of  source  code   must  retain  the  above
13
 *        copyright notice, this list  of conditions and the following
14
 *        disclaimer.
15
 *
16
 *     2. Redistributions  in binary  form  must  reproduce the  above
17
 *        copyright notice, this list  of conditions and the following
18
 *        disclaimer  in  the  documentation  and/or  other  materials
19
 *        provided with the distribution.
20
 *
21
 *     3. Neither the  name of the  copyright holder nor the  names of
22
 *        its contributors may be used  to endorse or promote products
23
 *        derived from  this software  without specific  prior written
24
 *        permission.
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27
 * "AS IS" AND  ANY EXPRESS OR IMPLIED WARRANTIES,  INCLUDING, BUT NOT
28
 * LIMITED TO,  THE IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS
29
 * FOR  A  PARTICULAR  PURPOSE  ARE  DISCLAIMED.  IN  NO  EVENT  SHALL
30
 * THE  COPYRIGHT HOLDER  OR CONTRIBUTORS  BE LIABLE  FOR ANY  DIRECT,
31
 * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
 * (INCLUDING, BUT NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE GOODS OR
33
 * SERVICES; LOSS OF USE, DATA,  OR PROFITS; OR BUSINESS INTERRUPTION)
34
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35
 * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
36
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37
 * OF THE POSSIBILITY OF SUCH DAMAGE.
38
 */
39
40
/* What encodings are enabled? */
41
#include <uriparser/UriDefsConfig.h>
42
#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43
/* Include SELF twice */
44
#  ifdef URI_ENABLE_ANSI
45
#    define URI_PASS_ANSI 1
46
#    include "UriQuery.c"
47
#    undef URI_PASS_ANSI
48
#  endif
49
#  ifdef URI_ENABLE_UNICODE
50
#    define URI_PASS_UNICODE 1
51
#    include "UriQuery.c"
52
#    undef URI_PASS_UNICODE
53
#  endif
54
#else
55
#  ifdef URI_PASS_ANSI
56
#    include <uriparser/UriDefsAnsi.h>
57
#  else
58
#    include <uriparser/UriDefsUnicode.h>
59
#    include <wchar.h>
60
#  endif
61
62
#  ifndef URI_DOXYGEN
63
#    include <uriparser/Uri.h>
64
#    include "UriCommon.h"
65
#    include "UriMemory.h"
66
#  endif
67
68
#  include <limits.h>
69
#  include <stddef.h> /* size_t */
70
#  include <stdint.h>  // SIZE_MAX
71
72
static int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest,
73
                                        const URI_TYPE(QueryList) * queryList,
74
                                        int maxChars, int * charsWritten,
75
                                        int * charsRequired, UriBool spaceToPlus,
76
                                        UriBool normalizeBreaks);
77
78
static UriBool URI_FUNC(AppendQueryItem)(
79
    URI_TYPE(QueryList) * *prevNext, int * itemCount, const URI_CHAR * keyFirst,
80
    const URI_CHAR * keyAfter, const URI_CHAR * valueFirst, const URI_CHAR * valueAfter,
81
    UriBool plusToSpace, UriBreakConversion breakConversion, UriMemoryManager * memory);
82
83
int URI_FUNC(ComposeQueryCharsRequired)(const URI_TYPE(QueryList) * queryList,
84
0
                                        int * charsRequired) {
85
0
    const UriBool spaceToPlus = URI_TRUE;
86
0
    const UriBool normalizeBreaks = URI_TRUE;
87
88
0
    return URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, charsRequired, spaceToPlus,
89
0
                                                 normalizeBreaks);
90
0
}
Unexecuted instantiation: uriComposeQueryCharsRequiredA
Unexecuted instantiation: uriComposeQueryCharsRequiredW
91
92
int URI_FUNC(ComposeQueryCharsRequiredEx)(const URI_TYPE(QueryList) * queryList,
93
                                          int * charsRequired, UriBool spaceToPlus,
94
0
                                          UriBool normalizeBreaks) {
95
0
    if ((queryList == NULL) || (charsRequired == NULL)) {
96
0
        return URI_ERROR_NULL;
97
0
    }
98
99
0
    return URI_FUNC(ComposeQueryEngine)(NULL, queryList, 0, NULL, charsRequired,
100
0
                                        spaceToPlus, normalizeBreaks);
101
0
}
Unexecuted instantiation: uriComposeQueryCharsRequiredExA
Unexecuted instantiation: uriComposeQueryCharsRequiredExW
102
103
int URI_FUNC(ComposeQuery)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList,
104
0
                           int maxChars, int * charsWritten) {
105
0
    const UriBool spaceToPlus = URI_TRUE;
106
0
    const UriBool normalizeBreaks = URI_TRUE;
107
108
0
    return URI_FUNC(ComposeQueryEx)(dest, queryList, maxChars, charsWritten, spaceToPlus,
109
0
                                    normalizeBreaks);
110
0
}
Unexecuted instantiation: uriComposeQueryA
Unexecuted instantiation: uriComposeQueryW
111
112
int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList,
113
                             int maxChars, int * charsWritten, UriBool spaceToPlus,
114
0
                             UriBool normalizeBreaks) {
115
0
    if ((dest == NULL) || (queryList == NULL)) {
116
0
        return URI_ERROR_NULL;
117
0
    }
118
119
0
    if (maxChars < 1) {
120
0
        return URI_ERROR_OUTPUT_TOO_LARGE;
121
0
    }
122
123
0
    return URI_FUNC(ComposeQueryEngine)(dest, queryList, maxChars, charsWritten, NULL,
124
0
                                        spaceToPlus, normalizeBreaks);
125
0
}
Unexecuted instantiation: uriComposeQueryExA
Unexecuted instantiation: uriComposeQueryExW
126
127
int URI_FUNC(ComposeQueryMalloc)(URI_CHAR ** dest,
128
0
                                 const URI_TYPE(QueryList) * queryList) {
129
0
    const UriBool spaceToPlus = URI_TRUE;
130
0
    const UriBool normalizeBreaks = URI_TRUE;
131
132
0
    return URI_FUNC(ComposeQueryMallocEx)(dest, queryList, spaceToPlus, normalizeBreaks);
133
0
}
Unexecuted instantiation: uriComposeQueryMallocA
Unexecuted instantiation: uriComposeQueryMallocW
134
135
int URI_FUNC(ComposeQueryMallocEx)(URI_CHAR ** dest,
136
                                   const URI_TYPE(QueryList) * queryList,
137
0
                                   UriBool spaceToPlus, UriBool normalizeBreaks) {
138
0
    return URI_FUNC(ComposeQueryMallocExMm)(dest, queryList, spaceToPlus, normalizeBreaks,
139
0
                                            NULL);
140
0
}
Unexecuted instantiation: uriComposeQueryMallocExA
Unexecuted instantiation: uriComposeQueryMallocExW
141
142
int URI_FUNC(ComposeQueryMallocExMm)(URI_CHAR ** dest,
143
                                     const URI_TYPE(QueryList) * queryList,
144
                                     UriBool spaceToPlus, UriBool normalizeBreaks,
145
0
                                     UriMemoryManager * memory) {
146
0
    int charsRequired;
147
0
    int res;
148
0
    URI_CHAR * queryString;
149
150
0
    if (dest == NULL) {
151
0
        return URI_ERROR_NULL;
152
0
    }
153
154
0
    URI_CHECK_MEMORY_MANAGER(memory); /* may return */
155
156
    /* Calculate space */
157
0
    res = URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, &charsRequired, spaceToPlus,
158
0
                                                normalizeBreaks);
159
0
    if (res != URI_SUCCESS) {
160
0
        return res;
161
0
    }
162
0
    if (charsRequired == INT_MAX) {
163
0
        return URI_ERROR_MALLOC;
164
0
    }
165
0
    charsRequired++;
166
167
    /* Allocate space */
168
0
    queryString = memory->calloc(memory, charsRequired, sizeof(URI_CHAR));
169
0
    if (queryString == NULL) {
170
0
        return URI_ERROR_MALLOC;
171
0
    }
172
173
    /* Put query in */
174
0
    res = URI_FUNC(ComposeQueryEx)(queryString, queryList, charsRequired, NULL,
175
0
                                   spaceToPlus, normalizeBreaks);
176
0
    if (res != URI_SUCCESS) {
177
0
        memory->free(memory, queryString);
178
0
        return res;
179
0
    }
180
181
0
    *dest = queryString;
182
0
    return URI_SUCCESS;
183
0
}
Unexecuted instantiation: uriComposeQueryMallocExMmA
Unexecuted instantiation: uriComposeQueryMallocExMmW
184
185
int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList,
186
                                 int maxChars, int * charsWritten, int * charsRequired,
187
0
                                 UriBool spaceToPlus, UriBool normalizeBreaks) {
188
0
    UriBool firstItem = URI_TRUE;
189
0
    int ampersandLen = 0; /* increased to 1 from second item on */
190
0
    URI_CHAR * write = dest;
191
192
    /* Subtract terminator */
193
0
    if (dest == NULL) {
194
0
        *charsRequired = 0;
195
0
    } else {
196
0
        maxChars--;
197
0
    }
198
199
0
    while (queryList != NULL) {
200
0
        const URI_CHAR * const key = queryList->key;
201
0
        const URI_CHAR * const value = queryList->value;
202
0
        const int worstCase = (normalizeBreaks == URI_TRUE ? 6 : 3);
203
0
        const size_t keyLen = (key == NULL) ? 0 : URI_STRLEN(key);
204
0
        int keyRequiredChars;
205
0
        const size_t valueLen = (value == NULL) ? 0 : URI_STRLEN(value);
206
0
        int valueRequiredChars;
207
208
0
        if ((keyLen >= (size_t)INT_MAX / worstCase)
209
0
            || (valueLen >= (size_t)INT_MAX / worstCase)) {
210
0
            return URI_ERROR_OUTPUT_TOO_LARGE;
211
0
        }
212
0
        keyRequiredChars = worstCase * (int)keyLen;
213
0
        valueRequiredChars = worstCase * (int)valueLen;
214
215
0
        if (dest == NULL) {
216
0
            (*charsRequired) += ampersandLen + keyRequiredChars
217
0
                                + ((value == NULL) ? 0 : 1 + valueRequiredChars);
218
219
0
            if (firstItem == URI_TRUE) {
220
0
                ampersandLen = 1;
221
0
                firstItem = URI_FALSE;
222
0
            }
223
0
        } else {
224
0
            if ((write - dest) + ampersandLen + keyRequiredChars > maxChars) {
225
0
                return URI_ERROR_OUTPUT_TOO_LARGE;
226
0
            }
227
228
            /* Copy key */
229
0
            if (firstItem == URI_TRUE) {
230
0
                ampersandLen = 1;
231
0
                firstItem = URI_FALSE;
232
0
            } else {
233
0
                write[0] = _UT('&');
234
0
                write++;
235
0
            }
236
0
            write = URI_FUNC(EscapeEx)(key, key + keyLen, write, spaceToPlus,
237
0
                                       normalizeBreaks);
238
239
0
            if (value != NULL) {
240
0
                if ((write - dest) + 1 + valueRequiredChars > maxChars) {
241
0
                    return URI_ERROR_OUTPUT_TOO_LARGE;
242
0
                }
243
244
                /* Copy value */
245
0
                write[0] = _UT('=');
246
0
                write++;
247
0
                write = URI_FUNC(EscapeEx)(value, value + valueLen, write, spaceToPlus,
248
0
                                           normalizeBreaks);
249
0
            }
250
0
        }
251
252
0
        queryList = queryList->next;
253
0
    }
254
255
0
    if (dest != NULL) {
256
0
        write[0] = _UT('\0');
257
0
        if (charsWritten != NULL) {
258
0
            const size_t lenInChars = write - dest;
259
260
            // Detect and avoid integer overflow
261
0
            if (lenInChars > INT_MAX - 1) {
262
0
                return URI_ERROR_OUTPUT_TOO_LARGE;
263
0
            }
264
265
0
            *charsWritten = (int)(lenInChars + 1); /* .. for terminator */
266
0
        }
267
0
    }
268
269
0
    return URI_SUCCESS;
270
0
}
Unexecuted instantiation: UriQuery.c:uriComposeQueryEngineA
Unexecuted instantiation: UriQuery.c:uriComposeQueryEngineW
271
272
UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) * *prevNext, int * itemCount,
273
                                  const URI_CHAR * keyFirst, const URI_CHAR * keyAfter,
274
                                  const URI_CHAR * valueFirst,
275
                                  const URI_CHAR * valueAfter, UriBool plusToSpace,
276
                                  UriBreakConversion breakConversion,
277
0
                                  UriMemoryManager * memory) {
278
0
    const size_t keyLen = keyAfter - keyFirst;
279
0
    const size_t valueLen = valueAfter - valueFirst;
280
0
    URI_CHAR * key;
281
0
    URI_CHAR * value;
282
283
0
    if ((prevNext == NULL) || (itemCount == NULL) || (keyFirst == NULL)
284
0
        || (keyAfter == NULL) || (keyFirst > keyAfter) || (valueFirst > valueAfter)
285
0
        || ((keyFirst == keyAfter) && (valueFirst == NULL) && (valueAfter == NULL))) {
286
0
        return URI_TRUE;
287
0
    }
288
289
    /* Append new empty item */
290
0
    *prevNext = memory->malloc(memory, 1 * sizeof(URI_TYPE(QueryList)));
291
0
    if (*prevNext == NULL) {
292
0
        return URI_FALSE; /* Raises malloc error */
293
0
    }
294
0
    (*prevNext)->next = NULL;
295
296
    // Detect integer overflow
297
0
    if ((keyLen > SIZE_MAX - 1) || (keyLen + 1 > SIZE_MAX / sizeof(URI_CHAR))) {
298
0
        memory->free(memory, *prevNext);
299
0
        *prevNext = NULL;
300
0
        return URI_FALSE;  // Raises malloc error
301
0
    }
302
303
    /* Fill key */
304
0
    key = memory->malloc(memory, (keyLen + 1) * sizeof(URI_CHAR));
305
0
    if (key == NULL) {
306
0
        memory->free(memory, *prevNext);
307
0
        *prevNext = NULL;
308
0
        return URI_FALSE; /* Raises malloc error */
309
0
    }
310
311
0
    key[keyLen] = _UT('\0');
312
0
    if (keyLen > 0) {
313
        /* Copy 1:1 */
314
0
        memcpy(key, keyFirst, keyLen * sizeof(URI_CHAR));
315
316
        /* Unescape */
317
0
        URI_FUNC(UnescapeInPlaceEx)(key, plusToSpace, breakConversion);
318
0
    }
319
0
    (*prevNext)->key = key;
320
321
    /* Fill value */
322
0
    if (valueFirst != NULL) {
323
        // Detect integer overflow
324
0
        if ((valueLen > SIZE_MAX - 1) || (valueLen + 1 > SIZE_MAX / sizeof(URI_CHAR))) {
325
0
            memory->free(memory, key);
326
0
            memory->free(memory, *prevNext);
327
0
            *prevNext = NULL;
328
0
            return URI_FALSE;  // Raises malloc error
329
0
        }
330
331
0
        value = memory->malloc(memory, (valueLen + 1) * sizeof(URI_CHAR));
332
0
        if (value == NULL) {
333
0
            memory->free(memory, key);
334
0
            memory->free(memory, *prevNext);
335
0
            *prevNext = NULL;
336
0
            return URI_FALSE; /* Raises malloc error */
337
0
        }
338
339
0
        value[valueLen] = _UT('\0');
340
0
        if (valueLen > 0) {
341
            /* Copy 1:1 */
342
0
            memcpy(value, valueFirst, valueLen * sizeof(URI_CHAR));
343
344
            /* Unescape */
345
0
            URI_FUNC(UnescapeInPlaceEx)(value, plusToSpace, breakConversion);
346
0
        }
347
0
        (*prevNext)->value = value;
348
0
    } else {
349
0
        value = NULL;
350
0
    }
351
0
    (*prevNext)->value = value;
352
353
0
    (*itemCount)++;
354
0
    return URI_TRUE;
355
0
}
Unexecuted instantiation: UriQuery.c:uriAppendQueryItemA
Unexecuted instantiation: UriQuery.c:uriAppendQueryItemW
356
357
0
void URI_FUNC(FreeQueryList)(URI_TYPE(QueryList) * queryList) {
358
0
    URI_FUNC(FreeQueryListMm)(queryList, NULL);
359
0
}
Unexecuted instantiation: uriFreeQueryListA
Unexecuted instantiation: uriFreeQueryListW
360
361
int URI_FUNC(FreeQueryListMm)(URI_TYPE(QueryList) * queryList,
362
0
                              UriMemoryManager * memory) {
363
0
    URI_CHECK_MEMORY_MANAGER(memory); /* may return */
364
0
    while (queryList != NULL) {
365
0
        URI_TYPE(QueryList) * nextBackup = queryList->next;
366
0
        memory->free(memory, (URI_CHAR *)queryList->key); /* const cast */
367
0
        memory->free(memory, (URI_CHAR *)queryList->value); /* const cast */
368
0
        memory->free(memory, queryList);
369
0
        queryList = nextBackup;
370
0
    }
371
0
    return URI_SUCCESS;
372
0
}
Unexecuted instantiation: uriFreeQueryListMmA
Unexecuted instantiation: uriFreeQueryListMmW
373
374
int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) * *dest, int * itemCount,
375
0
                                 const URI_CHAR * first, const URI_CHAR * afterLast) {
376
0
    const UriBool plusToSpace = URI_TRUE;
377
0
    const UriBreakConversion breakConversion = URI_BR_DONT_TOUCH;
378
379
0
    return URI_FUNC(DissectQueryMallocEx)(dest, itemCount, first, afterLast, plusToSpace,
380
0
                                          breakConversion);
381
0
}
Unexecuted instantiation: uriDissectQueryMallocA
Unexecuted instantiation: uriDissectQueryMallocW
382
383
int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) * *dest, int * itemCount,
384
                                   const URI_CHAR * first, const URI_CHAR * afterLast,
385
                                   UriBool plusToSpace,
386
0
                                   UriBreakConversion breakConversion) {
387
0
    return URI_FUNC(DissectQueryMallocExMm)(dest, itemCount, first, afterLast,
388
0
                                            plusToSpace, breakConversion, NULL);
389
0
}
Unexecuted instantiation: uriDissectQueryMallocExA
Unexecuted instantiation: uriDissectQueryMallocExW
390
391
int URI_FUNC(DissectQueryMallocExMm)(URI_TYPE(QueryList) * *dest, int * itemCount,
392
                                     const URI_CHAR * first, const URI_CHAR * afterLast,
393
                                     UriBool plusToSpace,
394
                                     UriBreakConversion breakConversion,
395
0
                                     UriMemoryManager * memory) {
396
0
    const URI_CHAR * walk = first;
397
0
    const URI_CHAR * keyFirst = first;
398
0
    const URI_CHAR * keyAfter = NULL;
399
0
    const URI_CHAR * valueFirst = NULL;
400
0
    const URI_CHAR * valueAfter = NULL;
401
0
    URI_TYPE(QueryList) ** prevNext = dest;
402
0
    int nullCounter;
403
0
    int * itemsAppended = (itemCount == NULL) ? &nullCounter : itemCount;
404
405
0
    if ((dest == NULL) || (first == NULL) || (afterLast == NULL)) {
406
0
        return URI_ERROR_NULL;
407
0
    }
408
409
0
    if (first > afterLast) {
410
0
        return URI_ERROR_RANGE_INVALID;
411
0
    }
412
413
0
    URI_CHECK_MEMORY_MANAGER(memory); /* may return */
414
415
0
    *dest = NULL;
416
0
    *itemsAppended = 0;
417
418
    /* Parse query string */
419
0
    for (; walk < afterLast; walk++) {
420
0
        switch (*walk) {
421
0
        case _UT('&'):
422
0
            if (valueFirst != NULL) {
423
0
                valueAfter = walk;
424
0
            } else {
425
0
                keyAfter = walk;
426
0
            }
427
428
0
            if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, keyFirst, keyAfter,
429
0
                                          valueFirst, valueAfter, plusToSpace,
430
0
                                          breakConversion, memory)
431
0
                == URI_FALSE) {
432
                /* Free list we built */
433
0
                *itemsAppended = 0;
434
0
                URI_FUNC(FreeQueryListMm)(*dest, memory);
435
0
                return URI_ERROR_MALLOC;
436
0
            }
437
438
            /* Make future items children of the current */
439
0
            if ((prevNext != NULL) && (*prevNext != NULL)) {
440
0
                prevNext = &((*prevNext)->next);
441
0
            }
442
443
0
            if (walk + 1 < afterLast) {
444
0
                keyFirst = walk + 1;
445
0
            } else {
446
0
                keyFirst = NULL;
447
0
            }
448
0
            keyAfter = NULL;
449
0
            valueFirst = NULL;
450
0
            valueAfter = NULL;
451
0
            break;
452
453
0
        case _UT('='):
454
            /* NOTE: WE treat the first '=' as a separator, */
455
            /*       all following go into the value part   */
456
0
            if (keyAfter == NULL) {
457
0
                keyAfter = walk;
458
0
                if (walk + 1 <= afterLast) {
459
0
                    valueFirst = walk + 1;
460
0
                    valueAfter = walk + 1;
461
0
                }
462
0
            }
463
0
            break;
464
465
0
        default:
466
0
            break;
467
0
        }
468
0
    }
469
470
0
    if (valueFirst != NULL) {
471
        /* Must be key/value pair */
472
0
        valueAfter = walk;
473
0
    } else {
474
        /* Must be key only */
475
0
        keyAfter = walk;
476
0
    }
477
478
0
    if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, keyFirst, keyAfter, valueFirst,
479
0
                                  valueAfter, plusToSpace, breakConversion, memory)
480
0
        == URI_FALSE) {
481
        /* Free list we built */
482
0
        *itemsAppended = 0;
483
0
        URI_FUNC(FreeQueryListMm)(*dest, memory);
484
0
        return URI_ERROR_MALLOC;
485
0
    }
486
487
0
    return URI_SUCCESS;
488
0
}
Unexecuted instantiation: uriDissectQueryMallocExMmA
Unexecuted instantiation: uriDissectQueryMallocExMmW
489
490
#endif