@@ -301,6 +301,17 @@ func FromBytes(buffer []byte, options ...ReaderOption) (*Reader, error) {
301
301
return nil , err
302
302
}
303
303
304
+ // Check for integer overflow in search tree size calculation
305
+ if metadata .NodeCount > 0 && metadata .RecordSize > 0 {
306
+ recordSizeQuarter := metadata .RecordSize / 4
307
+ if recordSizeQuarter > 0 {
308
+ maxNodes := ^ uint (0 ) / recordSizeQuarter
309
+ if metadata .NodeCount > maxNodes {
310
+ return nil , mmdberrors .NewInvalidDatabaseError ("database tree size would overflow" )
311
+ }
312
+ }
313
+ }
314
+
304
315
searchTreeSize := metadata .NodeCount * (metadata .RecordSize / 4 )
305
316
dataSectionStart := searchTreeSize + dataSectionSeparatorSize
306
317
dataSectionEnd := uint (metadataStart - len (metadataStartMarker ))
@@ -319,9 +330,12 @@ func FromBytes(buffer []byte, options ...ReaderOption) (*Reader, error) {
319
330
nodeOffsetMult : metadata .RecordSize / 4 ,
320
331
}
321
332
322
- reader .setIPv4Start ()
333
+ err = reader .setIPv4Start ()
334
+ if err != nil {
335
+ return nil , err
336
+ }
323
337
324
- return reader , err
338
+ return reader , nil
325
339
}
326
340
327
341
// Lookup retrieves the database record for ip and returns a Result, which can
@@ -365,21 +379,27 @@ func (r *Reader) LookupOffset(offset uintptr) Result {
365
379
return Result {decoder : r .decoder , offset : uint (offset )}
366
380
}
367
381
368
- func (r * Reader ) setIPv4Start () {
382
+ func (r * Reader ) setIPv4Start () error {
369
383
if r .Metadata .IPVersion != 6 {
370
384
r .ipv4StartBitDepth = 96
371
- return
385
+ return nil
372
386
}
373
387
374
388
nodeCount := r .Metadata .NodeCount
375
389
376
390
node := uint (0 )
377
391
i := 0
378
392
for ; i < 96 && node < nodeCount ; i ++ {
379
- node = readNodeBySize (r .buffer , node * r .nodeOffsetMult , 0 , r .Metadata .RecordSize )
393
+ var err error
394
+ node , err = readNodeBySize (r .buffer , node * r .nodeOffsetMult , 0 , r .Metadata .RecordSize )
395
+ if err != nil {
396
+ return err
397
+ }
380
398
}
381
399
r .ipv4Start = node
382
400
r .ipv4StartBitDepth = i
401
+
402
+ return nil
383
403
}
384
404
385
405
var zeroIP = netip .MustParseAddr ("::" )
@@ -409,46 +429,64 @@ func (r *Reader) lookupPointer(ip netip.Addr) (uint, int, error) {
409
429
}
410
430
411
431
// readNodeBySize reads a node value from the buffer based on record size and bit.
412
- func readNodeBySize (buffer []byte , offset , bit , recordSize uint ) uint {
432
+ func readNodeBySize (buffer []byte , offset , bit , recordSize uint ) (uint , error ) {
433
+ bufferLen := uint (len (buffer ))
413
434
switch recordSize {
414
435
case 24 :
415
436
offset += bit * 3
437
+ if offset > bufferLen - 3 {
438
+ return 0 , mmdberrors .NewInvalidDatabaseError (
439
+ "bounds check failed: insufficient buffer for 24-bit node read" ,
440
+ )
441
+ }
416
442
return (uint (buffer [offset ]) << 16 ) |
417
443
(uint (buffer [offset + 1 ]) << 8 ) |
418
- uint (buffer [offset + 2 ])
444
+ uint (buffer [offset + 2 ]), nil
419
445
case 28 :
420
446
if bit == 0 {
447
+ if offset > bufferLen - 4 {
448
+ return 0 , mmdberrors .NewInvalidDatabaseError (
449
+ "bounds check failed: insufficient buffer for 28-bit node read" ,
450
+ )
451
+ }
421
452
return ((uint (buffer [offset + 3 ]) & 0xF0 ) << 20 ) |
422
453
(uint (buffer [offset ]) << 16 ) |
423
454
(uint (buffer [offset + 1 ]) << 8 ) |
424
- uint (buffer [offset + 2 ])
455
+ uint (buffer [offset + 2 ]), nil
456
+ }
457
+ if offset > bufferLen - 7 {
458
+ return 0 , mmdberrors .NewInvalidDatabaseError (
459
+ "bounds check failed: insufficient buffer for 28-bit node read" ,
460
+ )
425
461
}
426
462
return ((uint (buffer [offset + 3 ]) & 0x0F ) << 24 ) |
427
463
(uint (buffer [offset + 4 ]) << 16 ) |
428
464
(uint (buffer [offset + 5 ]) << 8 ) |
429
- uint (buffer [offset + 6 ])
465
+ uint (buffer [offset + 6 ]), nil
430
466
case 32 :
431
467
offset += bit * 4
468
+ if offset > bufferLen - 4 {
469
+ return 0 , mmdberrors .NewInvalidDatabaseError (
470
+ "bounds check failed: insufficient buffer for 32-bit node read" ,
471
+ )
472
+ }
432
473
return (uint (buffer [offset ]) << 24 ) |
433
474
(uint (buffer [offset + 1 ]) << 16 ) |
434
475
(uint (buffer [offset + 2 ]) << 8 ) |
435
- uint (buffer [offset + 3 ])
476
+ uint (buffer [offset + 3 ]), nil
436
477
default :
437
- return 0
478
+ return 0 , mmdberrors . NewInvalidDatabaseError ( "unsupported record size" )
438
479
}
439
480
}
440
481
441
482
func (r * Reader ) traverseTree (ip netip.Addr , node uint , stopBit int ) (uint , int , error ) {
442
483
switch r .Metadata .RecordSize {
443
484
case 24 :
444
- n , i := r .traverseTree24 (ip , node , stopBit )
445
- return n , i , nil
485
+ return r .traverseTree24 (ip , node , stopBit )
446
486
case 28 :
447
- n , i := r .traverseTree28 (ip , node , stopBit )
448
- return n , i , nil
487
+ return r .traverseTree28 (ip , node , stopBit )
449
488
case 32 :
450
- n , i := r .traverseTree32 (ip , node , stopBit )
451
- return n , i , nil
489
+ return r .traverseTree32 (ip , node , stopBit )
452
490
default :
453
491
return 0 , 0 , mmdberrors .NewInvalidDatabaseError (
454
492
"unsupported record size: %d" ,
@@ -457,14 +495,15 @@ func (r *Reader) traverseTree(ip netip.Addr, node uint, stopBit int) (uint, int,
457
495
}
458
496
}
459
497
460
- func (r * Reader ) traverseTree24 (ip netip.Addr , node uint , stopBit int ) (uint , int ) {
498
+ func (r * Reader ) traverseTree24 (ip netip.Addr , node uint , stopBit int ) (uint , int , error ) {
461
499
i := 0
462
500
if ip .Is4 () {
463
501
i = r .ipv4StartBitDepth
464
502
node = r .ipv4Start
465
503
}
466
504
nodeCount := r .Metadata .NodeCount
467
505
buffer := r .buffer
506
+ bufferLen := uint (len (buffer ))
468
507
ip16 := ip .As16 ()
469
508
470
509
for ; i < stopBit && node < nodeCount ; i ++ {
@@ -475,22 +514,29 @@ func (r *Reader) traverseTree24(ip netip.Addr, node uint, stopBit int) (uint, in
475
514
baseOffset := node * 6
476
515
offset := baseOffset + bit * 3
477
516
517
+ if offset > bufferLen - 3 {
518
+ return 0 , 0 , mmdberrors .NewInvalidDatabaseError (
519
+ "bounds check failed during tree traversal" ,
520
+ )
521
+ }
522
+
478
523
node = (uint (buffer [offset ]) << 16 ) |
479
524
(uint (buffer [offset + 1 ]) << 8 ) |
480
525
uint (buffer [offset + 2 ])
481
526
}
482
527
483
- return node , i
528
+ return node , i , nil
484
529
}
485
530
486
- func (r * Reader ) traverseTree28 (ip netip.Addr , node uint , stopBit int ) (uint , int ) {
531
+ func (r * Reader ) traverseTree28 (ip netip.Addr , node uint , stopBit int ) (uint , int , error ) {
487
532
i := 0
488
533
if ip .Is4 () {
489
534
i = r .ipv4StartBitDepth
490
535
node = r .ipv4Start
491
536
}
492
537
nodeCount := r .Metadata .NodeCount
493
538
buffer := r .buffer
539
+ bufferLen := uint (len (buffer ))
494
540
ip16 := ip .As16 ()
495
541
496
542
for ; i < stopBit && node < nodeCount ; i ++ {
@@ -499,29 +545,37 @@ func (r *Reader) traverseTree28(ip netip.Addr, node uint, stopBit int) (uint, in
499
545
bit := (uint (ip16 [byteIdx ]) >> bitPos ) & 1
500
546
501
547
baseOffset := node * 7
548
+ offset := baseOffset + bit * 4
549
+
550
+ if baseOffset > bufferLen - 4 || offset > bufferLen - 3 {
551
+ return 0 , 0 , mmdberrors .NewInvalidDatabaseError (
552
+ "bounds check failed during tree traversal" ,
553
+ )
554
+ }
555
+
502
556
sharedByte := uint (buffer [baseOffset + 3 ])
503
557
mask := uint (0xF0 >> (bit * 4 ))
504
558
shift := 20 + bit * 4
505
559
nibble := ((sharedByte & mask ) << shift )
506
- offset := baseOffset + bit * 4
507
560
508
561
node = nibble |
509
562
(uint (buffer [offset ]) << 16 ) |
510
563
(uint (buffer [offset + 1 ]) << 8 ) |
511
564
uint (buffer [offset + 2 ])
512
565
}
513
566
514
- return node , i
567
+ return node , i , nil
515
568
}
516
569
517
- func (r * Reader ) traverseTree32 (ip netip.Addr , node uint , stopBit int ) (uint , int ) {
570
+ func (r * Reader ) traverseTree32 (ip netip.Addr , node uint , stopBit int ) (uint , int , error ) {
518
571
i := 0
519
572
if ip .Is4 () {
520
573
i = r .ipv4StartBitDepth
521
574
node = r .ipv4Start
522
575
}
523
576
nodeCount := r .Metadata .NodeCount
524
577
buffer := r .buffer
578
+ bufferLen := uint (len (buffer ))
525
579
ip16 := ip .As16 ()
526
580
527
581
for ; i < stopBit && node < nodeCount ; i ++ {
@@ -532,20 +586,33 @@ func (r *Reader) traverseTree32(ip netip.Addr, node uint, stopBit int) (uint, in
532
586
baseOffset := node * 8
533
587
offset := baseOffset + bit * 4
534
588
589
+ if offset > bufferLen - 4 {
590
+ return 0 , 0 , mmdberrors .NewInvalidDatabaseError (
591
+ "bounds check failed during tree traversal" ,
592
+ )
593
+ }
594
+
535
595
node = (uint (buffer [offset ]) << 24 ) |
536
596
(uint (buffer [offset + 1 ]) << 16 ) |
537
597
(uint (buffer [offset + 2 ]) << 8 ) |
538
598
uint (buffer [offset + 3 ])
539
599
}
540
600
541
- return node , i
601
+ return node , i , nil
542
602
}
543
603
544
604
func (r * Reader ) resolveDataPointer (pointer uint ) (uintptr , error ) {
545
- resolved := uintptr (pointer - r .Metadata .NodeCount - dataSectionSeparatorSize )
546
-
547
- if resolved >= uintptr (len (r .buffer )) {
605
+ // Check for integer underflow: pointer must be greater than nodeCount + separator
606
+ minPointer := r .Metadata .NodeCount + dataSectionSeparatorSize
607
+ if pointer >= minPointer {
608
+ resolved := uintptr (pointer - minPointer )
609
+ bufferLen := uintptr (len (r .buffer ))
610
+ if resolved < bufferLen {
611
+ return resolved , nil
612
+ }
613
+ // Error case - bounds exceeded
548
614
return 0 , mmdberrors .NewInvalidDatabaseError ("the MaxMind DB file's search tree is corrupt" )
549
615
}
550
- return resolved , nil
616
+ // Error case - underflow
617
+ return 0 , mmdberrors .NewInvalidDatabaseError ("the MaxMind DB file's search tree is corrupt" )
551
618
}
0 commit comments