PHP tratando de asignar 127 TB de memoria y pérdida de memoria con preg_match y preg_replace

Creo que he encontrado un problema que parece crear una pérdida de memoria en Apache / PHP cuando los caracteres unicode como un delimitador oa veces en cualquier parte de una expresión regular con preg_match y preg_replace . Es posible que esto ocurra en más métodos preg_* .

Caso de prueba 1

Cree un nuevo archivo PHP test.php con los siguientes contenidos:

 <?php preg_match( '°test°i', 'test', $matches ); 

Caso de prueba 2

Cree un nuevo archivo PHP test.php con los siguientes contenidos:

 <?php preg_match( '°', 'test', $matches ); 

El carácter unicode ° utilizado como delimitador es el signo de grado. Prueba con cualquier otro personaje Unicode para ver qué sucede, si quieres.

Resultado

Una vez cargado el archivo en un servidor web con Apache 2.4.10 (Debian) y PHP 5.6.0-1+b1 , ejecútelo desde su navegador favorito. Espere ver una página en blanco o un mensaje que diga “respuesta no válida” o “esta página no se pudo cargar”.

Esto dará como resultado las siguientes dos líneas en su error de Apache .log (generalmente /var/log/error.log):

 [Mon Dec 15 10:31:09.941622 2014] [:error] [pid 6292] [client ###.###.###.###:64413] PHP Warning: preg_match(): in /path/to/test.php on line 2 [Mon Dec 15 10:31:09.941796 2014] [:error] [pid 6292] [client ###.###.###.###:64413] PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 139979759686648 bytes) in Unknown on line 0 

Tenga en cuenta que la cantidad de bytes que PHP intentó asignar es de poco más de 127 Terabytes.

Debes reiniciar Apache ahora

Ejecutar scripts PHP después de probar el script anterior dará como resultado todo tipo de avisos o errores fatales que aparecerán incluso en el código que ni siquiera debería ser capaz de producirlos. Por ejemplo, las clases extendidas de carga automática no parecen funcionar correctamente y pueden mostrar errores como los siguientes:

Class MyClass not found in file MyExtendingClass.php on Line 3

Y el archivo MyExtendingClass.php se vería así:

 <?php class MyExtendingClass extends MyClass { } 

Como puede ver, MyClass está claramente en la línea 2 y, aunque existe y el autocargador se ha configurado correctamente, PHP ya no puede encontrarlo.

Obviamente, no uses caracteres Unicode en expresiones regulares. ¿Pero por qué PHP pierde memoria cuando usa ciertos caracteres Unicode? ¿Hay una explicación para este comportamiento? Me gustaría saber por qué PHP piensa que debería asignar una cantidad tan grande de bytes.

Información del sistema

Apache / 2.4.10 (Debian) PHP / 5.6.0-1 + b1 OpenSSL / 1.0.1i configurado

Solutions Collecting From Web of "PHP tratando de asignar 127 TB de memoria y pérdida de memoria con preg_match y preg_replace"

Tengo un error similar, con PHP tratando de cargar en la RAM algo ligeramente más grande que 127 TB, pero a mí me sucede DESPUÉS de que el script haya terminado.

 PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 140683487706824 bytes) in Unknown on line 0 

Estoy usando la versión de PHP 5.4.39-0 + deb7u2, y veo que ocurre después de la ejecución del script porque el script funciona bien y no muestra problemas. De hecho, traté de registrar el tiempo de ejecución del script y el uso de la RAM y todo estuvo bien, hasta el final del script.

Mi problema probablemente esté relacionado con el uso de la combinación dblib + freetds, para conectarse al servidor remoto MSSQL, desde Debian, que es un error conocido # 64511 . De hecho, se informó que se corrigió, pero en mi caso no parece ser así.

En realidad, es muy difícil obtener reglas sobre este comportamiento, pero esto es lo que veo.

  • Me conecto al MSSQL remoto
  • realizar una consulta (que en realidad consiste en una llamada a un procedimiento almacenado y luego ejecuta una consulta SELECT inmediatamente después)

Después de la ejecución, si utilizara algo como este código para obtener los resultados:

 $sprocResultSet = $PDOquery->fetchAll(PDO::FETCH_ASSOC); $PDOquery->nextRowset(); $sprocExitCode = $PDOquery->fetchAll(PDO::FETCH_ASSOC); 

en la mayoría de los casos, obtendría ese extraño error de asignación de RAM, con supuestos terabytes cargados.

Cuando en lugar de especificar el estilo fetch dentro de fetchAll (), usé setFetchMode (), como aquí:

 $PDOquery->setFetchMode(PDO::FETCH_ASSOC); $sprocResultSet = $PDOquery->fetchAll(); $PDOquery->nextRowset(); $sprocExitCode = $PDOquery->fetchAll(); 

entonces nunca me di cuenta del mismo error con la asignación de RAM.

Intenté cerrar los cursores y anular la variable $ PDOquery, o simplemente dejar que se cerrara automáticamente al final del script, ninguno me ayudó. Además, tal vez sea importante mencionar: tanto el sproc como la consulta SELECT adicional después de recuperar una sola línea de datos, por lo que definitivamente no hay un gran resultado que regrese.

Entonces … no estoy seguro de si esto ayuda en todos los casos, pero si tiene una situación similar a la mía, intente configurar ese modo predeterminado de búsqueda para PDO con anticipación.

Y solo dos cosas para agregar. Primero, en el código anterior, se utiliza el nextResultSet () de la PDO, que también se informa como un error, lo que causa un problema con la memoria: nextRowset provoca daños en la memoria . En segundo lugar, no se usa fetch () de PDO, ya que cada vez que se usa, se produce un error de DBLIB.