Make WordPress Core

Ticket #32075: 32075-patch-v10.patch

File 32075-patch-v10.patch, 19.3 KB (added by jrf, 9 years ago)
  • src/wp-admin/admin.php

    From fd3dcfde1094efb72f13831b28d398c45db375c3 Mon Sep 17 00:00:00 2001
    From: jrfnl <github_nospam@adviesenzo.nl>
    Date: Fri, 8 Jul 2016 08:24:25 +0200
    Subject: [PATCH] Prevent WP setting the memory limit to a value lower than it
     currently is.
    
    Also fixes a bug in how the memory limits were tested in the first place.
    
    Includes unit tests for the newly added functions in load.php.
    ---
     src/wp-admin/admin.php                            |  16 +---
     src/wp-admin/includes/file.php                    |   5 +-
     src/wp-admin/includes/image-edit.php              |   3 +-
     src/wp-includes/class-wp-image-editor-gd.php      |  12 +--
     src/wp-includes/class-wp-image-editor-imagick.php |   4 +-
     src/wp-includes/default-constants.php             |  65 +++++++------
     src/wp-includes/deprecated.php                    |   4 +-
     src/wp-includes/functions.php                     | 110 ++++++++++++++++++++++
     src/wp-includes/load.php                          |  57 +++++++++++
     src/wp-includes/media.php                         |  20 ----
     tests/phpunit/tests/functions.php                 |  14 +++
     tests/phpunit/tests/load.php                      |  72 ++++++++++++++
     12 files changed, 294 insertions(+), 88 deletions(-)
     create mode 100644 tests/phpunit/tests/load.php
    
    diff --git a/src/wp-admin/admin.php b/src/wp-admin/admin.php
    index 3fabd87..ad341c8 100644
    a b else 
    138138        require(ABSPATH . 'wp-admin/menu.php');
    139139
    140140if ( current_user_can( 'manage_options' ) ) {
    141         /**
    142          * Filters the maximum memory limit available for administration screens.
    143          *
    144          * This only applies to administrators, who may require more memory for tasks like updates.
    145          * Memory limits when processing images (uploaded or edited by users of any role) are
    146          * handled separately.
    147          *
    148          * The WP_MAX_MEMORY_LIMIT constant specifically defines the maximum memory limit available
    149          * when in the administration back end. The default is 256M, or 256 megabytes of memory.
    150          *
    151          * @since 3.0.0
    152          *
    153          * @param string 'WP_MAX_MEMORY_LIMIT' The maximum WordPress memory limit. Default 256M.
    154          */
    155         @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
     141        wp_raise_memory_limit( 'admin' );
    156142}
    157143
    158144/**
  • src/wp-admin/includes/file.php

    diff --git a/src/wp-admin/includes/file.php b/src/wp-admin/includes/file.php
    index 3b754df..ad66a8d 100644
    a b function unzip_file($file, $to) { 
    569569        if ( ! $wp_filesystem || !is_object($wp_filesystem) )
    570570                return new WP_Error('fs_unavailable', __('Could not access filesystem.'));
    571571
    572         // Unzip can use a lot of memory, but not this much hopefully
    573         /** This filter is documented in wp-admin/admin.php */
    574         @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
     572        // Unzip can use a lot of memory, but not this much hopefully.
     573        wp_raise_memory_limit( 'admin' );
    575574
    576575        $needed_dirs = array();
    577576        $to = trailingslashit($to);
  • src/wp-admin/includes/image-edit.php

    diff --git a/src/wp-admin/includes/image-edit.php b/src/wp-admin/includes/image-edit.php
    index b34d2ac..c614a77 100644
    a b function image_edit_apply_changes( $image, $changes ) { 
    585585function stream_preview_image( $post_id ) {
    586586        $post = get_post( $post_id );
    587587
    588         /** This filter is documented in wp-admin/admin.php */
    589         @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
     588        wp_raise_memory_limit( 'admin' );
    590589
    591590        $img = wp_get_image_editor( _load_image_to_edit_path( $post_id ) );
    592591
  • src/wp-includes/class-wp-image-editor-gd.php

    diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php
    index 96e7bf1..d7823eb 100644
    a b class WP_Image_Editor_GD extends WP_Image_Editor { 
    9696                if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) )
    9797                        return new WP_Error( 'error_loading_image', __('File doesn&#8217;t exist?'), $this->file );
    9898
    99                 /**
    100                  * Filters the memory limit allocated for image manipulation.
    101                  *
    102                  * @since 3.5.0
    103                  *
    104                  * @param int|string $limit Maximum memory limit to allocate for images. Default WP_MAX_MEMORY_LIMIT.
    105                  *                          Accepts an integer (bytes), or a shorthand string notation, such as '256M'.
    106                  */
    107                 $image_memory_limit = apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT );
    108 
    10999                // Set artificially high because GD uses uncompressed images in memory.
    110                 @ini_set( 'memory_limit', $image_memory_limit );
     100                wp_raise_memory_limit( 'image' );
    111101
    112102                $this->image = @imagecreatefromstring( file_get_contents( $this->file ) );
    113103
  • src/wp-includes/class-wp-image-editor-imagick.php

    diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php
    index 3d92d39..4dad46d 100644
    a b class WP_Image_Editor_Imagick extends WP_Image_Editor { 
    137137                if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) )
    138138                        return new WP_Error( 'error_loading_image', __('File doesn&#8217;t exist?'), $this->file );
    139139
    140                 /** This filter is documented in wp-includes/class-wp-image-editor-gd.php */
    141                 $image_memory_limit = apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT );
    142140
    143141                /*
    144142                 * Even though Imagick uses less PHP memory than GD, set higher limit
    145143                 * for users that have low PHP.ini limits.
    146144                 */
    147                 @ini_set( 'memory_limit', $image_memory_limit );
     145                wp_raise_memory_limit( 'image' );
    148146
    149147                try {
    150148                        $this->image = new Imagick( $this->file );
  • src/wp-includes/default-constants.php

    diff --git a/src/wp-includes/default-constants.php b/src/wp-includes/default-constants.php
    index fe96e34..2aa3430 100644
    a b  
    1717function wp_initial_constants() {
    1818        global $blog_id;
    1919
    20         // set memory limits
    21         if ( !defined('WP_MEMORY_LIMIT') ) {
    22                 if ( is_multisite() ) {
    23                         define('WP_MEMORY_LIMIT', '64M');
     20        /**#@+
     21         * Constants for expressing human-readable data sizes in their respective number of bytes.
     22         *
     23         * @since 4.4.0
     24         */
     25        define( 'KB_IN_BYTES', 1024 );
     26        define( 'MB_IN_BYTES', 1024 * KB_IN_BYTES );
     27        define( 'GB_IN_BYTES', 1024 * MB_IN_BYTES );
     28        define( 'TB_IN_BYTES', 1024 * GB_IN_BYTES );
     29        /**#@-*/
     30
     31        $current_limit     = @ini_get( 'memory_limit' );
     32        $current_limit_int = wp_convert_hr_to_bytes( $current_limit );
     33
     34        // Define memory limits.
     35        if ( ! defined( 'WP_MEMORY_LIMIT' ) ) {
     36                if ( false === wp_is_ini_value_changeable( 'memory_limit' ) ) {
     37                        define( 'WP_MEMORY_LIMIT', $current_limit );
     38                } elseif ( is_multisite() ) {
     39                        define( 'WP_MEMORY_LIMIT', '64M' );
    2440                } else {
    25                         define('WP_MEMORY_LIMIT', '40M');
     41                        define( 'WP_MEMORY_LIMIT', '40M' );
    2642                }
    2743        }
    2844
    2945        if ( ! defined( 'WP_MAX_MEMORY_LIMIT' ) ) {
    30                 define( 'WP_MAX_MEMORY_LIMIT', '256M' );
     46                if ( false === wp_is_ini_value_changeable( 'memory_limit' ) ) {
     47                        define( 'WP_MAX_MEMORY_LIMIT', $current_limit );
     48                } elseif ( -1 === $current_limit_int || $current_limit_int > 268435456 /* = 256M */ ) {
     49                        define( 'WP_MAX_MEMORY_LIMIT', $current_limit );
     50                } else {
     51                        define( 'WP_MAX_MEMORY_LIMIT', '256M' );
     52                }
     53        }
     54
     55        // Set memory limits.
     56        $wp_limit_int = wp_convert_hr_to_bytes( WP_MEMORY_LIMIT );
     57        if ( -1 !== $current_limit_int && ( -1 === $wp_limit_int || $wp_limit_int > $current_limit_int ) ) {
     58                @ini_set( 'memory_limit', WP_MEMORY_LIMIT );
    3159        }
    3260
    3361        if ( ! isset($blog_id) )
    3462                $blog_id = 1;
    3563
    36         // set memory limits.
    37         if ( function_exists( 'memory_get_usage' ) ) {
    38                 $current_limit = @ini_get( 'memory_limit' );
    39                 $current_limit_int = intval( $current_limit );
    40                 if ( false !== strpos( $current_limit, 'G' ) )
    41                         $current_limit_int *= 1024;
    42                 $wp_limit_int = intval( WP_MEMORY_LIMIT );
    43                 if ( false !== strpos( WP_MEMORY_LIMIT, 'G' ) )
    44                         $wp_limit_int *= 1024;
    45 
    46                 if ( -1 != $current_limit && ( -1 == WP_MEMORY_LIMIT || $current_limit_int < $wp_limit_int ) )
    47                         @ini_set( 'memory_limit', WP_MEMORY_LIMIT );
    48         }
    49 
    5064        if ( !defined('WP_CONTENT_DIR') )
    5165                define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' ); // no trailing slash, full paths only - WP_CONTENT_URL is defined further down
    5266
    function wp_initial_constants() { 
    110124        define( 'MONTH_IN_SECONDS',  30 * DAY_IN_SECONDS    );
    111125        define( 'YEAR_IN_SECONDS',  365 * DAY_IN_SECONDS    );
    112126        /**#@-*/
    113 
    114         /**#@+
    115          * Constants for expressing human-readable data sizes in their respective number of bytes.
    116          *
    117          * @since 4.4.0
    118          */
    119         define( 'KB_IN_BYTES', 1024 );
    120         define( 'MB_IN_BYTES', 1024 * KB_IN_BYTES );
    121         define( 'GB_IN_BYTES', 1024 * MB_IN_BYTES );
    122         define( 'TB_IN_BYTES', 1024 * GB_IN_BYTES );
    123         /**#@-*/
    124127}
    125128
    126129/**
  • src/wp-includes/deprecated.php

    diff --git a/src/wp-includes/deprecated.php b/src/wp-includes/deprecated.php
    index c61e5d3..cfd636c 100644
    a b function wp_load_image( $file ) { 
    31753175        if ( ! function_exists('imagecreatefromstring') )
    31763176                return __('The GD image library is not installed.');
    31773177
    3178         /** This filter is documented in wp-includes/class-wp-image-editor-gd.php */
    3179         $image_memory_limit = apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT );
    31803178
    31813179        // Set artificially high because GD uses uncompressed images in memory.
    3182         @ini_set( 'memory_limit', $image_memory_limit );
     3180        wp_raise_memory_limit( 'image' );
    31833181
    31843182        $image = imagecreatefromstring( file_get_contents( $file ) );
    31853183
  • src/wp-includes/functions.php

    diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php
    index df0dbce..1920e39 100644
    a b function wp_object_type_exists( $object_type ) { 
    53795379
    53805380        return false;
    53815381}
     5382
     5383/**
     5384 * Attempts to raise the PHP memory limit for memory intensive processes.
     5385 *
     5386 * Only allows raising the existing limit and prevents lowering it.
     5387 *
     5388 * @since 4.6.0
     5389 *
     5390 * @param string $context  Context in which the function is called.
     5391 *                         Either 'admin', 'image' or an arbitrary other context.
     5392 *                         Defaults to 'admin'.
     5393 *                         If an arbitrary context is passed, the similarly arbitrary
     5394 *                         "{$context}_memory_limit" filter will be invoked.
     5395 * @return bool|int|string The limit that was set or false on failure.
     5396 */
     5397function wp_raise_memory_limit( $context = 'admin' ) {
     5398        // Exit early if the limit cannot be changed.
     5399        if ( false === wp_is_ini_value_changeable( 'memory_limit' ) ) {
     5400                return false;
     5401        }
     5402
     5403        $current_limit     = @ini_get( 'memory_limit' );
     5404        $current_limit_int = wp_convert_hr_to_bytes( $current_limit );
     5405
     5406        if ( -1 === $current_limit_int ) {
     5407                return false;
     5408        }
     5409
     5410        $wp_max_limit     = WP_MAX_MEMORY_LIMIT;
     5411        $wp_max_limit_int = wp_convert_hr_to_bytes( $wp_max_limit );
     5412        $filtered_limit   = $wp_max_limit;
     5413
     5414        switch ( $context ) {
     5415                case 'admin':
     5416                        /**
     5417                         * Filters the maximum memory limit available for administration screens.
     5418                         *
     5419                         * This only applies to administrators, who may require more memory for tasks like updates.
     5420                         * Memory limits when processing images (uploaded or edited by users of any role) are
     5421                         * handled separately.
     5422                         *
     5423                         * The WP_MAX_MEMORY_LIMIT constant specifically defines the maximum memory limit available
     5424                         * when in the administration back end. The default is 256M (256 megabytes
     5425                         * of memory) or the original `memory_limit` php.ini value if this is higher.
     5426                         *
     5427                         * @since 3.0.0
     5428                         * @since 4.6.0 The default takes the original `memory_limit` into account.
     5429                         *
     5430                         * @param int|string $filtered_limit The maximum WordPress memory limit.
     5431                         *                                   Accepts an integer (bytes), or a shorthand string
     5432                         *                                   notation, such as '256M'.
     5433                         */
     5434                        $filtered_limit = apply_filters( 'admin_memory_limit', $filtered_limit );
     5435                        break;
     5436
     5437                case 'image':
     5438                        /**
     5439                         * Filters the memory limit allocated for image manipulation.
     5440                         *
     5441                         * @since 3.5.0
     5442                         * @since 4.6.0 The default takes the original `memory_limit` into account.
     5443                         *
     5444                         * @param int|string $filtered_limit Maximum memory limit to allocate for images.
     5445                         *                                   Default WP_MAX_MEMORY_LIMIT or the original
     5446                         *                                   php.ini memory_limit, whichever is higher.
     5447                         *                                   Accepts an integer (bytes), or a shorthand string
     5448                         *                                   notation, such as '256M'.
     5449                         */
     5450                        $filtered_limit = apply_filters( 'image_memory_limit', $filtered_limit );
     5451                        break;
     5452
     5453                default:
     5454                        /**
     5455                         * Filters the memory limit allocated for arbitrary contexts.
     5456                         *
     5457                         * The dynamic portion of the hook name, `$context`, refers to an arbitrary
     5458                         * context passed on calling the function. This allows for plugins to define
     5459                         * their own contexts for raising the memory limit.
     5460                         *
     5461                         * @since 4.6.0
     5462                         *
     5463                         * @param int|string $filtered_limit Maximum memory limit to allocate for images.
     5464                         *                                   Default 256M or the original php.ini memory_limit,
     5465                         *                                   whichever is higher.
     5466                         *                                   Accepts an integer (bytes), or a shorthand string
     5467                         *                                   notation, such as '256M'.
     5468                         */
     5469                        $filtered_limit = apply_filters( "{$context}_memory_limit", $filtered_limit );
     5470                        break;
     5471        }
     5472
     5473        $filtered_limit_int = wp_convert_hr_to_bytes( $filtered_limit );
     5474
     5475        if ( -1 === $filtered_limit_int || ( $filtered_limit_int > $wp_max_limit_int && $filtered_limit_int > $current_limit_int ) ) {
     5476                if ( false !== @ini_set( 'memory_limit', $filtered_limit ) ) {
     5477                        return $filtered_limit;
     5478                } else {
     5479                        return false;
     5480                }
     5481        } elseif ( -1 === $wp_max_limit_int || $wp_max_limit_int > $current_limit_int ) {
     5482                if ( false !== @ini_set( 'memory_limit', $wp_max_limit ) ) {
     5483                        return $wp_max_limit;
     5484                } else {
     5485                        return false;
     5486                }
     5487        }
     5488        else {
     5489                return false;
     5490        }
     5491}
  • src/wp-includes/load.php

    diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php
    index c6ff6cb..1d8e52d 100644
    a b function is_ssl() { 
    974974        }
    975975        return false;
    976976}
     977
     978/**
     979 * Converts a shorthand byte value to an integer byte value.
     980 *
     981 * @since 2.3.0
     982 *
     983 * @link http://php.net/manual/en/function.ini-get.php
     984 * @link http://php.net/manual/en/faq.using.php#faq.using.shorthandbytes
     985 *
     986 * @param string $value An (PHP ini) byte value, either shorthand or ordinary.
     987 * @return int An integer byte value.
     988 */
     989function wp_convert_hr_to_bytes( $value ) {
     990        $value = trim( $value );
     991        $last  = strtolower( substr( $value, -1 ) );
     992
     993        switch ( $last ) {
     994                case 'g':
     995                        $value *= GB_IN_BYTES;
     996                        break;
     997                case 'm':
     998                        $value *= MB_IN_BYTES;
     999                        break;
     1000                case 'k':
     1001                        $value *= KB_IN_BYTES;
     1002                        break;
     1003                default:
     1004                        // Left empty on purpose.
     1005                        break;
     1006        }
     1007
     1008    // Deal with large (float) values which run into the maximum integer size.
     1009        return (int) min( PHP_INT_MAX, $value );
     1010}
     1011
     1012/**
     1013 * Determines whether a PHP ini value is changeable at runtime.
     1014 *
     1015 * @since 4.6.0
     1016 *
     1017 * @link http://php.net/manual/en/function.ini-get-all.php
     1018 *
     1019 * @param string $setting The name of the ini setting to check.
     1020 * @return bool True if the value is changeable at runtime. False otherwise.
     1021 */
     1022function wp_is_ini_value_changeable( $setting ) {
     1023        static $ini_all;
     1024
     1025        if ( ! isset( $ini_all ) ) {
     1026                $ini_all = ini_get_all();
     1027        }
     1028
     1029        if ( isset( $ini_all[ $setting ]['access'] ) && ( INI_ALL === $ini_all[ $setting ]['access'] || INI_USER === $ini_all[ $setting ]['access'] ) ) {
     1030                return true;
     1031        }
     1032        return false;
     1033}
  • src/wp-includes/media.php

    diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php
    index 6a060b1..df96a17 100644
    a b function wp_expand_dimensions( $example_width, $example_height, $max_width, $max 
    27772777}
    27782778
    27792779/**
    2780  * Converts a shorthand byte value to an integer byte value.
    2781  *
    2782  * @since 2.3.0
    2783  *
    2784  * @param string $size A shorthand byte value.
    2785  * @return int An integer byte value.
    2786  */
    2787 function wp_convert_hr_to_bytes( $size ) {
    2788         $size  = strtolower( $size );
    2789         $bytes = (int) $size;
    2790         if ( strpos( $size, 'k' ) !== false )
    2791                 $bytes = intval( $size ) * KB_IN_BYTES;
    2792         elseif ( strpos( $size, 'm' ) !== false )
    2793                 $bytes = intval($size) * MB_IN_BYTES;
    2794         elseif ( strpos( $size, 'g' ) !== false )
    2795                 $bytes = intval( $size ) * GB_IN_BYTES;
    2796         return $bytes;
    2797 }
    2798 
    2799 /**
    28002780 * Determines the maximum upload size allowed in php.ini.
    28012781 *
    28022782 * @since 2.5.0
  • tests/phpunit/tests/functions.php

    diff --git a/tests/phpunit/tests/functions.php b/tests/phpunit/tests/functions.php
    index ce5f657..ff49f28 100644
    a b class Tests_Functions extends WP_UnitTestCase { 
    865865
    866866                $this->assertNull( wp_ext2type( 'unknown_format' ) );
    867867        }
     868
     869        /**
     870         * Test raising the memory limit.
     871         *
     872         * Unfortunately as the default for 'WP_MAX_MEMORY_LIMIT' in the
     873         * test suite is -1, we can not test the memory limit negotiations.
     874         *
     875         * @ticket 32075
     876         */
     877        function test_wp_raise_memory_limit() {
     878                ini_set( 'memory_limit', '40M' );
     879                $this->assertSame( -1, wp_raise_memory_limit() );
     880                $this->assertEquals( '-1', ini_get( 'memory_limit' ) );
     881        }
    868882}
  • new file tests/phpunit/tests/load.php

    diff --git a/tests/phpunit/tests/load.php b/tests/phpunit/tests/load.php
    new file mode 100644
    index 0000000..a27de49
    - +  
     1<?php
     2
     3/**
     4 * @group load.php
     5 */
     6class Tests_Load extends WP_UnitTestCase {
     7
     8        /**
     9         * Test converting (PHP ini) byte values to integer byte values.
     10         *
     11         * @dataProvider data_wp_convert_hr_to_bytes
     12         */
     13        function test_wp_convert_hr_to_bytes( $value, $expected ) {
     14                $this->assertSame( $expected, wp_convert_hr_to_bytes( $value ) );
     15        }
     16
     17        function data_wp_convert_hr_to_bytes() {
     18                $array = array(
     19                        // Integer input.
     20                        array( -1, -1 ), // = no memory limit.
     21                        array( 8388608, 8388608 ), // 8M.
     22
     23                        // String input (memory limit shorthand values).
     24                        array( '32k', 32768 ),
     25                        array( '64K', 65536 ),
     26                        array( '128m', 134217728 ),
     27                        array( '256M', 268435456 ),
     28                        array( '1g', 1073741824 ),
     29                        array( '1024', 1024 ), // No letter will be interpreted as integer value.
     30
     31                        // Edge cases.
     32                        array( 'g', 0 ),
     33                        array( 'null', 0 ),
     34                        array( 'off', 0 ),
     35                );
     36
     37                // Test for running into maximum integer size limit on 32bit systems.
     38                if ( 2147483647 === PHP_INT_MAX ) {
     39                        $array[] = array( '2G', 2147483647 );
     40                        $array[] = array( '4G', 2147483647 );
     41                } else {
     42                        $array[] = array( '2G', 2147483648 );
     43                        $array[] = array( '4G', 4294967296 );
     44                }
     45
     46                return $array;
     47        }
     48
     49        /**
     50         * Test the determining of the changeability of a PHP ini value.
     51         *
     52         * @dataProvider data_wp_is_ini_value_changeable
     53         */
     54        function test_wp_is_ini_value_changeable( $setting, $expected ) {
     55                $this->assertSame( $expected, wp_is_ini_value_changeable( $setting ) );
     56        }
     57
     58        function data_wp_is_ini_value_changeable() {
     59                $array = array(
     60                        array( 'memory_limit', true ), // PHP_INI_ALL.
     61                        array( 'log_errors', true ), // PHP_INI_ALL.
     62                        array( 'upload_max_filesize', false ), // PHP_INI_PERDIR.
     63                        array( 'upload_tmp_dir', false ), // PHP_INI_SYSTEM.
     64                );
     65
     66                if ( extension_loaded( 'Tidy' ) && version_compare( PHP_VERSION, '7.0.0', '>' ) ) {
     67                        $array[] = array( 'tidy.clean_output', true ); // PHP_INI_USER.
     68                }
     69
     70                return $array;
     71        }
     72}