summaryrefslogtreecommitdiffstats
path: root/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php134
1 files changed, 94 insertions, 40 deletions
diff --git a/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php b/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php
index 8f451b8..8786a01 100644
--- a/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php
+++ b/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php
@@ -5,14 +5,6 @@ declare(strict_types=1);
namespace MaxMind\Db\Reader;
// @codingStandardsIgnoreLine
-use RuntimeException;
-
-/*
- * @ignore
- *
- * We subtract 1 from the log to protect against precision loss.
- */
-\define(__NAMESPACE__ . '\_MM_MAX_INT_BYTES', (int) ((log(\PHP_INT_MAX, 2) - 1) / 8));
class Decoder
{
@@ -20,20 +12,19 @@ class Decoder
* @var resource
*/
private $fileStream;
+
/**
* @var int
*/
private $pointerBase;
- /**
- * @var float
- */
- private $pointerBaseByteSize;
+
/**
* This is only used for unit testing.
*
* @var bool
*/
private $pointerTestHack;
+
/**
* @var bool
*/
@@ -51,8 +42,8 @@ class Decoder
private const _UINT64 = 9;
private const _UINT128 = 10;
private const _ARRAY = 11;
- private const _CONTAINER = 12;
- private const _END_MARKER = 13;
+ // 12 is the container type
+ // 13 is the end marker type
private const _BOOLEAN = 14;
private const _FLOAT = 15;
@@ -67,7 +58,6 @@ class Decoder
$this->fileStream = $fileStream;
$this->pointerBase = $pointerBase;
- $this->pointerBaseByteSize = $pointerBase > 0 ? log($pointerBase, 2) / 8 : 0;
$this->pointerTestHack = $pointerTestHack;
$this->switchByteOrder = $this->isPlatformLittleEndian();
@@ -118,6 +108,9 @@ class Decoder
return $this->decodeByType($type, $offset, $size);
}
+ /**
+ * @param int<0, max> $size
+ */
private function decodeByType(int $type, int $offset, int $size): array
{
switch ($type) {
@@ -195,7 +188,13 @@ class Decoder
{
// This assumes IEEE 754 doubles, but most (all?) modern platforms
// use them.
- [, $double] = unpack('E', $bytes);
+ $rc = unpack('E', $bytes);
+ if ($rc === false) {
+ throw new InvalidDatabaseException(
+ 'Could not unpack a double value from the given bytes.'
+ );
+ }
+ [, $double] = $rc;
return $double;
}
@@ -204,7 +203,13 @@ class Decoder
{
// This assumes IEEE 754 floats, but most (all?) modern platforms
// use them.
- [, $float] = unpack('G', $bytes);
+ $rc = unpack('G', $bytes);
+ if ($rc === false) {
+ throw new InvalidDatabaseException(
+ 'Could not unpack a float value from the given bytes.'
+ );
+ }
+ [, $float] = $rc;
return $float;
}
@@ -231,7 +236,13 @@ class Decoder
);
}
- [, $int] = unpack('l', $this->maybeSwitchByteOrder($bytes));
+ $rc = unpack('l', $this->maybeSwitchByteOrder($bytes));
+ if ($rc === false) {
+ throw new InvalidDatabaseException(
+ 'Could not unpack a 32bit integer value from the given bytes.'
+ );
+ }
+ [, $int] = $rc;
return $int;
}
@@ -254,19 +265,31 @@ class Decoder
$pointerSize = (($ctrlByte >> 3) & 0x3) + 1;
$buffer = Util::read($this->fileStream, $offset, $pointerSize);
- $offset = $offset + $pointerSize;
+ $offset += $pointerSize;
switch ($pointerSize) {
case 1:
$packed = \chr($ctrlByte & 0x7) . $buffer;
- [, $pointer] = unpack('n', $packed);
+ $rc = unpack('n', $packed);
+ if ($rc === false) {
+ throw new InvalidDatabaseException(
+ 'Could not unpack an unsigned short value from the given bytes (pointerSize is 1).'
+ );
+ }
+ [, $pointer] = $rc;
$pointer += $this->pointerBase;
break;
case 2:
$packed = "\x00" . \chr($ctrlByte & 0x7) . $buffer;
- [, $pointer] = unpack('N', $packed);
+ $rc = unpack('N', $packed);
+ if ($rc === false) {
+ throw new InvalidDatabaseException(
+ 'Could not unpack an unsigned long value from the given bytes (pointerSize is 2).'
+ );
+ }
+ [, $pointer] = $rc;
$pointer += $this->pointerBase + 2048;
break;
@@ -276,7 +299,13 @@ class Decoder
// It is safe to use 'N' here, even on 32 bit machines as the
// first bit is 0.
- [, $pointer] = unpack('N', $packed);
+ $rc = unpack('N', $packed);
+ if ($rc === false) {
+ throw new InvalidDatabaseException(
+ 'Could not unpack an unsigned long value from the given bytes (pointerSize is 3).'
+ );
+ }
+ [, $pointer] = $rc;
$pointer += $this->pointerBase + 526336;
break;
@@ -291,7 +320,7 @@ class Decoder
if (\PHP_INT_MAX - $pointerBase >= $pointerOffset) {
$pointer = $pointerOffset + $pointerBase;
} else {
- throw new RuntimeException(
+ throw new \RuntimeException(
'The database offset is too large to be represented on your platform.'
);
}
@@ -314,37 +343,44 @@ class Decoder
return 0;
}
- $integer = 0;
-
- // PHP integers are signed. _MM_MAX_INT_BYTES is the number of
+ // PHP integers are signed. PHP_INT_SIZE - 1 is the number of
// complete bytes that can be converted to an integer. However,
// we can convert another byte if the leading bit is zero.
- $useRealInts = $byteLength <= _MM_MAX_INT_BYTES
- || ($byteLength === _MM_MAX_INT_BYTES + 1 && (\ord($bytes[0]) & 0x80) === 0);
+ $useRealInts = $byteLength <= \PHP_INT_SIZE - 1
+ || ($byteLength === \PHP_INT_SIZE && (\ord($bytes[0]) & 0x80) === 0);
+
+ if ($useRealInts) {
+ $integer = 0;
+ for ($i = 0; $i < $byteLength; ++$i) {
+ $part = \ord($bytes[$i]);
+ $integer = ($integer << 8) + $part;
+ }
+ return $integer;
+ }
+
+ // We only use gmp or bcmath if the final value is too big
+ $integerAsString = '0';
for ($i = 0; $i < $byteLength; ++$i) {
$part = \ord($bytes[$i]);
- // We only use gmp or bcmath if the final value is too big
- if ($useRealInts) {
- $integer = ($integer << 8) + $part;
- } elseif (\extension_loaded('gmp')) {
- $integer = gmp_strval(gmp_add(gmp_mul((string) $integer, '256'), $part));
+ if (\extension_loaded('gmp')) {
+ $integerAsString = gmp_strval(gmp_add(gmp_mul($integerAsString, '256'), $part));
} elseif (\extension_loaded('bcmath')) {
- $integer = bcadd(bcmul((string) $integer, '256'), (string) $part);
+ $integerAsString = bcadd(bcmul($integerAsString, '256'), (string) $part);
} else {
- throw new RuntimeException(
+ throw new \RuntimeException(
'The gmp or bcmath extension must be installed to read this database.'
);
}
}
- return $integer;
+ return $integerAsString;
}
private function sizeFromCtrlByte(int $ctrlByte, int $offset): array
{
- $size = $ctrlByte & 0x1f;
+ $size = $ctrlByte & 0x1F;
if ($size < 29) {
return [$size, $offset];
@@ -356,10 +392,22 @@ class Decoder
if ($size === 29) {
$size = 29 + \ord($bytes);
} elseif ($size === 30) {
- [, $adjust] = unpack('n', $bytes);
+ $rc = unpack('n', $bytes);
+ if ($rc === false) {
+ throw new InvalidDatabaseException(
+ 'Could not unpack an unsigned short value from the given bytes.'
+ );
+ }
+ [, $adjust] = $rc;
$size = 285 + $adjust;
} else {
- [, $adjust] = unpack('N', "\x00" . $bytes);
+ $rc = unpack('N', "\x00" . $bytes);
+ if ($rc === false) {
+ throw new InvalidDatabaseException(
+ 'Could not unpack an unsigned long value from the given bytes.'
+ );
+ }
+ [, $adjust] = $rc;
$size = $adjust + 65821;
}
@@ -375,7 +423,13 @@ class Decoder
{
$testint = 0x00FF;
$packed = pack('S', $testint);
+ $rc = unpack('v', $packed);
+ if ($rc === false) {
+ throw new InvalidDatabaseException(
+ 'Could not unpack an unsigned short value from the given bytes.'
+ );
+ }
- return $testint === current(unpack('v', $packed));
+ return $testint === current($rc);
}
}