@@ -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 ))
@@ -376,7 +387,13 @@ func (r *Reader) setIPv4Start() {
376
387
node := uint (0 )
377
388
i := 0
378
389
for ; i < 96 && node < nodeCount ; i ++ {
379
- node = readNodeBySize (r .buffer , node * r .nodeOffsetMult , 0 , r .Metadata .RecordSize )
390
+ var err error
391
+ node , err = readNodeBySize (r .buffer , node * r .nodeOffsetMult , 0 , r .Metadata .RecordSize )
392
+ if err != nil {
393
+ // If we encounter a bounds error during IPv4 start calculation,
394
+ // fall back to treating it as an IPv4-only database
395
+ break
396
+ }
380
397
}
381
398
r .ipv4Start = node
382
399
r .ipv4StartBitDepth = i
@@ -409,46 +426,64 @@ func (r *Reader) lookupPointer(ip netip.Addr) (uint, int, error) {
409
426
}
410
427
411
428
// readNodeBySize reads a node value from the buffer based on record size and bit.
412
- func readNodeBySize (buffer []byte , offset , bit , recordSize uint ) uint {
429
+ func readNodeBySize (buffer []byte , offset , bit , recordSize uint ) (uint , error ) {
430
+ bufferLen := uint (len (buffer ))
413
431
switch recordSize {
414
432
case 24 :
415
433
offset += bit * 3
434
+ if offset > bufferLen - 3 {
435
+ return 0 , mmdberrors .NewInvalidDatabaseError (
436
+ "bounds check failed: insufficient buffer for 24-bit node read" ,
437
+ )
438
+ }
416
439
return (uint (buffer [offset ]) << 16 ) |
417
440
(uint (buffer [offset + 1 ]) << 8 ) |
418
- uint (buffer [offset + 2 ])
441
+ uint (buffer [offset + 2 ]), nil
419
442
case 28 :
420
443
if bit == 0 {
444
+ if offset > bufferLen - 4 {
445
+ return 0 , mmdberrors .NewInvalidDatabaseError (
446
+ "bounds check failed: insufficient buffer for 28-bit node read" ,
447
+ )
448
+ }
421
449
return ((uint (buffer [offset + 3 ]) & 0xF0 ) << 20 ) |
422
450
(uint (buffer [offset ]) << 16 ) |
423
451
(uint (buffer [offset + 1 ]) << 8 ) |
424
- uint (buffer [offset + 2 ])
452
+ uint (buffer [offset + 2 ]), nil
453
+ }
454
+ if offset > bufferLen - 7 {
455
+ return 0 , mmdberrors .NewInvalidDatabaseError (
456
+ "bounds check failed: insufficient buffer for 28-bit node read" ,
457
+ )
425
458
}
426
459
return ((uint (buffer [offset + 3 ]) & 0x0F ) << 24 ) |
427
460
(uint (buffer [offset + 4 ]) << 16 ) |
428
461
(uint (buffer [offset + 5 ]) << 8 ) |
429
- uint (buffer [offset + 6 ])
462
+ uint (buffer [offset + 6 ]), nil
430
463
case 32 :
431
464
offset += bit * 4
465
+ if offset > bufferLen - 4 {
466
+ return 0 , mmdberrors .NewInvalidDatabaseError (
467
+ "bounds check failed: insufficient buffer for 32-bit node read" ,
468
+ )
469
+ }
432
470
return (uint (buffer [offset ]) << 24 ) |
433
471
(uint (buffer [offset + 1 ]) << 16 ) |
434
472
(uint (buffer [offset + 2 ]) << 8 ) |
435
- uint (buffer [offset + 3 ])
473
+ uint (buffer [offset + 3 ]), nil
436
474
default :
437
- return 0
475
+ return 0 , mmdberrors . NewInvalidDatabaseError ( "unsupported record size" )
438
476
}
439
477
}
440
478
441
479
func (r * Reader ) traverseTree (ip netip.Addr , node uint , stopBit int ) (uint , int , error ) {
442
480
switch r .Metadata .RecordSize {
443
481
case 24 :
444
- n , i := r .traverseTree24 (ip , node , stopBit )
445
- return n , i , nil
482
+ return r .traverseTree24 (ip , node , stopBit )
446
483
case 28 :
447
- n , i := r .traverseTree28 (ip , node , stopBit )
448
- return n , i , nil
484
+ return r .traverseTree28 (ip , node , stopBit )
449
485
case 32 :
450
- n , i := r .traverseTree32 (ip , node , stopBit )
451
- return n , i , nil
486
+ return r .traverseTree32 (ip , node , stopBit )
452
487
default :
453
488
return 0 , 0 , mmdberrors .NewInvalidDatabaseError (
454
489
"unsupported record size: %d" ,
@@ -457,14 +492,15 @@ func (r *Reader) traverseTree(ip netip.Addr, node uint, stopBit int) (uint, int,
457
492
}
458
493
}
459
494
460
- func (r * Reader ) traverseTree24 (ip netip.Addr , node uint , stopBit int ) (uint , int ) {
495
+ func (r * Reader ) traverseTree24 (ip netip.Addr , node uint , stopBit int ) (uint , int , error ) {
461
496
i := 0
462
497
if ip .Is4 () {
463
498
i = r .ipv4StartBitDepth
464
499
node = r .ipv4Start
465
500
}
466
501
nodeCount := r .Metadata .NodeCount
467
502
buffer := r .buffer
503
+ bufferLen := uint (len (buffer ))
468
504
ip16 := ip .As16 ()
469
505
470
506
for ; i < stopBit && node < nodeCount ; i ++ {
@@ -475,22 +511,29 @@ func (r *Reader) traverseTree24(ip netip.Addr, node uint, stopBit int) (uint, in
475
511
baseOffset := node * 6
476
512
offset := baseOffset + bit * 3
477
513
514
+ if offset > bufferLen - 3 {
515
+ return 0 , 0 , mmdberrors .NewInvalidDatabaseError (
516
+ "bounds check failed during tree traversal" ,
517
+ )
518
+ }
519
+
478
520
node = (uint (buffer [offset ]) << 16 ) |
479
521
(uint (buffer [offset + 1 ]) << 8 ) |
480
522
uint (buffer [offset + 2 ])
481
523
}
482
524
483
- return node , i
525
+ return node , i , nil
484
526
}
485
527
486
- func (r * Reader ) traverseTree28 (ip netip.Addr , node uint , stopBit int ) (uint , int ) {
528
+ func (r * Reader ) traverseTree28 (ip netip.Addr , node uint , stopBit int ) (uint , int , error ) {
487
529
i := 0
488
530
if ip .Is4 () {
489
531
i = r .ipv4StartBitDepth
490
532
node = r .ipv4Start
491
533
}
492
534
nodeCount := r .Metadata .NodeCount
493
535
buffer := r .buffer
536
+ bufferLen := uint (len (buffer ))
494
537
ip16 := ip .As16 ()
495
538
496
539
for ; i < stopBit && node < nodeCount ; i ++ {
@@ -499,29 +542,37 @@ func (r *Reader) traverseTree28(ip netip.Addr, node uint, stopBit int) (uint, in
499
542
bit := (uint (ip16 [byteIdx ]) >> bitPos ) & 1
500
543
501
544
baseOffset := node * 7
545
+ offset := baseOffset + bit * 4
546
+
547
+ if baseOffset > bufferLen - 4 || offset > bufferLen - 3 {
548
+ return 0 , 0 , mmdberrors .NewInvalidDatabaseError (
549
+ "bounds check failed during tree traversal" ,
550
+ )
551
+ }
552
+
502
553
sharedByte := uint (buffer [baseOffset + 3 ])
503
554
mask := uint (0xF0 >> (bit * 4 ))
504
555
shift := 20 + bit * 4
505
556
nibble := ((sharedByte & mask ) << shift )
506
- offset := baseOffset + bit * 4
507
557
508
558
node = nibble |
509
559
(uint (buffer [offset ]) << 16 ) |
510
560
(uint (buffer [offset + 1 ]) << 8 ) |
511
561
uint (buffer [offset + 2 ])
512
562
}
513
563
514
- return node , i
564
+ return node , i , nil
515
565
}
516
566
517
- func (r * Reader ) traverseTree32 (ip netip.Addr , node uint , stopBit int ) (uint , int ) {
567
+ func (r * Reader ) traverseTree32 (ip netip.Addr , node uint , stopBit int ) (uint , int , error ) {
518
568
i := 0
519
569
if ip .Is4 () {
520
570
i = r .ipv4StartBitDepth
521
571
node = r .ipv4Start
522
572
}
523
573
nodeCount := r .Metadata .NodeCount
524
574
buffer := r .buffer
575
+ bufferLen := uint (len (buffer ))
525
576
ip16 := ip .As16 ()
526
577
527
578
for ; i < stopBit && node < nodeCount ; i ++ {
@@ -532,20 +583,33 @@ func (r *Reader) traverseTree32(ip netip.Addr, node uint, stopBit int) (uint, in
532
583
baseOffset := node * 8
533
584
offset := baseOffset + bit * 4
534
585
586
+ if offset > bufferLen - 4 {
587
+ return 0 , 0 , mmdberrors .NewInvalidDatabaseError (
588
+ "bounds check failed during tree traversal" ,
589
+ )
590
+ }
591
+
535
592
node = (uint (buffer [offset ]) << 24 ) |
536
593
(uint (buffer [offset + 1 ]) << 16 ) |
537
594
(uint (buffer [offset + 2 ]) << 8 ) |
538
595
uint (buffer [offset + 3 ])
539
596
}
540
597
541
- return node , i
598
+ return node , i , nil
542
599
}
543
600
544
601
func (r * Reader ) resolveDataPointer (pointer uint ) (uintptr , error ) {
545
- resolved := uintptr (pointer - r .Metadata .NodeCount - dataSectionSeparatorSize )
546
-
547
- if resolved >= uintptr (len (r .buffer )) {
602
+ // Check for integer underflow: pointer must be greater than nodeCount + separator
603
+ minPointer := r .Metadata .NodeCount + dataSectionSeparatorSize
604
+ if pointer >= minPointer {
605
+ resolved := uintptr (pointer - minPointer )
606
+ bufferLen := uintptr (len (r .buffer ))
607
+ if resolved < bufferLen {
608
+ return resolved , nil
609
+ }
610
+ // Error case - bounds exceeded
548
611
return 0 , mmdberrors .NewInvalidDatabaseError ("the MaxMind DB file's search tree is corrupt" )
549
612
}
550
- return resolved , nil
613
+ // Error case - underflow
614
+ return 0 , mmdberrors .NewInvalidDatabaseError ("the MaxMind DB file's search tree is corrupt" )
551
615
}
0 commit comments