php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #49664 Clone causes Segmentation fault
Submitted: 2009-09-25 07:46 UTC Modified: 2015-03-19 20:59 UTC
Votes:7
Avg. Score:4.3 ± 0.9
Reproduced:4 of 4 (100.0%)
Same Version:4 (100.0%)
Same OS:3 (75.0%)
From: patrik dot lermon at gmail dot com Assigned:
Status: Not a bug Package: Reproducible crash
PHP Version: 5.*, 6 (2009-09-20) OS: Linux
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: patrik dot lermon at gmail dot com
New email:
PHP Version: OS:

 

 [2009-09-25 07:46 UTC] patrik dot lermon at gmail dot com
Description:
------------
Under certain circumstances the clone keyword causes a Segmentation fault. This code is reproducible and tested with the same result on:
  - Ubuntu 9.04 / PHP 5.2.10 (cli) (built: Jun 22 2009 12:32:02)
  - Slackware 13.0.0.0.0 / PHP 5.3.0 (cli) (built: Sep 25 2009 08:58:26) (DEBUG)
  - Mac OS X 10.5.8 / PHP 5.2.10 (cli) (built: Aug 24 2009 12:47:12) 
  - Mac OS X 10.6.1 / PHP 5.3.0 (cli) (built: Jul 19 2009 00:34:29)

The Ubuntu and Mac OS X versions are standard builds from Zend, and the Slackware is built by me like this:

EXTENSION_DIR=/usr/lib/php/extensions \
CFLAGS="-O2 -march=i486 -mtune=i686" \
./configure \
--enable-force-cgi-redirect \
--enable-pcntl \
--enable-sigchild \
--prefix=/usr \
--libdir=/usr/lib \
--with-libdir=lib \
--sysconfdir=/etc \
--disable-safe-mode \
--disable-magic-quotes \
--enable-zend-multibyte \
--enable-mbregex \
--enable-tokenizer=shared \
--with-config-file-scan-dir=/etc/php \
--with-config-file-path=/etc/httpd \
--enable-mod_charset \
--with-layout=PHP \
--enable-sigchild \
--enable-xml \
--with-libxml-dir=/usr \
--enable-simplexml \
--enable-spl \
--enable-filter \
--enable-debug \
--with-openssl=shared \
--with-pcre-regex=/usr \
--with-zlib=shared,/usr \
--enable-bcmath=shared \
--with-bz2=shared,/usr \
--enable-calendar=shared \
--enable-ctype=shared \
--with-curl=shared \
--with-curlwrappers \
--with-mcrypt=/usr \
--enable-dba=shared \
--with-gdbm=/usr \
--with-db4=/usr \
--enable-exif=shared \
--enable-ftp=shared \
--with-gd=shared \
--with-jpeg-dir=/usr \
--with-png-dir=/usr \
--with-zlib-dir=/usr \
--with-xpm-dir=/usr \
--with-freetype-dir=/usr \
--with-t1lib=/usr \
--enable-gd-native-ttf \
--enable-gd-jis-conv \
--with-gettext=shared,/usr \
--with-gmp=shared,/usr \
--with-iconv=shared \
--with-imap-ssl=/usr \
--with-imap=/usr/local/lib/c-client \
--with-ldap=shared \
--enable-mbstring=shared \
--enable-hash \
--with-mysql=shared,/usr \
--with-mysqli=shared,/usr/bin/mysql_config \
--enable-pdo=shared \
--with-pdo-mysql=shared,/usr \
--with-pdo-sqlite=shared \
--with-pspell=shared,/usr \
--with-mm=/usr \
--enable-shmop=shared \
--with-snmp=shared,/usr \
--enable-soap=shared \
--enable-sockets \
--with-sqlite=shared \
--enable-sqlite-utf8 \
--with-regex=php \
--enable-sysvmsg \
--enable-sysvsem \
--enable-sysvshm \
--enable-wddx=shared \
--with-xsl=shared,/usr \
--enable-zip=shared \
--with-tsrm-pthreads \
--enable-shared=yes \
--enable-static=no \
--with-gnu-ld \
--with-pic \
--build=i486-slackware-linux


Reproduce code:
---------------
<?php
date_default_timezone_set('America/Los_Angeles');
class Test {
    public $previous, $next = NULL;
    public function __clone() {
        $this->previous != NULL ? $this->previous = clone $this->previous : $this->previous = NULL;
        $this->next != NULL ? $this->next = clone $this->next : $this->next = NULL;
    }
    public function __toString() {
        return '[' . ($this->previous != NULL ? '<' : '-') . ' ' . ($this->next != NULL ? '>' : '-') . ']';
    }
}

// Create some test objects
$a = new Test(); $b = new Test();

// Link them together
$a->next =& $b; $b->previous =& $a;

// Clone and print
echo "a before cloning:\na: " . $a . "\n";
$b = clone $a;
echo "These two should not look the same:\na: " . $a . "\nb: " . $clone . "\n";


Expected result:
----------------
a before cloning:
a: [- >]
These two should not look the same:
a: [- >]
b: [- -]


Actual result:
--------------
a before cloning:
a: [- >]
Segmentation fault


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-09-25 07:50 UTC] patrik dot lermon at gmail dot com
I am aware that the cloning will be recursive in some circumstances, but PHP should not segfault because of this.
 [2009-09-28 12:06 UTC] patrik dot lermon at gmail dot com
I get the same result with http://snaps.php.net/php5.3-latest.tar.gz
on Slackware.
 [2009-10-19 15:10 UTC] jani@php.net
Infinite recursion crashes. There's no fix for that.
 [2009-10-19 15:31 UTC] patrik dot lermon at gmail dot com
I don't agree. Perhaps my knowledge is not detailed enough, but an infinte recursion should:
a) run out of memory and die, or
b) detect the recursion and die.
In both these cases PHP should die in a controlled manner, not segfault.

My understanding is that segfault is never ok - that means the code is faulty.
 [2012-12-18 23:53 UTC] kurt at kurtrose dot com
Python handles this kind of recursion fine:

class F(object):
   def __repr__(self): return self.__repr__()

>>> repr(F())
  File "<stdin>", line 2, in __repr__
  File "<stdin>", line 2, in __repr__
  ...
  File "<stdin>", line 2, in __repr__
  File "<stdin>", line 2, in __repr__
RuntimeError: maximum recursion depth exceeded

No segfault.
 [2013-01-23 11:27 UTC] cf0hay at gmail dot com
> Infinite recursion crashes. There's no fix for that.

Err, what?

$ php -r 'function a(){ a(); } a();'
PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to 
allocate 130968 bytes) in Command line code on line 1

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 
130968 bytes) in Command line code on line 1

This is the intended behaviour on infinite recursion, not a segmentation fault. 
I wouldn't be surprised this could lead a security problem rather just a simple 
crash.
 [2013-01-23 12:07 UTC] patrik dot lermon at gmail dot com
And what do you get when you try with the reproduce code?
 [2013-01-28 13:43 UTC] cf0hay at gmail dot com
Same az OP (with PHP 5.4.8):

$ php a.php
a before cloning:
a: [- >]
Segmentation fault
 [2013-02-23 22:58 UTC] cataphract@php.net
-Status: Not a bug +Status: Re-Opened
 [2013-02-23 22:58 UTC] cataphract@php.net
Still present in trunk; reopening.
 [2013-04-06 17:45 UTC] dinesh dot joshi at yahoo dot com
This segmentation fault / coredump behavior is consistent with what lower level 
languages like C. So IMHO this should not be considered a PHP bug. Just don't get 
into infinite recursions. The language can't stop you from doing something stupid.

Here's a C program that demos the same behavior:

------------------------------
#include<stdio.h>

void fn() {
    char buff[16*1024]; 
    fn();
}

int main(void) {
    fn();
}
------------------------------
 [2013-08-07 20:19 UTC] initrd dot gz at gmail dot com
C lets you do a lot of stuff you aren't supposed to do. Just because C allows it 
doesn't mean higher level languages like PHP should. An out of memory error is 
much more helpful than a segfault, which could come from anything. Also, segfaults 
have historically lead to exploits.
 [2014-04-03 12:15 UTC] mike@php.net
-Status: Re-Opened +Status: Not a bug
 [2014-04-03 12:15 UTC] mike@php.net
@cataphract, why was this re-opened?
AFAICT there won't be a recursion counter.
 [2015-03-19 19:52 UTC] omars@php.net
I'd say the best way to handle this, could be using an ini directive.
 [2015-03-19 20:44 UTC] patrik dot lermon at gmail dot com
The error is for instance handled in a civilised manner by hhvm (Fatal error: Stack overflow in /in/aMjnT on line 6). So I guess infinite recursion crashes, and there is a way to realise this without segfaulting.
I withstand if a high level programming language segfaults its design is broken, and thus this the bug should remain open IMHO.
I'm not familiar with the underlaying design in php, but I guess that it just tries to reference memory and hope for the best instead of actually perform some checks, which hhvm manages to do. Again, I'm just guessing.

Segfault or stack overflow, does it matter?
Someone correct me if I'm wrong here, but as I understand it a stack overflow is realised and reported by the interpreter itself, while a segfault is the actual OS realising that the process tries to reference memory that it's not allowed to access and kills it. And if that is the case the actual use case for fixing this would be to give the user a proper error message (what happened, what file and line). Or if a proper exception handling was implemented in php (not mixing errors and exceptions) I presume the programmer could even wrap his code in a try-catch and make his own decision what to do in case of a stack overflow.


See http://3v4l.org/aMjnT


hhvm-3.3.1 - 3.5.1
    a before cloning:
    a: [- >]
    
    Fatal error: Stack overflow in /in/aMjnT on line 6
    
    Process exited with code 255.
 [2015-03-19 20:59 UTC] yohgaki@php.net
@patrik Open new feature request for recursion limit if it's not exist.

IIRC, perl segfault (well at least old one). Ruby/Python has limit like 1000.
 [2015-06-02 19:57 UTC] failureyousuck at gmail dot com
$ perl <<EOF
> use v5.14;
> use warnings;
>
> sub asub {
>         asub();
> }
> asub();
> EOF
Deep recursion on subroutine "main::asub" at - line 5.
Out of memory!

This has been the case since at least Perl 5.8.x., that is, since 2002, I believe.

Basically every language except C (because C is DESIGNED for you to be able to do stupid stuff) gives you something more useful than PHP. This is a bug.

http://en.wikipedia.org/wiki/Software_bug: "A software bug is an error, flaw, failure, or fault in a computer program or system that causes it to produce an incorrect or unexpected result, or to behave in unintended ways."

PHP is not designed (so far as I'm aware) to allow you to do stupid stuff. It's designed to be easy (see also "no eq"). If it's not designed to allow you to do stupid stuff, why does it let you do stupid stuff (WITH NO ERROR MESSAGE!) when it would be rudimentary to stop it? That is VERY unexpected. Hell, at this point PHP has a traceback -- so you even already have a stack counter!

Fixing enormous gaping holes in the language design is not a feature addition, it's a bug fix.


(And why does it matter whether or not Perl is broken too, anyway? Both are broken, only one is broken, either way whichever is broken needs fixing...)
 [2015-12-03 14:53 UTC] andreas at schipplock dot de
Hello everyone,
this is a bug and it still happens in php7 (https://github.com/php/php-src/releases/tag/php-7.0.0).

I realize it's the endless recursion that causes it but someone is right in here: if this happens in production code you will have no clue what caused it. If you see a fatal error then you can, at least, see where it happened and you can try to fix it but this segmentation fault is completely "dumb" in a way you can't even know where it happened.

This bug even brings down phpdbg.
 [2020-08-21 19:31 UTC] mtd at cardboardbox dot ri
> Infinite recursion crashes. There's no fix for that.

> if this happens in production code you will have no clue what caused it.
> This bug even brings down phpdbg.

And here it is. Infinite recursion crashes indeed, there's no fix for a badly designed script running on your interpreter. However, what should crash is the running script, not the interpreter. When the interpreter dies, nobody can know what and where caused the crash, it dies before it has a chance to report that. A segfault can occur for many different reasons. Hell, bad hardware is a potential cause. Therefore, the fact that the interpreter itself dies before it can report the problem is the bug here, not the infinite recursion that causes it.
 [2023-11-10 15:05 UTC] dams@php.net
This is now fixed in PHP 8.3.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Nov 24 02:01:28 2024 UTC