Press "Enter" to skip to content

A memory_limit Bug

In PHP 5.2x, memory_limit could not be set to any value above 4G, because zend_atoi was chosen by mistake.
A colleague shared a problem with me today (thanks to yanmi): a piece of code (PHP 5.2.11, Linux/X86_64) exhausts memory when memory_limit is set to 4096M, but not when it is set to 4095M. A strange problem indeed.
So what's going on?

The cause is simple enough. In OnChangeMemoryLimit, the handler for the memory_limit setting defined in PHPSRC/main.c, the machine's word length is never checked; zend_atoi is used uniformly to turn the string into a number. This was already fixed in PHP 5.3 (switched to zend_atol):

static PHP_INI_MH(OnChangeMemoryLimit)
{
    if (new_value) {
        PG(memory_limit) = zend_atoi(new_value, new_value_length);
    } else {
        PG(memory_limit) = 1<<30;       /* effectively, no limit */
    }
    return zend_set_memory_limit(PG(memory_limit));
}

And, as the name suggests, atoi converts to an integer. 4096M is 2 to the power of 32, so it overflows and wraps around to 0. Here is the zend_atoi code:

ZEND_API int zend_atoi(const char *str, int str_len)
{
    int retval;
    if (!str_len) {
        str_len = strlen(str);
    }
    retval = strtol(str, NULL, 0);
    if (str_len>0) {
        switch (str[str_len-1]) {
            case 'g':
            case 'G':
                retval *= 1024;
                /* break intentionally missing */
            case 'm':
            case 'M':
                retval *= 1024;
                /* break intentionally missing */
            case 'k':
            case 'K':
                retval *= 1024;
                break;
        }
    }
    return retval;
}

Finally, in zend_set_memory_limit, memory_limit is wrongly set to mm_heap's block_size, so the result is certainly far smaller than the 4096M you expected:

....
 AG(mm_heap)->limit = (memory_limit >= AG(mm_heap)->block_size) ? memory_limit : AG(mm_heap)->block_size;
...

Lastly: on a 32-bit machine this isn't really a bug. But plenty of machines are 64-bit these days and maximum memory is no longer capped at 4GB — PHP has to keep up with the times.
PS: reading other people's code pays off. Today I also learned the word "intentionally", ^_^.

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.