Coverage Report

Created: 2023-06-07 06:05

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