Coverage Report

Created: 2023-06-07 06:14

/src/libxml2/dict.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * dict.c: dictionary of reusable strings, just used to avoid allocation
3
 *         and freeing operations.
4
 *
5
 * Copyright (C) 2003-2012 Daniel Veillard.
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
12
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
14
 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
15
 *
16
 * Author: daniel@veillard.com
17
 */
18
19
#define IN_LIBXML
20
#include "libxml.h"
21
22
#include <limits.h>
23
#include <stdlib.h>
24
#include <time.h>
25
26
#include "private/dict.h"
27
#include "private/threads.h"
28
29
/*
30
 * Following http://www.ocert.org/advisories/ocert-2011-003.html
31
 * it seems that having hash randomization might be a good idea
32
 * when using XML with untrusted data
33
 * Note1: that it works correctly only if compiled with WITH_BIG_KEY
34
 *  which is the default.
35
 * Note2: the fast function used for a small dict won't protect very
36
 *  well but since the attack is based on growing a very big hash
37
 *  list we will use the BigKey algo as soon as the hash size grows
38
 *  over MIN_DICT_SIZE so this actually works
39
 */
40
#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
41
#define DICT_RANDOMIZATION
42
#endif
43
44
#include <string.h>
45
#ifdef HAVE_STDINT_H
46
#include <stdint.h>
47
#else
48
#ifdef HAVE_INTTYPES_H
49
#include <inttypes.h>
50
#elif defined(_WIN32)
51
typedef unsigned __int32 uint32_t;
52
#endif
53
#endif
54
#include <libxml/tree.h>
55
#include <libxml/dict.h>
56
#include <libxml/xmlmemory.h>
57
#include <libxml/xmlerror.h>
58
#include <libxml/globals.h>
59
60
/* #define DEBUG_GROW */
61
/* #define DICT_DEBUG_PATTERNS */
62
63
10.2k
#define MAX_HASH_LEN 3
64
3.43M
#define MIN_DICT_SIZE 128
65
150
#define MAX_DICT_HASH 100000000
66
#define WITH_BIG_KEY
67
68
#ifdef WITH_BIG_KEY
69
#define xmlDictComputeKey(dict, name, len)                              \
70
3.42M
    (((dict)->size == MIN_DICT_SIZE) ?                                  \
71
3.42M
     xmlDictComputeFastKey(name, len, (dict)->seed) :                   \
72
3.42M
     xmlDictComputeBigKey(name, len, (dict)->seed))
73
74
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
75
10.8k
    (((prefix) == NULL) ?                                               \
76
10.8k
      (xmlDictComputeKey(dict, name, len)) :                             \
77
10.8k
      (((dict)->size == MIN_DICT_SIZE) ?                                \
78
10.8k
       xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) :  \
79
10.8k
       xmlDictComputeBigQKey(prefix, plen, name, len, (dict)->seed)))
80
81
#else /* !WITH_BIG_KEY */
82
#define xmlDictComputeKey(dict, name, len)                              \
83
        xmlDictComputeFastKey(name, len, (dict)->seed)
84
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
85
        xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed)
86
#endif /* WITH_BIG_KEY */
87
88
/*
89
 * An entry in the dictionary
90
 */
91
typedef struct _xmlDictEntry xmlDictEntry;
92
typedef xmlDictEntry *xmlDictEntryPtr;
93
struct _xmlDictEntry {
94
    struct _xmlDictEntry *next;
95
    const xmlChar *name;
96
    unsigned int len;
97
    int valid;
98
    unsigned long okey;
99
};
100
101
typedef struct _xmlDictStrings xmlDictStrings;
102
typedef xmlDictStrings *xmlDictStringsPtr;
103
struct _xmlDictStrings {
104
    xmlDictStringsPtr next;
105
    xmlChar *free;
106
    xmlChar *end;
107
    size_t size;
108
    size_t nbStrings;
109
    xmlChar array[1];
110
};
111
/*
112
 * The entire dictionary
113
 */
114
struct _xmlDict {
115
    int ref_counter;
116
117
    struct _xmlDictEntry *dict;
118
    size_t size;
119
    unsigned int nbElems;
120
    xmlDictStringsPtr strings;
121
122
    struct _xmlDict *subdict;
123
    /* used for randomization */
124
    int seed;
125
    /* used to impose a limit on size */
126
    size_t limit;
127
};
128
129
/*
130
 * A mutex for modifying the reference counter for shared
131
 * dictionaries.
132
 */
133
static xmlMutex xmlDictMutex;
134
135
#ifdef DICT_RANDOMIZATION
136
#ifdef HAVE_RAND_R
137
/*
138
 * Internal data for random function, protected by xmlDictMutex
139
 */
140
static unsigned int rand_seed = 0;
141
#endif
142
#endif
143
144
/**
145
 * xmlInitializeDict:
146
 *
147
 * DEPRECATED: Alias for xmlInitParser.
148
 */
149
0
int xmlInitializeDict(void) {
150
0
    xmlInitParser();
151
0
    return(0);
152
0
}
153
154
/**
155
 * __xmlInitializeDict:
156
 *
157
 * This function is not public
158
 * Do the dictionary mutex initialization.
159
 */
160
2
int __xmlInitializeDict(void) {
161
2
    xmlInitMutex(&xmlDictMutex);
162
163
#ifdef DICT_RANDOMIZATION
164
#ifdef HAVE_RAND_R
165
    rand_seed = time(NULL);
166
    rand_r(& rand_seed);
167
#else
168
    srand(time(NULL));
169
#endif
170
#endif
171
2
    return(1);
172
2
}
173
174
#ifdef DICT_RANDOMIZATION
175
int __xmlRandom(void) {
176
    int ret;
177
178
    xmlMutexLock(&xmlDictMutex);
179
#ifdef HAVE_RAND_R
180
    ret = rand_r(& rand_seed);
181
#else
182
    ret = rand();
183
#endif
184
    xmlMutexUnlock(&xmlDictMutex);
185
    return(ret);
186
}
187
#endif
188
189
/**
190
 * xmlDictCleanup:
191
 *
192
 * DEPRECATED: This function is a no-op. Call xmlCleanupParser
193
 * to free global state but see the warnings there. xmlCleanupParser
194
 * should be only called once at program exit. In most cases, you don't
195
 * have call cleanup functions at all.
196
 */
197
void
198
0
xmlDictCleanup(void) {
199
0
}
200
201
/**
202
 * xmlCleanupDictInternal:
203
 *
204
 * Free the dictionary mutex.
205
 */
206
void
207
0
xmlCleanupDictInternal(void) {
208
0
    xmlCleanupMutex(&xmlDictMutex);
209
0
}
210
211
/*
212
 * xmlDictAddString:
213
 * @dict: the dictionary
214
 * @name: the name of the userdata
215
 * @len: the length of the name
216
 *
217
 * Add the string to the array[s]
218
 *
219
 * Returns the pointer of the local string, or NULL in case of error.
220
 */
221
static const xmlChar *
222
8.72k
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
223
8.72k
    xmlDictStringsPtr pool;
224
8.72k
    const xmlChar *ret;
225
8.72k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
226
8.72k
    size_t limit = 0;
227
228
#ifdef DICT_DEBUG_PATTERNS
229
    fprintf(stderr, "-");
230
#endif
231
8.72k
    pool = dict->strings;
232
9.14k
    while (pool != NULL) {
233
8.47k
  if ((size_t)(pool->end - pool->free) > namelen)
234
8.05k
      goto found_pool;
235
417
  if (pool->size > size) size = pool->size;
236
417
        limit += pool->size;
237
417
  pool = pool->next;
238
417
    }
239
    /*
240
     * Not found, need to allocate
241
     */
242
669
    if (pool == NULL) {
243
669
        if ((dict->limit > 0) && (limit > dict->limit)) {
244
0
            return(NULL);
245
0
        }
246
247
669
        if (size == 0) size = 1000;
248
243
  else size *= 4; /* exponential growth */
249
669
        if (size < 4 * namelen)
250
155
      size = 4 * namelen; /* just in case ! */
251
669
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
252
669
  if (pool == NULL)
253
0
      return(NULL);
254
669
  pool->size = size;
255
669
  pool->nbStrings = 0;
256
669
  pool->free = &pool->array[0];
257
669
  pool->end = &pool->array[size];
258
669
  pool->next = dict->strings;
259
669
  dict->strings = pool;
260
#ifdef DICT_DEBUG_PATTERNS
261
        fprintf(stderr, "+");
262
#endif
263
669
    }
264
8.72k
found_pool:
265
8.72k
    ret = pool->free;
266
8.72k
    memcpy(pool->free, name, namelen);
267
8.72k
    pool->free += namelen;
268
8.72k
    *(pool->free++) = 0;
269
8.72k
    pool->nbStrings++;
270
8.72k
    return(ret);
271
669
}
272
273
/*
274
 * xmlDictAddQString:
275
 * @dict: the dictionary
276
 * @prefix: the prefix of the userdata
277
 * @plen: the prefix length
278
 * @name: the name of the userdata
279
 * @len: the length of the name
280
 *
281
 * Add the QName to the array[s]
282
 *
283
 * Returns the pointer of the local string, or NULL in case of error.
284
 */
285
static const xmlChar *
286
xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, unsigned int plen,
287
                 const xmlChar *name, unsigned int namelen)
288
1.33k
{
289
1.33k
    xmlDictStringsPtr pool;
290
1.33k
    const xmlChar *ret;
291
1.33k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
292
1.33k
    size_t limit = 0;
293
294
1.33k
    if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
295
296
#ifdef DICT_DEBUG_PATTERNS
297
    fprintf(stderr, "=");
298
#endif
299
1.33k
    pool = dict->strings;
300
1.39k
    while (pool != NULL) {
301
1.37k
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
302
1.31k
      goto found_pool;
303
65
  if (pool->size > size) size = pool->size;
304
65
        limit += pool->size;
305
65
  pool = pool->next;
306
65
    }
307
    /*
308
     * Not found, need to allocate
309
     */
310
20
    if (pool == NULL) {
311
20
        if ((dict->limit > 0) && (limit > dict->limit)) {
312
0
            return(NULL);
313
0
        }
314
315
20
        if (size == 0) size = 1000;
316
20
  else size *= 4; /* exponential growth */
317
20
        if (size < 4 * (namelen + plen + 1))
318
0
      size = 4 * (namelen + plen + 1); /* just in case ! */
319
20
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
320
20
  if (pool == NULL)
321
0
      return(NULL);
322
20
  pool->size = size;
323
20
  pool->nbStrings = 0;
324
20
  pool->free = &pool->array[0];
325
20
  pool->end = &pool->array[size];
326
20
  pool->next = dict->strings;
327
20
  dict->strings = pool;
328
#ifdef DICT_DEBUG_PATTERNS
329
        fprintf(stderr, "+");
330
#endif
331
20
    }
332
1.33k
found_pool:
333
1.33k
    ret = pool->free;
334
1.33k
    memcpy(pool->free, prefix, plen);
335
1.33k
    pool->free += plen;
336
1.33k
    *(pool->free++) = ':';
337
1.33k
    memcpy(pool->free, name, namelen);
338
1.33k
    pool->free += namelen;
339
1.33k
    *(pool->free++) = 0;
340
1.33k
    pool->nbStrings++;
341
1.33k
    return(ret);
342
20
}
343
344
#ifdef WITH_BIG_KEY
345
/*
346
 * xmlDictComputeBigKey:
347
 *
348
 * Calculate a hash key using a good hash function that works well for
349
 * larger hash table sizes.
350
 *
351
 * Hash function by "One-at-a-Time Hash" see
352
 * http://burtleburtle.net/bob/hash/doobs.html
353
 */
354
355
#ifdef __clang__
356
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
357
ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
358
#endif
359
static uint32_t
360
921k
xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
361
921k
    uint32_t hash;
362
921k
    int i;
363
364
921k
    if (namelen <= 0 || data == NULL) return(0);
365
366
920k
    hash = seed;
367
368
15.3M
    for (i = 0;i < namelen; i++) {
369
14.4M
        hash += data[i];
370
14.4M
  hash += (hash << 10);
371
14.4M
  hash ^= (hash >> 6);
372
14.4M
    }
373
920k
    hash += (hash << 3);
374
920k
    hash ^= (hash >> 11);
375
920k
    hash += (hash << 15);
376
377
920k
    return hash;
378
921k
}
379
380
/*
381
 * xmlDictComputeBigQKey:
382
 *
383
 * Calculate a hash key for two strings using a good hash function
384
 * that works well for larger hash table sizes.
385
 *
386
 * Hash function by "One-at-a-Time Hash" see
387
 * http://burtleburtle.net/bob/hash/doobs.html
388
 *
389
 * Neither of the two strings must be NULL.
390
 */
391
#ifdef __clang__
392
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
393
ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
394
#endif
395
static unsigned long
396
xmlDictComputeBigQKey(const xmlChar *prefix, int plen,
397
                      const xmlChar *name, int len, int seed)
398
6.33k
{
399
6.33k
    uint32_t hash;
400
6.33k
    int i;
401
402
6.33k
    hash = seed;
403
404
47.3k
    for (i = 0;i < plen; i++) {
405
41.0k
        hash += prefix[i];
406
41.0k
  hash += (hash << 10);
407
41.0k
  hash ^= (hash >> 6);
408
41.0k
    }
409
6.33k
    hash += ':';
410
6.33k
    hash += (hash << 10);
411
6.33k
    hash ^= (hash >> 6);
412
413
2.17M
    for (i = 0;i < len; i++) {
414
2.16M
        hash += name[i];
415
2.16M
  hash += (hash << 10);
416
2.16M
  hash ^= (hash >> 6);
417
2.16M
    }
418
6.33k
    hash += (hash << 3);
419
6.33k
    hash ^= (hash >> 11);
420
6.33k
    hash += (hash << 15);
421
422
6.33k
    return hash;
423
6.33k
}
424
#endif /* WITH_BIG_KEY */
425
426
/*
427
 * xmlDictComputeFastKey:
428
 *
429
 * Calculate a hash key using a fast hash function that works well
430
 * for low hash table fill.
431
 */
432
static unsigned long
433
2.50M
xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
434
2.50M
    unsigned long value = seed;
435
436
2.50M
    if ((name == NULL) || (namelen <= 0))
437
187
        return(value);
438
2.50M
    value += *name;
439
2.50M
    value <<= 5;
440
2.50M
    if (namelen > 10) {
441
10.7k
        value += name[namelen - 1];
442
10.7k
        namelen = 10;
443
10.7k
    }
444
2.50M
    switch (namelen) {
445
11.5k
        case 10: value += name[9];
446
        /* Falls through. */
447
12.8k
        case 9: value += name[8];
448
        /* Falls through. */
449
13.6k
        case 8: value += name[7];
450
        /* Falls through. */
451
15.0k
        case 7: value += name[6];
452
        /* Falls through. */
453
21.3k
        case 6: value += name[5];
454
        /* Falls through. */
455
1.83M
        case 5: value += name[4];
456
        /* Falls through. */
457
1.84M
        case 4: value += name[3];
458
        /* Falls through. */
459
1.86M
        case 3: value += name[2];
460
        /* Falls through. */
461
1.95M
        case 2: value += name[1];
462
        /* Falls through. */
463
2.50M
        default: break;
464
2.50M
    }
465
2.50M
    return(value);
466
2.50M
}
467
468
/*
469
 * xmlDictComputeFastQKey:
470
 *
471
 * Calculate a hash key for two strings using a fast hash function
472
 * that works well for low hash table fill.
473
 *
474
 * Neither of the two strings must be NULL.
475
 */
476
static unsigned long
477
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
478
                       const xmlChar *name, int len, int seed)
479
4.49k
{
480
4.49k
    unsigned long value = seed;
481
482
4.49k
    if (plen == 0)
483
0
  value += 30 * ':';
484
4.49k
    else
485
4.49k
  value += 30 * (*prefix);
486
487
4.49k
    if (len > 10) {
488
789
        int offset = len - (plen + 1 + 1);
489
789
  if (offset < 0)
490
1
      offset = len - (10 + 1);
491
789
  value += name[offset];
492
789
        len = 10;
493
789
  if (plen > 10)
494
461
      plen = 10;
495
789
    }
496
4.49k
    switch (plen) {
497
462
        case 10: value += prefix[9];
498
        /* Falls through. */
499
524
        case 9: value += prefix[8];
500
        /* Falls through. */
501
527
        case 8: value += prefix[7];
502
        /* Falls through. */
503
527
        case 7: value += prefix[6];
504
        /* Falls through. */
505
2.24k
        case 6: value += prefix[5];
506
        /* Falls through. */
507
2.24k
        case 5: value += prefix[4];
508
        /* Falls through. */
509
2.34k
        case 4: value += prefix[3];
510
        /* Falls through. */
511
2.50k
        case 3: value += prefix[2];
512
        /* Falls through. */
513
2.59k
        case 2: value += prefix[1];
514
        /* Falls through. */
515
3.95k
        case 1: value += prefix[0];
516
        /* Falls through. */
517
4.49k
        default: break;
518
4.49k
    }
519
4.49k
    len -= plen;
520
4.49k
    if (len > 0) {
521
990
        value += ':';
522
990
  len--;
523
990
    }
524
4.49k
    switch (len) {
525
0
        case 10: value += name[9];
526
        /* Falls through. */
527
0
        case 9: value += name[8];
528
        /* Falls through. */
529
190
        case 8: value += name[7];
530
        /* Falls through. */
531
194
        case 7: value += name[6];
532
        /* Falls through. */
533
316
        case 6: value += name[5];
534
        /* Falls through. */
535
431
        case 5: value += name[4];
536
        /* Falls through. */
537
675
        case 4: value += name[3];
538
        /* Falls through. */
539
692
        case 3: value += name[2];
540
        /* Falls through. */
541
732
        case 2: value += name[1];
542
        /* Falls through. */
543
846
        case 1: value += name[0];
544
        /* Falls through. */
545
4.49k
        default: break;
546
4.49k
    }
547
4.49k
    return(value);
548
4.49k
}
549
550
/**
551
 * xmlDictCreate:
552
 *
553
 * Create a new dictionary
554
 *
555
 * Returns the newly created dictionary, or NULL if an error occurred.
556
 */
557
xmlDictPtr
558
811
xmlDictCreate(void) {
559
811
    xmlDictPtr dict;
560
561
811
    xmlInitParser();
562
563
#ifdef DICT_DEBUG_PATTERNS
564
    fprintf(stderr, "C");
565
#endif
566
567
811
    dict = xmlMalloc(sizeof(xmlDict));
568
811
    if (dict) {
569
811
        dict->ref_counter = 1;
570
811
        dict->limit = 0;
571
572
811
        dict->size = MIN_DICT_SIZE;
573
811
  dict->nbElems = 0;
574
811
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
575
811
  dict->strings = NULL;
576
811
  dict->subdict = NULL;
577
811
        if (dict->dict) {
578
811
      memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
579
#ifdef DICT_RANDOMIZATION
580
            dict->seed = __xmlRandom();
581
#else
582
811
            dict->seed = 0;
583
811
#endif
584
811
      return(dict);
585
811
        }
586
0
        xmlFree(dict);
587
0
    }
588
0
    return(NULL);
589
811
}
590
591
/**
592
 * xmlDictCreateSub:
593
 * @sub: an existing dictionary
594
 *
595
 * Create a new dictionary, inheriting strings from the read-only
596
 * dictionary @sub. On lookup, strings are first searched in the
597
 * new dictionary, then in @sub, and if not found are created in the
598
 * new dictionary.
599
 *
600
 * Returns the newly created dictionary, or NULL if an error occurred.
601
 */
602
xmlDictPtr
603
399
xmlDictCreateSub(xmlDictPtr sub) {
604
399
    xmlDictPtr dict = xmlDictCreate();
605
606
399
    if ((dict != NULL) && (sub != NULL)) {
607
#ifdef DICT_DEBUG_PATTERNS
608
        fprintf(stderr, "R");
609
#endif
610
399
        dict->seed = sub->seed;
611
399
        dict->subdict = sub;
612
399
  xmlDictReference(dict->subdict);
613
399
    }
614
399
    return(dict);
615
399
}
616
617
/**
618
 * xmlDictReference:
619
 * @dict: the dictionary
620
 *
621
 * Increment the reference counter of a dictionary
622
 *
623
 * Returns 0 in case of success and -1 in case of error
624
 */
625
int
626
4.00k
xmlDictReference(xmlDictPtr dict) {
627
4.00k
    if (dict == NULL) return -1;
628
4.00k
    xmlMutexLock(&xmlDictMutex);
629
4.00k
    dict->ref_counter++;
630
4.00k
    xmlMutexUnlock(&xmlDictMutex);
631
4.00k
    return(0);
632
4.00k
}
633
634
/**
635
 * xmlDictGrow:
636
 * @dict: the dictionary
637
 * @size: the new size of the dictionary
638
 *
639
 * resize the dictionary
640
 *
641
 * Returns 0 in case of success, -1 in case of failure
642
 */
643
static int
644
75
xmlDictGrow(xmlDictPtr dict, size_t size) {
645
75
    unsigned long key, okey;
646
75
    size_t oldsize, i;
647
75
    xmlDictEntryPtr iter, next;
648
75
    struct _xmlDictEntry *olddict;
649
#ifdef DEBUG_GROW
650
    unsigned long nbElem = 0;
651
#endif
652
75
    int ret = 0;
653
75
    int keep_keys = 1;
654
655
75
    if (dict == NULL)
656
0
  return(-1);
657
75
    if (size < 8)
658
0
        return(-1);
659
75
    if (size > MAX_DICT_HASH)
660
0
  return(-1);
661
662
#ifdef DICT_DEBUG_PATTERNS
663
    fprintf(stderr, "*");
664
#endif
665
666
75
    oldsize = dict->size;
667
75
    olddict = dict->dict;
668
75
    if (olddict == NULL)
669
0
        return(-1);
670
75
    if (oldsize == MIN_DICT_SIZE)
671
75
        keep_keys = 0;
672
673
75
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
674
75
    if (dict->dict == NULL) {
675
0
  dict->dict = olddict;
676
0
  return(-1);
677
0
    }
678
75
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
679
75
    dict->size = size;
680
681
    /*  If the two loops are merged, there would be situations where
682
  a new entry needs to allocated and data copied into it from
683
  the main dict. It is nicer to run through the array twice, first
684
  copying all the elements in the main array (less probability of
685
  allocate) and then the rest, so we only free in the second loop.
686
    */
687
9.67k
    for (i = 0; i < oldsize; i++) {
688
9.60k
  if (olddict[i].valid == 0)
689
7.49k
      continue;
690
691
2.11k
  if (keep_keys)
692
0
      okey = olddict[i].okey;
693
2.11k
  else
694
2.11k
      okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
695
2.11k
  key = okey % dict->size;
696
697
2.11k
  if (dict->dict[key].valid == 0) {
698
2.03k
      memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
699
2.03k
      dict->dict[key].next = NULL;
700
2.03k
      dict->dict[key].okey = okey;
701
2.03k
  } else {
702
76
      xmlDictEntryPtr entry;
703
704
76
      entry = xmlMalloc(sizeof(xmlDictEntry));
705
76
      if (entry != NULL) {
706
76
    entry->name = olddict[i].name;
707
76
    entry->len = olddict[i].len;
708
76
    entry->okey = okey;
709
76
    entry->next = dict->dict[key].next;
710
76
    entry->valid = 1;
711
76
    dict->dict[key].next = entry;
712
76
      } else {
713
          /*
714
     * we don't have much ways to alert from here
715
     * result is losing an entry and unicity guarantee
716
     */
717
0
          ret = -1;
718
0
      }
719
76
  }
720
#ifdef DEBUG_GROW
721
  nbElem++;
722
#endif
723
2.11k
    }
724
725
9.67k
    for (i = 0; i < oldsize; i++) {
726
9.60k
  iter = olddict[i].next;
727
10.9k
  while (iter) {
728
1.35k
      next = iter->next;
729
730
      /*
731
       * put back the entry in the new dict
732
       */
733
734
1.35k
      if (keep_keys)
735
0
    okey = iter->okey;
736
1.35k
      else
737
1.35k
    okey = xmlDictComputeKey(dict, iter->name, iter->len);
738
1.35k
      key = okey % dict->size;
739
1.35k
      if (dict->dict[key].valid == 0) {
740
1.26k
    memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
741
1.26k
    dict->dict[key].next = NULL;
742
1.26k
    dict->dict[key].valid = 1;
743
1.26k
    dict->dict[key].okey = okey;
744
1.26k
    xmlFree(iter);
745
1.26k
      } else {
746
84
    iter->next = dict->dict[key].next;
747
84
    iter->okey = okey;
748
84
    dict->dict[key].next = iter;
749
84
      }
750
751
#ifdef DEBUG_GROW
752
      nbElem++;
753
#endif
754
755
1.35k
      iter = next;
756
1.35k
  }
757
9.60k
    }
758
759
75
    xmlFree(olddict);
760
761
#ifdef DEBUG_GROW
762
    xmlGenericError(xmlGenericErrorContext,
763
      "xmlDictGrow : from %lu to %lu, %u elems\n", oldsize, size, nbElem);
764
#endif
765
766
75
    return(ret);
767
75
}
768
769
/**
770
 * xmlDictFree:
771
 * @dict: the dictionary
772
 *
773
 * Free the hash @dict and its contents. The userdata is
774
 * deallocated with @f if provided.
775
 */
776
void
777
4.81k
xmlDictFree(xmlDictPtr dict) {
778
4.81k
    size_t i;
779
4.81k
    xmlDictEntryPtr iter;
780
4.81k
    xmlDictEntryPtr next;
781
4.81k
    int inside_dict = 0;
782
4.81k
    xmlDictStringsPtr pool, nextp;
783
784
4.81k
    if (dict == NULL)
785
0
  return;
786
787
    /* decrement the counter, it may be shared by a parser and docs */
788
4.81k
    xmlMutexLock(&xmlDictMutex);
789
4.81k
    dict->ref_counter--;
790
4.81k
    if (dict->ref_counter > 0) {
791
4.00k
        xmlMutexUnlock(&xmlDictMutex);
792
4.00k
        return;
793
4.00k
    }
794
795
809
    xmlMutexUnlock(&xmlDictMutex);
796
797
809
    if (dict->subdict != NULL) {
798
399
        xmlDictFree(dict->subdict);
799
399
    }
800
801
809
    if (dict->dict) {
802
92.2k
  for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
803
91.4k
      iter = &(dict->dict[i]);
804
91.4k
      if (iter->valid == 0)
805
82.8k
    continue;
806
8.68k
      inside_dict = 1;
807
18.7k
      while (iter) {
808
10.0k
    next = iter->next;
809
10.0k
    if (!inside_dict)
810
1.33k
        xmlFree(iter);
811
10.0k
    dict->nbElems--;
812
10.0k
    inside_dict = 0;
813
10.0k
    iter = next;
814
10.0k
      }
815
8.68k
  }
816
809
  xmlFree(dict->dict);
817
809
    }
818
809
    pool = dict->strings;
819
1.49k
    while (pool != NULL) {
820
686
        nextp = pool->next;
821
686
  xmlFree(pool);
822
686
  pool = nextp;
823
686
    }
824
809
    xmlFree(dict);
825
809
}
826
827
/**
828
 * xmlDictLookup:
829
 * @dict: the dictionary
830
 * @name: the name of the userdata
831
 * @len: the length of the name, if -1 it is recomputed
832
 *
833
 * Add the @name to the dictionary @dict if not present.
834
 *
835
 * Returns the internal copy of the name or NULL in case of internal error
836
 */
837
const xmlChar *
838
3.42M
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
839
3.42M
    unsigned long key, okey, nbi = 0;
840
3.42M
    xmlDictEntryPtr entry;
841
3.42M
    xmlDictEntryPtr insert;
842
3.42M
    const xmlChar *ret;
843
3.42M
    unsigned int l;
844
845
3.42M
    if ((dict == NULL) || (name == NULL))
846
826
  return(NULL);
847
848
3.42M
    if (len < 0)
849
1.84M
        l = strlen((const char *) name);
850
1.57M
    else
851
1.57M
        l = len;
852
853
3.42M
    if (((dict->limit > 0) && (l >= dict->limit)) ||
854
3.42M
        (l > INT_MAX / 2))
855
0
        return(NULL);
856
857
    /*
858
     * Check for duplicate and insertion location.
859
     */
860
3.42M
    okey = xmlDictComputeKey(dict, name, l);
861
3.42M
    key = okey % dict->size;
862
3.42M
    if (dict->dict[key].valid == 0) {
863
6.61k
  insert = NULL;
864
3.41M
    } else {
865
4.11M
  for (insert = &(dict->dict[key]); insert->next != NULL;
866
3.41M
       insert = insert->next) {
867
786k
#ifdef __GNUC__
868
786k
      if ((insert->okey == okey) && (insert->len == l)) {
869
86.7k
    if (!memcmp(insert->name, name, l))
870
86.0k
        return(insert->name);
871
86.7k
      }
872
#else
873
      if ((insert->okey == okey) && (insert->len == l) &&
874
          (!xmlStrncmp(insert->name, name, l)))
875
    return(insert->name);
876
#endif
877
700k
      nbi++;
878
700k
  }
879
3.32M
#ifdef __GNUC__
880
3.32M
  if ((insert->okey == okey) && (insert->len == l)) {
881
3.32M
      if (!memcmp(insert->name, name, l))
882
3.32M
    return(insert->name);
883
3.32M
  }
884
#else
885
  if ((insert->okey == okey) && (insert->len == l) &&
886
      (!xmlStrncmp(insert->name, name, l)))
887
      return(insert->name);
888
#endif
889
3.32M
    }
890
891
8.72k
    if (dict->subdict) {
892
25
        unsigned long skey;
893
894
        /* we cannot always reuse the same okey for the subdict */
895
25
        if (((dict->size == MIN_DICT_SIZE) &&
896
25
       (dict->subdict->size != MIN_DICT_SIZE)) ||
897
25
            ((dict->size != MIN_DICT_SIZE) &&
898
25
       (dict->subdict->size == MIN_DICT_SIZE)))
899
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
900
25
  else
901
25
      skey = okey;
902
903
25
  key = skey % dict->subdict->size;
904
25
  if (dict->subdict->dict[key].valid != 0) {
905
2
      xmlDictEntryPtr tmp;
906
907
4
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
908
2
     tmp = tmp->next) {
909
2
#ifdef __GNUC__
910
2
    if ((tmp->okey == skey) && (tmp->len == l)) {
911
0
        if (!memcmp(tmp->name, name, l))
912
0
      return(tmp->name);
913
0
    }
914
#else
915
    if ((tmp->okey == skey) && (tmp->len == l) &&
916
        (!xmlStrncmp(tmp->name, name, l)))
917
        return(tmp->name);
918
#endif
919
2
    nbi++;
920
2
      }
921
2
#ifdef __GNUC__
922
2
      if ((tmp->okey == skey) && (tmp->len == l)) {
923
0
    if (!memcmp(tmp->name, name, l))
924
0
        return(tmp->name);
925
0
      }
926
#else
927
      if ((tmp->okey == skey) && (tmp->len == l) &&
928
    (!xmlStrncmp(tmp->name, name, l)))
929
    return(tmp->name);
930
#endif
931
2
  }
932
25
  key = okey % dict->size;
933
25
    }
934
935
8.72k
    ret = xmlDictAddString(dict, name, l);
936
8.72k
    if (ret == NULL)
937
0
        return(NULL);
938
8.72k
    if (insert == NULL) {
939
6.61k
  entry = &(dict->dict[key]);
940
6.61k
    } else {
941
2.11k
  entry = xmlMalloc(sizeof(xmlDictEntry));
942
2.11k
  if (entry == NULL)
943
0
       return(NULL);
944
2.11k
    }
945
8.72k
    entry->name = ret;
946
8.72k
    entry->len = l;
947
8.72k
    entry->next = NULL;
948
8.72k
    entry->valid = 1;
949
8.72k
    entry->okey = okey;
950
951
952
8.72k
    if (insert != NULL)
953
2.11k
  insert->next = entry;
954
955
8.72k
    dict->nbElems++;
956
957
8.72k
    if ((nbi > MAX_HASH_LEN) &&
958
8.72k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
959
64
  if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
960
0
      return(NULL);
961
64
    }
962
    /* Note that entry may have been freed at this point by xmlDictGrow */
963
964
8.72k
    return(ret);
965
8.72k
}
966
967
/**
968
 * xmlDictExists:
969
 * @dict: the dictionary
970
 * @name: the name of the userdata
971
 * @len: the length of the name, if -1 it is recomputed
972
 *
973
 * Check if the @name exists in the dictionary @dict.
974
 *
975
 * Returns the internal copy of the name or NULL if not found.
976
 */
977
const xmlChar *
978
0
xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
979
0
    unsigned long key, okey;
980
0
    xmlDictEntryPtr insert;
981
0
    unsigned int l;
982
983
0
    if ((dict == NULL) || (name == NULL))
984
0
  return(NULL);
985
986
0
    if (len < 0)
987
0
        l = strlen((const char *) name);
988
0
    else
989
0
        l = len;
990
0
    if (((dict->limit > 0) && (l >= dict->limit)) ||
991
0
        (l > INT_MAX / 2))
992
0
        return(NULL);
993
994
    /*
995
     * Check for duplicate and insertion location.
996
     */
997
0
    okey = xmlDictComputeKey(dict, name, l);
998
0
    key = okey % dict->size;
999
0
    if (dict->dict[key].valid == 0) {
1000
0
  insert = NULL;
1001
0
    } else {
1002
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
1003
0
       insert = insert->next) {
1004
0
#ifdef __GNUC__
1005
0
      if ((insert->okey == okey) && (insert->len == l)) {
1006
0
    if (!memcmp(insert->name, name, l))
1007
0
        return(insert->name);
1008
0
      }
1009
#else
1010
      if ((insert->okey == okey) && (insert->len == l) &&
1011
          (!xmlStrncmp(insert->name, name, l)))
1012
    return(insert->name);
1013
#endif
1014
0
  }
1015
0
#ifdef __GNUC__
1016
0
  if ((insert->okey == okey) && (insert->len == l)) {
1017
0
      if (!memcmp(insert->name, name, l))
1018
0
    return(insert->name);
1019
0
  }
1020
#else
1021
  if ((insert->okey == okey) && (insert->len == l) &&
1022
      (!xmlStrncmp(insert->name, name, l)))
1023
      return(insert->name);
1024
#endif
1025
0
    }
1026
1027
0
    if (dict->subdict) {
1028
0
        unsigned long skey;
1029
1030
        /* we cannot always reuse the same okey for the subdict */
1031
0
        if (((dict->size == MIN_DICT_SIZE) &&
1032
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1033
0
            ((dict->size != MIN_DICT_SIZE) &&
1034
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1035
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
1036
0
  else
1037
0
      skey = okey;
1038
1039
0
  key = skey % dict->subdict->size;
1040
0
  if (dict->subdict->dict[key].valid != 0) {
1041
0
      xmlDictEntryPtr tmp;
1042
1043
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1044
0
     tmp = tmp->next) {
1045
0
#ifdef __GNUC__
1046
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
1047
0
        if (!memcmp(tmp->name, name, l))
1048
0
      return(tmp->name);
1049
0
    }
1050
#else
1051
    if ((tmp->okey == skey) && (tmp->len == l) &&
1052
        (!xmlStrncmp(tmp->name, name, l)))
1053
        return(tmp->name);
1054
#endif
1055
0
      }
1056
0
#ifdef __GNUC__
1057
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
1058
0
    if (!memcmp(tmp->name, name, l))
1059
0
        return(tmp->name);
1060
0
      }
1061
#else
1062
      if ((tmp->okey == skey) && (tmp->len == l) &&
1063
    (!xmlStrncmp(tmp->name, name, l)))
1064
    return(tmp->name);
1065
#endif
1066
0
  }
1067
0
    }
1068
1069
    /* not found */
1070
0
    return(NULL);
1071
0
}
1072
1073
/**
1074
 * xmlDictQLookup:
1075
 * @dict: the dictionary
1076
 * @prefix: the prefix
1077
 * @name: the name
1078
 *
1079
 * Add the QName @prefix:@name to the hash @dict if not present.
1080
 *
1081
 * Returns the internal copy of the QName or NULL in case of internal error
1082
 */
1083
const xmlChar *
1084
10.8k
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
1085
10.8k
    unsigned long okey, key, nbi = 0;
1086
10.8k
    xmlDictEntryPtr entry;
1087
10.8k
    xmlDictEntryPtr insert;
1088
10.8k
    const xmlChar *ret;
1089
10.8k
    unsigned int len, plen, l;
1090
1091
10.8k
    if ((dict == NULL) || (name == NULL))
1092
0
  return(NULL);
1093
10.8k
    if (prefix == NULL)
1094
0
        return(xmlDictLookup(dict, name, -1));
1095
1096
10.8k
    l = len = strlen((const char *) name);
1097
10.8k
    plen = strlen((const char *) prefix);
1098
10.8k
    len += 1 + plen;
1099
1100
    /*
1101
     * Check for duplicate and insertion location.
1102
     */
1103
10.8k
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
1104
10.8k
    key = okey % dict->size;
1105
10.8k
    if (dict->dict[key].valid == 0) {
1106
917
  insert = NULL;
1107
9.91k
    } else {
1108
12.9k
  for (insert = &(dict->dict[key]); insert->next != NULL;
1109
9.91k
       insert = insert->next) {
1110
5.02k
      if ((insert->okey == okey) && (insert->len == len) &&
1111
5.02k
          (xmlStrQEqual(prefix, name, insert->name)))
1112
1.94k
    return(insert->name);
1113
3.08k
      nbi++;
1114
3.08k
  }
1115
7.97k
  if ((insert->okey == okey) && (insert->len == len) &&
1116
7.97k
      (xmlStrQEqual(prefix, name, insert->name)))
1117
7.55k
      return(insert->name);
1118
7.97k
    }
1119
1120
1.33k
    if (dict->subdict) {
1121
0
        unsigned long skey;
1122
1123
        /* we cannot always reuse the same okey for the subdict */
1124
0
        if (((dict->size == MIN_DICT_SIZE) &&
1125
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1126
0
            ((dict->size != MIN_DICT_SIZE) &&
1127
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1128
0
      skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
1129
0
  else
1130
0
      skey = okey;
1131
1132
0
  key = skey % dict->subdict->size;
1133
0
  if (dict->subdict->dict[key].valid != 0) {
1134
0
      xmlDictEntryPtr tmp;
1135
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1136
0
     tmp = tmp->next) {
1137
0
    if ((tmp->okey == skey) && (tmp->len == len) &&
1138
0
        (xmlStrQEqual(prefix, name, tmp->name)))
1139
0
        return(tmp->name);
1140
0
    nbi++;
1141
0
      }
1142
0
      if ((tmp->okey == skey) && (tmp->len == len) &&
1143
0
    (xmlStrQEqual(prefix, name, tmp->name)))
1144
0
    return(tmp->name);
1145
0
  }
1146
0
  key = okey % dict->size;
1147
0
    }
1148
1149
1.33k
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1150
1.33k
    if (ret == NULL)
1151
0
        return(NULL);
1152
1.33k
    if (insert == NULL) {
1153
917
  entry = &(dict->dict[key]);
1154
917
    } else {
1155
413
  entry = xmlMalloc(sizeof(xmlDictEntry));
1156
413
  if (entry == NULL)
1157
0
       return(NULL);
1158
413
    }
1159
1.33k
    entry->name = ret;
1160
1.33k
    entry->len = len;
1161
1.33k
    entry->next = NULL;
1162
1.33k
    entry->valid = 1;
1163
1.33k
    entry->okey = okey;
1164
1165
1.33k
    if (insert != NULL)
1166
413
  insert->next = entry;
1167
1168
1.33k
    dict->nbElems++;
1169
1170
1.33k
    if ((nbi > MAX_HASH_LEN) &&
1171
1.33k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1172
11
  xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1173
    /* Note that entry may have been freed at this point by xmlDictGrow */
1174
1175
1.33k
    return(ret);
1176
1.33k
}
1177
1178
/**
1179
 * xmlDictOwns:
1180
 * @dict: the dictionary
1181
 * @str: the string
1182
 *
1183
 * check if a string is owned by the dictionary
1184
 *
1185
 * Returns 1 if true, 0 if false and -1 in case of error
1186
 * -1 in case of error
1187
 */
1188
int
1189
7.06M
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1190
7.06M
    xmlDictStringsPtr pool;
1191
1192
7.06M
    if ((dict == NULL) || (str == NULL))
1193
0
  return(-1);
1194
7.06M
    pool = dict->strings;
1195
10.1M
    while (pool != NULL) {
1196
6.44M
        if ((str >= &pool->array[0]) && (str <= pool->free))
1197
3.33M
      return(1);
1198
3.11M
  pool = pool->next;
1199
3.11M
    }
1200
3.73M
    if (dict->subdict)
1201
1.81M
        return(xmlDictOwns(dict->subdict, str));
1202
1.91M
    return(0);
1203
3.73M
}
1204
1205
/**
1206
 * xmlDictSize:
1207
 * @dict: the dictionary
1208
 *
1209
 * Query the number of elements installed in the hash @dict.
1210
 *
1211
 * Returns the number of elements in the dictionary or
1212
 * -1 in case of error
1213
 */
1214
int
1215
0
xmlDictSize(xmlDictPtr dict) {
1216
0
    if (dict == NULL)
1217
0
  return(-1);
1218
0
    if (dict->subdict)
1219
0
        return(dict->nbElems + dict->subdict->nbElems);
1220
0
    return(dict->nbElems);
1221
0
}
1222
1223
/**
1224
 * xmlDictSetLimit:
1225
 * @dict: the dictionary
1226
 * @limit: the limit in bytes
1227
 *
1228
 * Set a size limit for the dictionary
1229
 * Added in 2.9.0
1230
 *
1231
 * Returns the previous limit of the dictionary or 0
1232
 */
1233
size_t
1234
410
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
1235
410
    size_t ret;
1236
1237
410
    if (dict == NULL)
1238
0
  return(0);
1239
410
    ret = dict->limit;
1240
410
    dict->limit = limit;
1241
410
    return(ret);
1242
410
}
1243
1244
/**
1245
 * xmlDictGetUsage:
1246
 * @dict: the dictionary
1247
 *
1248
 * Get how much memory is used by a dictionary for strings
1249
 * Added in 2.9.0
1250
 *
1251
 * Returns the amount of strings allocated
1252
 */
1253
size_t
1254
0
xmlDictGetUsage(xmlDictPtr dict) {
1255
0
    xmlDictStringsPtr pool;
1256
0
    size_t limit = 0;
1257
1258
0
    if (dict == NULL)
1259
0
  return(0);
1260
0
    pool = dict->strings;
1261
0
    while (pool != NULL) {
1262
0
        limit += pool->size;
1263
0
  pool = pool->next;
1264
0
    }
1265
0
    return(limit);
1266
0
}
1267