php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #28673 readfile() crashes on huge local files
Submitted: 2004-06-07 15:46 UTC Modified: 2004-12-21 01:00 UTC
Votes:2
Avg. Score:4.5 ± 0.5
Reproduced:2 of 2 (100.0%)
Same Version:1 (50.0%)
Same OS:1 (50.0%)
From: valyala at tut dot by Assigned:
Status: No Feedback Package: Output Control
PHP Version: 4.3.7 OS: any with MMAP support
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: valyala at tut dot by
New email:
PHP Version: OS:

 

 [2004-06-07 15:46 UTC] valyala at tut dot by
Description:
------------
When I try to print huge files (greater than 500Mb) using readfile() function, my computer crashes.

I found in the PHP 4.3.7 sources the file /main/strems.c and function _php_stream_passthru() in it.
 The readfile() uses this function to print content of the file.
Below you can see source of the function with my comments:
===================================================================
PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC)
{
    size_t bcount = 0;
    int ready = 0;
    char buf[8192];
#ifdef HAVE_MMAP
    int fd;
#endif
 
#ifdef HAVE_MMAP
    if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)
            && stream->filterhead == NULL
            && php_stream_tell(stream) == 0
            && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0))
    {
        struct stat sbuf;
        off_t off; /* !!! offset == 0 (see above condition [php_stream_tell(stream) == 0] ) */
        void *p;
        size_t len;
 
        fstat(fd, &sbuf); /* !!! missing error check after this line */
 
        if (sbuf.st_size > sizeof(buf)) {
            off = php_stream_tell(stream); /* !!! offset == 0 (see above) */
            len = sbuf.st_size - off;
            /* suppose len > 1 Gb, machine has 128Mb RAM and 128Mb swap. What happens after the next 
line? */
            p = mmap(0, len, PROT_READ, MAP_SHARED, fd, off); /* !!! why MAP_SHARED, not MAP_PRIVATE ?
                            First parameter of the mmap is (void *) type, not (int) */
            if (p != (void *) MAP_FAILED) {
                BG(mmap_file) = p; /* !!! what sense of this and next string? Thread safety?
                        I don't understand how it works here */
                BG(mmap_len) = len;
                PHPWRITE(p, len);
                BG(mmap_file) = NULL; /* !!! thread safety? ok. why there is not BG(mmap_len) = 0 on 
the next line ? */
                munmap(p, len); /* !!! missing error check after munmap */
                bcount += len;
                ready = 1;
            }
        }
    }
#endif
    if(!ready) {
        int b;
 
        while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
            PHPWRITE(buf, b);
            bcount += b;
        }
    }
    return bcount;
}
===================================================================
 
And here you can see my version of the function:
 
===================================================================
PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC)
{
    size_t bcount = 0; /* counter of printed out bytes */
    int is_mapped = 0;
    char buf[8192];
    size_t buf_len = sizeof(buf);
#ifdef HAVE_MMAP
    int fd;
 
    if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)
            && stream->filterhead == NULL
            && php_stream_tell(stream) == 0
            && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0))
    {
        is_mapped = 1;
        buf_len = 1024 * 1024; /* default length of the mapped memory */
        struct stat sbuf;
        void *p; /* pinter to the mapped part of file */
        size_t len;
        /* get the length of local file connected to descriptor fd */
        fstat(fd, &sbuf);
        if (errno) {
             /* cannot get length of file */
            php_error_docref(NULL TSRMLS_CC, E_ERROR, "cannot get length of the file");
            return bcount;
        }
        len = (size_t) sbuf.st_size;
        /* print to the output buffer file contents */
        while (bcount < len) {
            if (len - bcount < buf_len) buf_len = len - bcount;
            p = mmap(NULL, buf_len, PROT_READ, MAP_PRIVATE, fd, (off_t) bcount); /* try to map part of 
the file to memory */
            if (p == (void *) MAP_FAILED) {
                /* error when mapping part of the file to memory */
                php_error_docref(NULL TSRMLS_CC, E_ERROR, "mmap error: cannot map part of the file to 
memory");
                break;
            }
            PHPWRITE(p, buf_len);
            munmap(p, buf_len); /* try to unmap allocated memory */
            if (errno) {
                /* error when unmapping memory */
                php_error_docref(NULL TSRMLS_CC, E_ERROR, "mmap error: cannot unmap allocated memory");
                break;
            }
            bcount += buf_len;
        }
    }
#endif
    if (!is_mapped) {
        /* print to the output buffer stream contents */
        while ((buf_len = php_stream_read(stream, buf, sizeof(buf))) > 0) {
            PHPWRITE(buf, buf_len);
            bcount += buf_len;
        }
    }
    return bcount;
}

Reproduce code:
---------------
<?php
readfile('very_big_file');
?>

Expected result:
----------------
contents of the very_big_file

Actual result:
--------------
PHP crash

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2004-12-13 01:30 UTC] sniper@php.net
Is this still a problem in the most recent PHP versions? (PHP 4.3.9 for example) If it is, please provide an unified diff of your changes (diff -u) and place the patch somewhere in the web and paste an url here.

 [2004-12-21 01:00 UTC] php-bugs at lists dot php dot net
No feedback was provided for this bug for over a week, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Oct 06 13:01:27 2024 UTC