connortechnology / ZoneMinder

ZoneMinder is a free, open source Closed-circuit television software application developed for Linux which supports IP, USB and Analog cameras.
http://www.zoneminder.com/
GNU General Public License v2.0
9 stars 9 forks source link

Allowed memory size of 134217728 bytes exhausted #15

Closed Rayn0r closed 7 years ago

Rayn0r commented 7 years ago

While using Mocord the recorded files tend to grow large. Depending on the value under "Monitor" -> "Misc" -> "Section length" files can grow to over 400MB. When setting it to 300(sec) it creates files between 200-230MB.

Trying to watch a recording via https://server/zm/index.php?view=view_video&eid=2 results in Firefox telling me "Video format or MIME type not supported." Chrome just prints "ERR_CONTENT_LENGTH_MISMATCH"

The logs show a php error at line 147 here:

https://github.com/ZoneMinder/ZoneMinder/commit/d38bae72aeece85a20e3774c4953080e2e09e63c#diff-ce0ce7468f5290e4cbda150f33fe8616R147

This is the error from /var/log/php/php_errors.log:

[18-Apr-2017 11:10:44 Europe/Berlin] PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 64995360 bytes) in /usr/share/zoneminder/www/includes/csrf/csrf-magic.php on line 147

The script seems to try and load the entire h264 video into the buffer, which is a bit much when memory_limit = 128M is set in /etc/php/7.0/apache2/php.ini.

How can we work around this?

I am running Zoneminder on Ubuntu 16.04.2 LTS from storageareas branch at b71d021ae1f45e8c91e1aad72427446aae96fee9.

connortechnology commented 7 years ago

Today's packages (still building) have the csrf script turned off for view=video, so that should fix it. Let me know.

Rayn0r commented 7 years ago

I built the package from todays sources, and unfortunately the error is not gone. The the php_errors.log now show this:

[19-Apr-2017 22:34:05 Europe/Berlin] PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 65028096 bytes) in /usr/share/zoneminder/www/views/view_video.php on line 107
[19-Apr-2017 22:34:05 Europe/Berlin] PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 64995360 bytes) in /usr/share/zoneminder/www/includes/csrf/csrf-magic.php on line 147
Rayn0r commented 7 years ago

I belive to have figured out that rewriting is enabled at the bottom of includes/csrf/csrf-magic.php:

if ($GLOBALS['csrf']['rewrite']) ob_start('csrf_ob_handler');

includes/csrf/README.txt says, that calling csrf_conf('rewrite', false); should disable rewriting.

I applied the following patch to figure out what is happening:

--- /home/andrew/temp/ZoneMinder-connor/zoneminder_release/web/includes/csrf/csrf-magic.php     2017-04-20 10:36:29.909806406 +0200
+++ csrf-magic.php      2017-04-20 10:36:54.389802600 +0200
@@ -342,6 +342,9 @@
         trigger_error('No such configuration ' . $key, E_USER_WARNING);
         return;
     }
+    $wert='false';
+    if ($val) $wert='true';
+     syslog(LOG_WARNING,"Zoneminder: inside csrf_conf key: ".$key." value: ".$wert );
     $GLOBALS['csrf'][$key] = $val;
 }

@@ -400,6 +403,12 @@
 // Load user configuration
 if (function_exists('csrf_startup')) csrf_startup();
 // Initialize our handler
-if ($GLOBALS['csrf']['rewrite']) ob_start('csrf_ob_handler');
+if ($GLOBALS['csrf']['rewrite']){
+     ob_start('csrf_ob_handler');
+        syslog(LOG_WARNING,"Zoneminder: csrf_magig.php rewrite turned on" );
+}
+else {
+        syslog(LOG_WARNING,"Zoneminder: csrf_magig.php rewrite turned off" );
+}
 // Perform check
 if (!$GLOBALS['csrf']['defer'])      csrf_check();

--- /home/andrew/temp/ZoneMinder-connor/zoneminder_release/web/index.php        2017-04-20 08:06:15.181877415 +0200
+++ index.php   2017-04-20 10:38:36.501786264 +0200
@@ -192,6 +192,12 @@
 isset($request) || $request = NULL;
 isset($action) || $action = NULL;

+if ($view == 'view_video') {
+    syslog(LOG_WARNING,"Zoneminder: turning off rewrite" );
+    syslog(LOG_WARNING,"Zoneminder: view=".$view );
+    csrf_conf('rewrite', false);
+    }
+
 if ( ZM_ENABLE_CSRF_MAGIC && ( $action != 'login' ) && ( $view != 'view_video' ) ) {
     Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
     if ( ! csrf_check() ) {

The log then looks like this:

Apr 20 10:44:00 server web_php[12470]: Zoneminder: inside csrf_conf key: rewrite-js value: true
Apr 20 10:44:00 server web_php[12470]: Zoneminder: csrf_magig.php rewrite turned on
Apr 20 10:49:30 server web_php[8458]: Zoneminder: turning off rewrite
Apr 20 10:44:00 server web_php[12470]: Zoneminder: view=view_video
Apr 20 10:44:00 server web_php[12470]: Zoneminder: inside csrf_conf key: rewrite value: false

So the check whether or not to rewrite is done before it is turned off... What would be the right place to put the call?

Rayn0r commented 7 years ago

I got it working, but it is probably just a dirty hack. The video will be displayed without being sent through the buffer...

Please take a look and comment:

diff --git a/web/includes/csrf/csrf-magic.php b/web/includes/csrf/csrf-magic.php
index 153417f..a7783ea 100644
--- a/web/includes/csrf/csrf-magic.php
+++ b/web/includes/csrf/csrf-magic.php
@@ -61,7 +61,8 @@ $GLOBALS['csrf']['secret'] = '';
  * its rewriting capabilities. If you're serving non HTML content, you should
  * definitely set this false.
  */
-$GLOBALS['csrf']['rewrite'] = true;
+if (!isset ($GLOBALS['csrf']['rewrite'])) $GLOBALS['csrf']['rewrite'] = true;
+

 /**
  * Whether or not to use IP addresses when binding a user to a token. This is
diff --git a/web/index.php b/web/index.php
index 5d66bc1..0a1e2c7 100644
--- a/web/index.php
+++ b/web/index.php
@@ -146,7 +146,6 @@ if ( ZM_OPT_USE_AUTH ) {

 require_once( 'includes/lang.php' );
 require_once( 'includes/functions.php' );
-require_once( 'includes/csrf/csrf-magic.php' );

 # Running is global but only do the daemonCheck if it is actually needed
 $running = null;
@@ -165,6 +164,9 @@ $view = null;
 if ( isset($_REQUEST['view']) )
   $view = detaintPath($_REQUEST['view']);

+if ($view == 'view_video') $GLOBALS['csrf']['rewrite'] = false;
+require_once( 'includes/csrf/csrf-magic.php' );
+
 $request = null;
 if ( isset($_REQUEST['request']) )
   $request = detaintPath($_REQUEST['request']);
Rayn0r commented 7 years ago

Did you find the time yet to take a look this patch? Is there a different way to set the array ['csrf']['rewrite'] to "false" after the file 'includes/csrf/csrf-magic.php' was included?

connortechnology commented 7 years ago

My current index.php just doesn't load csrf for view_video, much like your patch. There is simply no need to do csrf checks on view_video.

Rayn0r commented 7 years ago

Displaying video is working fine now, so I'd say it is safe to close the issue.