• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

arangodb / velocypack / 3998645281

pending completion
3998645281

Pull #148

github

GitHub
Merge b1e3c924b into 5a28b6413
Pull Request #148: use separate namespace for xxh functions

0 of 5107 relevant lines covered (0.0%)

0.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Validator.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
/// DISCLAIMER
3
///
4
/// Copyright 2014-2020 ArangoDB GmbH, Cologne, Germany
5
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
6
///
7
/// Licensed under the Apache License, Version 2.0 (the "License");
8
/// you may not use this file except in compliance with the License.
9
/// You may obtain a copy of the License at
10
///
11
///     http://www.apache.org/licenses/LICENSE-2.0
12
///
13
/// Unless required by applicable law or agreed to in writing, software
14
/// distributed under the License is distributed on an "AS IS" BASIS,
15
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
/// See the License for the specific language governing permissions and
17
/// limitations under the License.
18
///
19
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
20
///
21
/// @author Max Neunhoeffer
22
/// @author Jan Steemann
23
////////////////////////////////////////////////////////////////////////////////
24

25
#include <unordered_set>
26
#include <memory>
27

28
#include "velocypack/velocypack-common.h"
29
#include "velocypack/Validator.h"
30
#include "velocypack/Exception.h"
31
#include "velocypack/Slice.h"
32
#include "velocypack/ValueType.h"
33

34
#include "asm-functions.h"
35

36
using namespace arangodb::velocypack;
37

38
template<bool reverse>
39
static ValueLength ReadVariableLengthValue(uint8_t const*& p,
×
40
                                           uint8_t const* end) {
41
  ValueLength value = 0;
×
42
  ValueLength shifter = 0;
×
43
  while (true) {
×
44
    uint8_t c = *p;
×
45
    VELOCYPACK_ASSERT(shifter <= 8 * 7);
×
46
    value += static_cast<ValueLength>(c & 0x7fU) << shifter;
×
47
    shifter += 7;
×
48
    if (reverse) {
49
      --p;
×
50
    } else {
51
      ++p;
×
52
    }
53
    if (!(c & 0x80U)) {
×
54
      break;
×
55
    }
56
    if (VELOCYPACK_UNLIKELY(p == end || shifter > 7 * 8)) {
×
57
      throw Exception(Exception::ValidatorInvalidLength,
×
58
                      "Compound value length value is out of bounds");
59
    }
60
  }
61
  return value;
×
62
}
63

64
Validator::Validator(Options const* options) : options(options), _nesting(0) {
×
65
  if (options == nullptr) {
×
66
    throw Exception(Exception::InternalError, "Options cannot be a nullptr");
×
67
  }
68
}
×
69

70
bool Validator::validate(uint8_t const* ptr, std::size_t length,
×
71
                         bool isSubPart) {
72
  // reset internal state
73
  _nesting = 0;
×
74
  validatePart(ptr, length, isSubPart);
×
75
  return true;
×
76
}
77

78
void Validator::validatePart(uint8_t const* ptr, std::size_t length,
×
79
                             bool isSubPart) {
80
  if (length == 0) {
×
81
    throw Exception(Exception::ValidatorInvalidLength,
×
82
                    "length 0 is invalid for any VelocyPack value");
×
83
  }
84

85
  uint8_t head = *ptr;
×
86

87
  // type() only reads the first byte, which is safe
88
  ValueType const type = Slice(ptr).type();
×
89

90
  if (type == ValueType::None && head != 0x00U) {
×
91
    // invalid type
92
    throw Exception(Exception::ValidatorInvalidType);
×
93
  }
94

95
  // special handling for certain types...
96
  switch (type) {
×
97
    case ValueType::None:
×
98
    case ValueType::Null:
99
    case ValueType::Bool:
100
    case ValueType::MinKey:
101
    case ValueType::MaxKey:
102
    case ValueType::SmallInt:
103
    case ValueType::Int:
104
    case ValueType::UInt:
105
    case ValueType::Double:
106
    case ValueType::UTCDate:
107
    case ValueType::Binary:
108
    case ValueType::Illegal: {
109
      break;
×
110
    }
111

112
    case ValueType::String: {
×
113
      uint8_t const* p;
114
      ValueLength len;
115
      if (head == 0xbfU) {
×
116
        // long UTF-8 string. must be at least 9 bytes long so we
117
        // can read the entire string length safely
118
        validateBufferLength(1 + 8, length, true);
×
119
        len = readIntegerFixed<ValueLength, 8>(ptr + 1);
×
120
        p = ptr + 1 + 8;
×
121
        validateBufferLength(len + 1 + 8, length, true);
×
122
      } else {
123
        len = head - 0x40U;
×
124
        p = ptr + 1;
×
125
        validateBufferLength(len + 1, length, true);
×
126
      }
127

128
      if (options->validateUtf8Strings &&
×
129
          !ValidateUtf8String(p, static_cast<std::size_t>(len))) {
×
130
        throw Exception(Exception::InvalidUtf8Sequence);
×
131
      }
132
      break;
×
133
    }
134

135
    case ValueType::Array: {
×
136
      if (++_nesting >= options->nestingLimit) {
×
137
        throw Exception(Exception::TooDeepNesting);
×
138
      }
139
      validateArray(ptr, length);
×
140
      --_nesting;
×
141
      break;
×
142
    }
143

144
    case ValueType::Object: {
×
145
      if (++_nesting >= options->nestingLimit) {
×
146
        throw Exception(Exception::TooDeepNesting);
×
147
      }
148
      validateObject(ptr, length);
×
149
      --_nesting;
×
150
      break;
×
151
    }
152

153
    case ValueType::BCD: {
×
154
      if (options->disallowBCD) {
×
155
        throw Exception(Exception::BuilderBCDDisallowed);
×
156
      }
157
      throw Exception(Exception::NotImplemented);
×
158
    }
159

160
    case ValueType::Tagged: {
×
161
      if (options->disallowTags) {
×
162
        throw Exception(Exception::BuilderTagsDisallowed);
×
163
      }
164
      validateTagged(ptr, length);
×
165
      break;
×
166
    }
167

168
    case ValueType::External: {
×
169
      // check if Externals are forbidden
170
      if (options->disallowExternals) {
×
171
        throw Exception(Exception::BuilderExternalsDisallowed);
×
172
      }
173
      // validate if Slice length exceeds the given buffer
174
      validateBufferLength(1 + sizeof(void*), length, true);
×
175
      // do not perform pointer validation
176
      break;
×
177
    }
178

179
    case ValueType::Custom: {
×
180
      if (options->disallowCustom) {
×
181
        throw Exception(Exception::BuilderCustomDisallowed);
×
182
      }
183
      ValueLength byteSize = 0;
×
184

185
      if (head == 0xf0U) {
×
186
        byteSize = 1 + 1;
×
187
      } else if (head == 0xf1U) {
×
188
        byteSize = 1 + 2;
×
189
      } else if (head == 0xf2U) {
×
190
        byteSize = 1 + 4;
×
191
      } else if (head == 0xf3U) {
×
192
        byteSize = 1 + 8;
×
193
      } else if (head >= 0xf4U && head <= 0xf6U) {
×
194
        validateBufferLength(1 + 1, length, true);
×
195
        byteSize = 1 + 1 + readIntegerNonEmpty<ValueLength>(ptr + 1, 1);
×
196
        if (byteSize == 1 + 1) {
×
197
          throw Exception(Exception::ValidatorInvalidLength,
×
198
                          "Invalid size for Custom type");
×
199
        }
200
      } else if (head >= 0xf7U && head <= 0xf9U) {
×
201
        validateBufferLength(1 + 2, length, true);
×
202
        byteSize = 1 + 2 + readIntegerNonEmpty<ValueLength>(ptr + 1, 2);
×
203
        if (byteSize == 1 + 2) {
×
204
          throw Exception(Exception::ValidatorInvalidLength,
×
205
                          "Invalid size for Custom type");
×
206
        }
207
      } else if (head >= 0xfaU && head <= 0xfcU) {
×
208
        validateBufferLength(1 + 4, length, true);
×
209
        byteSize = 1 + 4 + readIntegerNonEmpty<ValueLength>(ptr + 1, 4);
×
210
        if (byteSize == 1 + 4) {
×
211
          throw Exception(Exception::ValidatorInvalidLength,
×
212
                          "Invalid size for Custom type");
×
213
        }
214
      } else if (head >= 0xfdU) {
×
215
        validateBufferLength(1 + 8, length, true);
×
216
        byteSize = 1 + 8 + readIntegerNonEmpty<ValueLength>(ptr + 1, 8);
×
217
        if (byteSize == 1 + 8) {
×
218
          throw Exception(Exception::ValidatorInvalidLength,
×
219
                          "Invalid size for Custom type");
×
220
        }
221
      }
222

223
      validateSliceLength(ptr, byteSize, isSubPart);
×
224
      break;
×
225
    }
226
  }
227

228
  // common validation that must happen for all types
229
  validateSliceLength(ptr, length, isSubPart);
×
230
}
×
231

232
void Validator::validateTagged(uint8_t const* ptr, std::size_t length) {
×
233
  uint8_t head = *ptr;
×
234

235
  do {
×
236
    // looping here to skip over nested tags.
237
    if (head == 0xee) {
×
238
      // 1 byte tag type
239
      // the actual Slice (without tag) must be at least one byte long
240
      validateBufferLength(1 + 1 + 1, length, true);
×
241
      VELOCYPACK_ASSERT(length > 2);
×
242
      ptr += 2;
×
243
      length -= 2;
×
244
    } else if (head == 0xef) {
×
245
      // 8 bytes tag type
246
      // the actual Slice (without tag) must be at least one byte long
247
      validateBufferLength(1 + 8 + 1, length, true);
×
248
      VELOCYPACK_ASSERT(length > 9);
×
249
      ptr += 9;
×
250
      length -= 9;
×
251
    } else {
252
      throw Exception(Exception::NotImplemented);
×
253
    }
254
    VELOCYPACK_ASSERT(length > 0);
×
255
    head = *ptr;
×
256
  } while (head == 0xee || head == 0xef);
×
257

258
  validatePart(ptr, length, true);
×
259
}
×
260

261
void Validator::validateArray(uint8_t const* ptr, std::size_t length) {
×
262
  uint8_t head = *ptr;
×
263

264
  if (head == 0x13U) {
×
265
    // compact array
266
    validateCompactArray(ptr, length);
×
267
  } else if (head >= 0x02U && head <= 0x05U) {
×
268
    // array without index table
269
    validateUnindexedArray(ptr, length);
×
270
  } else if (head >= 0x06U && head <= 0x09U) {
×
271
    // array with index table
272
    validateIndexedArray(ptr, length);
×
273
  } else if (head == 0x01U) {
274
    // empty array. always valid
275
  }
276
}
×
277

278
void Validator::validateCompactArray(uint8_t const* ptr, std::size_t length) {
×
279
  // compact Array without index table
280
  validateBufferLength(4, length, true);
×
281

282
  uint8_t const* p = ptr + 1;
×
283
  // read byteLength
284
  ValueLength const byteSize = ReadVariableLengthValue<false>(p, p + length);
×
285
  if (byteSize > length || byteSize < 4) {
×
286
    throw Exception(Exception::ValidatorInvalidLength,
×
287
                    "Array length value is out of bounds");
×
288
  }
289

290
  // read nrItems
291
  uint8_t const* data = p;
×
292
  p = ptr + byteSize - 1;
×
293
  ValueLength nrItems = ReadVariableLengthValue<true>(p, ptr + byteSize);
×
294
  if (nrItems == 0) {
×
295
    throw Exception(Exception::ValidatorInvalidLength,
×
296
                    "Array length value is out of bounds");
×
297
  }
298
  ++p;
×
299

300
  // validate the array members
301
  uint8_t const* e = p;
×
302
  p = data;
×
303
  while (nrItems-- > 0) {
×
304
    if (p >= e) {
×
305
      throw Exception(Exception::ValidatorInvalidLength,
×
306
                      "Array items number is out of bounds");
×
307
    }
308
    validatePart(p, e - p, true);
×
309
    p += Slice(p).byteSize();
×
310
  }
311
}
×
312

313
void Validator::validateUnindexedArray(uint8_t const* ptr, std::size_t length) {
×
314
  // Array without index table, with 1-8 bytes lengths, all values with same
315
  // length
316
  uint8_t head = *ptr;
×
317
  ValueLength const byteSizeLength =
×
318
      1ULL << (static_cast<ValueLength>(head) - 0x02U);
×
319
  validateBufferLength(1 + byteSizeLength + 1, length, true);
×
320
  ValueLength const byteSize =
321
      readIntegerNonEmpty<ValueLength>(ptr + 1, byteSizeLength);
×
322

323
  if (byteSize > length) {
×
324
    throw Exception(Exception::ValidatorInvalidLength,
×
325
                    "Array length is out of bounds");
×
326
  }
327

328
  // look up first member
329
  uint8_t const* p = ptr + 1 + byteSizeLength;
×
330
  uint8_t const* e = p + (8 - byteSizeLength);
×
331

332
  if (e > ptr + byteSize) {
×
333
    e = ptr + byteSize;
×
334
  }
335
  while (p < e && *p == '\x00') {
×
336
    ++p;
×
337
  }
338

339
  if (p >= ptr + byteSize) {
×
340
    throw Exception(Exception::ValidatorInvalidLength,
×
341
                    "Array structure is invalid");
×
342
  }
343

344
  // check if padding is correct
345
  if (p != ptr + 1 + byteSizeLength &&
×
346
      p != ptr + 1 + byteSizeLength + (8 - byteSizeLength)) {
×
347
    throw Exception(Exception::ValidatorInvalidLength,
×
348
                    "Array padding is invalid");
×
349
  }
350

351
  validatePart(p, length - (p - ptr), true);
×
352
  ValueLength itemSize = Slice(p).byteSize();
×
353
  if (itemSize == 0) {
×
354
    throw Exception(Exception::ValidatorInvalidLength,
×
355
                    "Array itemSize value is invalid");
×
356
  }
357
  ValueLength nrItems = (byteSize - (p - ptr)) / itemSize;
×
358

359
  if (nrItems == 0) {
×
360
    throw Exception(Exception::ValidatorInvalidLength,
×
361
                    "Array nrItems value is invalid");
×
362
  }
363
  // we already validated p, so move it forward
364
  p += itemSize;
×
365
  e = ptr + length;
×
366
  --nrItems;
×
367

368
  while (nrItems > 0) {
×
369
    if (p >= e) {
×
370
      throw Exception(Exception::ValidatorInvalidLength,
×
371
                      "Array value is out of bounds");
×
372
    }
373
    // validate sub value
374
    validatePart(p, e - p, true);
×
375
    if (Slice(p).byteSize() != itemSize) {
×
376
      // got a sub-object with a different size. this is not allowed
377
      throw Exception(Exception::ValidatorInvalidLength,
×
378
                      "Unexpected Array value length");
×
379
    }
380
    p += itemSize;
×
381
    --nrItems;
×
382
  }
383
}
×
384

385
void Validator::validateIndexedArray(uint8_t const* ptr, std::size_t length) {
×
386
  // Array with index table, with 1-8 bytes lengths
387
  uint8_t head = *ptr;
×
388
  ValueLength const byteSizeLength =
×
389
      1ULL << (static_cast<ValueLength>(head) - 0x06U);
×
390
  validateBufferLength(1 + byteSizeLength + byteSizeLength + 1, length, true);
×
391
  ValueLength byteSize =
392
      readIntegerNonEmpty<ValueLength>(ptr + 1, byteSizeLength);
×
393

394
  if (byteSize > length) {
×
395
    throw Exception(Exception::ValidatorInvalidLength,
×
396
                    "Array length is out of bounds");
×
397
  }
398

399
  ValueLength nrItems;
400
  uint8_t const* indexTable;
401
  uint8_t const* firstMember;
402

403
  if (head == 0x09U) {
×
404
    // byte length = 8
405
    nrItems = readIntegerNonEmpty<ValueLength>(ptr + byteSize - byteSizeLength,
×
406
                                               byteSizeLength);
407

408
    if (nrItems == 0) {
×
409
      throw Exception(Exception::ValidatorInvalidLength,
×
410
                      "Array nrItems value is invalid");
×
411
    }
412

413
    indexTable = ptr + byteSize - byteSizeLength - (nrItems * byteSizeLength);
×
414
    if (indexTable < ptr + byteSizeLength ||
×
415
        indexTable > ptr + length - byteSizeLength - byteSizeLength) {
×
416
      throw Exception(Exception::ValidatorInvalidLength,
×
417
                      "Array index table is out of bounds");
×
418
    }
419

420
    firstMember = ptr + 1 + byteSizeLength;
×
421
  } else {
422
    // byte length = 1, 2 or 4
423
    nrItems = readIntegerNonEmpty<ValueLength>(ptr + 1 + byteSizeLength,
×
424
                                               byteSizeLength);
425

426
    if (nrItems == 0) {
×
427
      throw Exception(Exception::ValidatorInvalidLength,
×
428
                      "Array nrItems value is invalid");
×
429
    }
430

431
    // look up first member
432
    uint8_t const* p = ptr + 1 + byteSizeLength + byteSizeLength;
×
433
    uint8_t const* e = p + (8 - byteSizeLength - byteSizeLength);
×
434
    if (e > ptr + byteSize) {
×
435
      e = ptr + byteSize;
×
436
    }
437
    while (p < e && *p == '\x00') {
×
438
      ++p;
×
439
    }
440

441
    // check if padding is correct
442
    if (p != ptr + 1 + byteSizeLength + byteSizeLength &&
×
443
        p != ptr + 1 + byteSizeLength + byteSizeLength +
×
444
                 (8 - byteSizeLength - byteSizeLength)) {
445
      throw Exception(Exception::ValidatorInvalidLength,
×
446
                      "Array padding is invalid");
×
447
    }
448

449
    indexTable = ptr + byteSize - (nrItems * byteSizeLength);
×
450
    if (indexTable < ptr + byteSizeLength + byteSizeLength || indexTable < p ||
×
451
        indexTable > ptr + length - byteSizeLength) {
×
452
      throw Exception(Exception::ValidatorInvalidLength,
×
453
                      "Array index table is out of bounds");
×
454
    }
455

456
    firstMember = p;
×
457
  }
458

459
  VELOCYPACK_ASSERT(nrItems > 0);
×
460

461
  ValueLength actualNrItems = 0;
×
462
  uint8_t const* member = firstMember;
×
463
  while (member < indexTable) {
×
464
    validatePart(member, indexTable - member, true);
×
465
    ValueLength offset = readIntegerNonEmpty<ValueLength>(
×
466
        indexTable + actualNrItems * byteSizeLength, byteSizeLength);
×
467
    if (offset != static_cast<ValueLength>(member - ptr)) {
×
468
      throw Exception(Exception::ValidatorInvalidLength,
×
469
                      "Array index table is wrong");
×
470
    }
471

472
    member += Slice(member).byteSize();
×
473
    ++actualNrItems;
×
474
  }
475

476
  if (actualNrItems != nrItems) {
×
477
    throw Exception(Exception::ValidatorInvalidLength,
×
478
                    "Array has more items than in index");
×
479
  }
480
}
×
481

482
void Validator::validateObject(uint8_t const* ptr, std::size_t length) {
×
483
  uint8_t head = *ptr;
×
484

485
  if (head == 0x14U) {
×
486
    // compact object
487
    validateCompactObject(ptr, length);
×
488
  } else if (head >= 0x0bU && head <= 0x12U) {
×
489
    // regular object
490
    validateIndexedObject(ptr, length);
×
491
  } else if (head == 0x0aU) {
492
    // empty object. always valid
493
  }
494
}
×
495

496
void Validator::validateCompactObject(uint8_t const* ptr, std::size_t length) {
×
497
  // compact Object without index table
498
  validateBufferLength(5, length, true);
×
499

500
  uint8_t const* p = ptr + 1;
×
501
  // read byteLength
502
  ValueLength const byteSize = ReadVariableLengthValue<false>(p, p + length);
×
503
  if (byteSize > length || byteSize < 5) {
×
504
    throw Exception(Exception::ValidatorInvalidLength,
×
505
                    "Object length value is out of bounds");
×
506
  }
507

508
  // read nrItems
509
  uint8_t const* data = p;
×
510
  p = ptr + byteSize - 1;
×
511
  ValueLength nrItems = ReadVariableLengthValue<true>(p, ptr + byteSize);
×
512
  if (nrItems == 0) {
×
513
    throw Exception(Exception::ValidatorInvalidLength,
×
514
                    "Object length value is out of bounds");
×
515
  }
516
  ++p;
×
517

518
  // validate the object members
519
  uint8_t const* e = p;
×
520
  p = data;
×
521
  while (nrItems-- > 0) {
×
522
    if (p >= e) {
×
523
      throw Exception(Exception::ValidatorInvalidLength,
×
524
                      "Object items number is out of bounds");
×
525
    }
526
    // validate key
527
    validatePart(p, e - p, true);
×
528
    Slice key(p);
×
529
    bool isString = key.isString();
×
530
    if (!isString) {
×
531
      bool const isSmallInt = key.isSmallInt();
×
532
      if ((!isSmallInt && !key.isUInt()) ||
×
533
          (isSmallInt && key.getSmallInt() <= 0)) {
×
534
        throw Exception(Exception::ValidatorInvalidLength,
×
535
                        "Invalid object key type");
×
536
      }
537
    }
538
    ValueLength keySize = key.byteSize();
×
539
    // validate key
540
    if (isString && options->validateUtf8Strings) {
×
541
      validatePart(p, keySize, true);
×
542
    }
543

544
    // validate value
545
    p += keySize;
×
546
    validatePart(p, e - p, true);
×
547
    p += Slice(p).byteSize();
×
548
  }
549

550
  // finally check if we are now pointing at the end or not
551
  if (p != e) {
×
552
    throw Exception(Exception::ValidatorInvalidLength,
×
553
                    "Object has more members than specified");
×
554
  }
555
}
×
556

557
void Validator::validateIndexedObject(uint8_t const* ptr, std::size_t length) {
×
558
  // Object with index table, with 1-8 bytes lengths
559
  uint8_t head = *ptr;
×
560
  ValueLength byteSizeLength;
561
  if (VELOCYPACK_UNLIKELY(head >= 0x0f)) {
×
562
    byteSizeLength = 1ULL << (static_cast<ValueLength>(head) - 0x0fU);
×
563
  } else {
564
    byteSizeLength = 1ULL << (static_cast<ValueLength>(head) - 0x0bU);
×
565
  }
566
  VELOCYPACK_ASSERT(byteSizeLength > 0);
×
567
  validateBufferLength(1 + byteSizeLength + byteSizeLength + 1, length, true);
×
568
  ValueLength const byteSize =
569
      readIntegerNonEmpty<ValueLength>(ptr + 1, byteSizeLength);
×
570

571
  if (byteSize > length) {
×
572
    throw Exception(Exception::ValidatorInvalidLength,
×
573
                    "Object length is out of bounds");
×
574
  }
575

576
  ValueLength nrItems;
577
  uint8_t const* indexTable;
578
  uint8_t const* firstMember;
579

580
  if (head == 0x0eU || head == 0x12U) {
×
581
    // byte length = 8
582
    nrItems = readIntegerNonEmpty<ValueLength>(ptr + byteSize - byteSizeLength,
×
583
                                               byteSizeLength);
584

585
    if (nrItems == 0) {
×
586
      throw Exception(Exception::ValidatorInvalidLength,
×
587
                      "Object nrItems value is invalid");
×
588
    }
589

590
    indexTable = ptr + byteSize - byteSizeLength - (nrItems * byteSizeLength);
×
591
    if (indexTable < ptr + byteSizeLength ||
×
592
        indexTable > ptr + length - byteSizeLength - byteSizeLength) {
×
593
      throw Exception(Exception::ValidatorInvalidLength,
×
594
                      "Object index table is out of bounds");
×
595
    }
596

597
    firstMember = ptr + byteSize;
×
598
  } else {
599
    // byte length = 1, 2 or 4
600
    nrItems = readIntegerNonEmpty<ValueLength>(ptr + 1 + byteSizeLength,
×
601
                                               byteSizeLength);
602

603
    if (nrItems == 0) {
×
604
      throw Exception(Exception::ValidatorInvalidLength,
×
605
                      "Object nrItems value is invalid");
×
606
    }
607

608
    // look up first member
609
    uint8_t const* p = ptr + 1 + byteSizeLength + byteSizeLength;
×
610
    uint8_t const* e = p + (8 - byteSizeLength - byteSizeLength);
×
611
    if (e > ptr + byteSize) {
×
612
      e = ptr + byteSize;
×
613
    }
614
    while (p < e && *p == '\x00') {
×
615
      ++p;
×
616
    }
617

618
    // check if padding is correct
619
    if (p != ptr + 1 + byteSizeLength + byteSizeLength &&
×
620
        p != ptr + 1 + byteSizeLength + byteSizeLength +
×
621
                 (8 - byteSizeLength - byteSizeLength)) {
622
      throw Exception(Exception::ValidatorInvalidLength,
×
623
                      "Object padding is invalid");
×
624
    }
625

626
    indexTable = ptr + byteSize - (nrItems * byteSizeLength);
×
627
    if (indexTable < ptr + byteSizeLength + byteSizeLength || indexTable < p ||
×
628
        indexTable > ptr + length - byteSizeLength) {
×
629
      throw Exception(Exception::ValidatorInvalidLength,
×
630
                      "Object index table is out of bounds");
×
631
    }
632

633
    firstMember = p;
×
634
  }
635

636
  VELOCYPACK_ASSERT(nrItems > 0);
×
637

638
  ValueLength tableBuf[16];  // Fixed space to save offsets found sequentially
639
  ValueLength* table = tableBuf;
×
640
  std::unique_ptr<ValueLength[]> tableGuard;
×
641
  std::unique_ptr<std::unordered_set<ValueLength>> offsetSet;
×
642
  if (nrItems > 16) {
×
643
    if (nrItems <= 128) {
×
644
      table = new ValueLength[nrItems];  // throws if bad_alloc
×
645
      tableGuard.reset(table);           // for automatic deletion
×
646
    } else {
647
      // if we have even more items, we directly create an unordered_set
648
      offsetSet = std::make_unique<std::unordered_set<ValueLength>>();
×
649
    }
650
  }
651
  ValueLength actualNrItems = 0;
×
652
  uint8_t const* member = firstMember;
×
653
  while (member < indexTable) {
×
654
    validatePart(member, indexTable - member, true);
×
655

656
    Slice key(member);
×
657
    bool const isString = key.isString();
×
658
    if (!isString) {
×
659
      bool const isSmallInt = key.isSmallInt();
×
660
      if ((!isSmallInt && !key.isUInt()) ||
×
661
          (isSmallInt && key.getSmallInt() <= 0)) {
×
662
        throw Exception(Exception::ValidatorInvalidLength,
×
663
                        "Invalid object key type");
×
664
      }
665
    }
666

667
    ValueLength const keySize = key.byteSize();
×
668
    if (isString && options->validateUtf8Strings) {
×
669
      validatePart(member, keySize, true);
×
670
    }
671

672
    uint8_t const* value = member + keySize;
×
673
    if (value >= indexTable) {
×
674
      throw Exception(Exception::ValidatorInvalidLength,
×
675
                      "Object value leaking into index table");
×
676
    }
677
    validatePart(value, indexTable - value, true);
×
678

679
    ValueLength offset = static_cast<ValueLength>(member - ptr);
×
680
    if (nrItems <= 128) {
×
681
      table[actualNrItems] = offset;
×
682
    } else {
683
      offsetSet->emplace(offset);
×
684
    }
685

686
    member += keySize + Slice(value).byteSize();
×
687
    ++actualNrItems;
×
688

689
    if (actualNrItems > nrItems) {
×
690
      throw Exception(Exception::ValidatorInvalidLength,
×
691
                      "Object value has more key/value pairs than announced");
×
692
    }
693
  }
694

695
  if (actualNrItems < nrItems) {
×
696
    throw Exception(Exception::ValidatorInvalidLength,
×
697
                    "Object has fewer items than in index");
×
698
  }
699

700
  // Finally verify each offset in the index:
701
  if (nrItems <= 128) {
×
702
    for (ValueLength pos = 0; pos < nrItems; ++pos) {
×
703
      ValueLength offset = readIntegerNonEmpty<ValueLength>(
×
704
          indexTable + pos * byteSizeLength, byteSizeLength);
×
705
      // Binary search in sorted index list:
706
      ValueLength low = 0;
×
707
      ValueLength high = nrItems;
×
708
      bool found = false;
×
709
      while (low < high) {
×
710
        ValueLength mid = (low + high) / 2;
×
711
        if (offset == table[mid]) {
×
712
          found = true;
×
713
          break;
×
714
        } else if (offset < table[mid]) {
×
715
          high = mid;
×
716
        } else {  // offset > table[mid]
717
          low = mid + 1;
×
718
        }
719
      }
720
      if (!found) {
×
721
        throw Exception(Exception::ValidatorInvalidLength,
×
722
                        "Object has invalid index offset");
×
723
      }
724
    }
725
  } else {
726
    for (ValueLength pos = 0; pos < nrItems; ++pos) {
×
727
      ValueLength offset = readIntegerNonEmpty<ValueLength>(
×
728
          indexTable + pos * byteSizeLength, byteSizeLength);
×
729
      auto i = offsetSet->find(offset);
×
730
      if (i == offsetSet->end()) {
×
731
        throw Exception(Exception::ValidatorInvalidLength,
×
732
                        "Object has invalid index offset");
×
733
      }
734
      offsetSet->erase(i);
×
735
    }
736
  }
737
}
×
738

739
void Validator::validateBufferLength(std::size_t expected, std::size_t actual,
×
740
                                     bool isSubPart) {
741
  if ((expected > actual) || (expected != actual && !isSubPart)) {
×
742
    throw Exception(
×
743
        Exception::ValidatorInvalidLength,
744
        "given buffer length is unequal to actual length of Slice in buffer");
×
745
  }
746
}
×
747

748
void Validator::validateSliceLength(uint8_t const* ptr, std::size_t length,
×
749
                                    bool isSubPart) {
750
  std::size_t actual = static_cast<std::size_t>(Slice(ptr).byteSize());
×
751
  validateBufferLength(actual, length, isSubPart);
×
752
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc