From 88ce26f8ca405a7572ef584b63131e0ef3c4b170 Mon Sep 17 00:00:00 2001 From: John Sterling Date: Sat, 18 Jun 2016 23:03:20 -0400 Subject: [PATCH] Improve performance of File\X509->_mapInExtensions() for large arrays This avoids passing array references by-value to is_array() (which would trigger a copy) by refactoring _subArray() into a separate is_array() check on a by-value var, and a separate unchecked reference return. --- phpseclib/File/X509.php | 95 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index 70cad035..91d90429 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -1505,7 +1505,9 @@ class File_X509 $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); - $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); + if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) { + $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); + } $this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1); $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1); @@ -1609,9 +1611,9 @@ class File_X509 */ function _mapInExtensions(&$root, $path, $asn1) { - $extensions = &$this->_subArray($root, $path); + $extensions = &$this->_subArrayUnchecked($root, $path); - if (is_array($extensions)) { + if ($extensions) { for ($i = 0; $i < count($extensions); $i++) { $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; @@ -1743,7 +1745,7 @@ class File_X509 if ($mapped !== false) { $values[$j] = $mapped; } - if ($id == 'pkcs-9-at-extensionRequest') { + if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) { $this->_mapInExtensions($values, $j, $asn1); } } elseif ($map) { @@ -3276,11 +3278,18 @@ class File_X509 $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); - $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); - $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); - if (is_array($rclist)) { - foreach ($rclist as $i => $extension) { - $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1); + if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) { + $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + } + if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) { + $rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates'); + if ($rclist_ref) { + $rclist = $crl['tbsCertList']['revokedCertificates']; + foreach ($rclist as $i => $extension) { + if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) { + $this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1); + } + } } } @@ -3903,6 +3912,74 @@ class File_X509 $this->caFlag = true; } + /** + * Check for validity of subarray + * + * This is intended for use in conjunction with _subArrayUnchecked(), + * implementing the checks included in _subArray() but without copying + * a potentially large array by passing its reference by-value to is_array(). + * + * @param array $root + * @param string $path + * @return boolean + * @access private + */ + function _isSubArrayValid($root, $path) + { + if (!is_array($root)) { + return false; + } + + foreach (explode('/', $path) as $i) { + if (!is_array($root)) { + return false; + } + + if (!isset($root[$i])) { + return true; + } + + $root = $root[$i]; + } + + return true; + } + + /** + * Get a reference to a subarray + * + * This variant of _subArray() does no is_array() checking, + * so $root should be checked with _isSubArrayValid() first. + * + * This is here for performance reasons: + * Passing a reference (i.e. $root) by-value (i.e. to is_array()) + * creates a copy. If $root is an especially large array, this is expensive. + * + * @param array $root + * @param string $path absolute path with / as component separator + * @param bool $create optional + * @access private + * @return array|false + */ + function &_subArrayUnchecked(&$root, $path, $create = false) + { + $false = false; + + foreach (explode('/', $path) as $i) { + if (!isset($root[$i])) { + if (!$create) { + return $false; + } + + $root[$i] = array(); + } + + $root = &$root[$i]; + } + + return $root; + } + /** * Get a reference to a subarray *