php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73816 Broken eval(anonymous class)
Submitted: 2016-12-26 12:34 UTC Modified: 2020-01-03 10:28 UTC
Votes:4
Avg. Score:4.8 ± 0.4
Reproduced:4 of 4 (100.0%)
Same Version:2 (50.0%)
Same OS:1 (25.0%)
From: nicolas dot grekas+php at gmail dot com Assigned: nikic (profile)
Status: Closed Package: Scripting Engine problem
PHP Version: 7.1.0 OS:
Private report: No CVE-ID: None
 [2016-12-26 12:34 UTC] nicolas dot grekas+php at gmail dot com
Description:
------------
Calling several times eval() to create different anonymous classes doesn't create new anonymous classes but always returns the first one.
Used to work until 7.0.10 & 7.1.0 which both have the bug.

See https://3v4l.org/t7c5Z

Test script:
---------------
<?php

function anon()
{
    static $i = 0;
    return eval(sprintf('return new class { private $prop%s; };', ++$i));
}

var_dump(anon());
var_dump(anon());

Expected result:
----------------
object(class@anonymous)#1 (1) { ["prop1":"class@anonymous":private]=> NULL } object(class@anonymous)#1 (1) { ["prop2":"class@anonymous":private]=> NULL }

Actual result:
--------------
object(class@anonymous)#1 (1) { ["prop1":"class@anonymous":private]=> NULL } object(class@anonymous)#1 (1) { ["prop1":"class@anonymous":private]=> NULL }

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-12-26 12:48 UTC] nicolas dot grekas+php at gmail dot com
Maybe introduced by the fix for https://bugs.php.net/72594 ?
 [2016-12-26 20:48 UTC] dave at mudsite dot com
I believe you're right in the sense the fix for the referenced bug prevents your example from working, but had you stored the result of the first call to anon() and tried to use it, you'd have hit the bug that was fixed; the reference of the class would have been the second eval'd anon class, rather than the first.  I'd leave it to others to say if the previous, or current, behavior is appropriate.

If we took your example and ran it with a slight change, storing the return of anon() to variables and var_dumping after you'd expose the bug that's resolved (kinda).  You'd see that both c1, and c2 do reference a class-entry, but it'd be the second updated pointer to the entry.
https://3v4l.org/RiRcW

If you were to then set the prop1 before creating it again, you'd get even weirder output where prop1 wouldn't exist.
https://3v4l.org/Eq3Di

I'd believe the current (7.0.10, 7.1.0) results ought to be correct.  Where an anon class is defined it should be the only entry in the class-table.  Since the actual name of the class is something along the lines of:

class@anonymous\0/Path/to/file.php(6) : eval()'d code0xdeadbeef

Where the only printed value of this name is up to that nul byte, however, the hash into the class table you'd see references the memory location where the 'new class()' is defined.  Which, if your eval() line is at the same location the memory location never changes, even though you change the properties in it.
 [2016-12-26 21:51 UTC] nicolas dot grekas+php at gmail dot com
100% agree with this analysis. This still means eval+anonymous classes is kinda broken. I don't know if that would be a proper fix, but can't we use the hash of the evaluated string as part of the generated name? Or at least some nonce?
 [2016-12-27 00:48 UTC] dave at mudsite dot com
It's not really feasible to hash the eval string, as in theory the class could be a small part of the eval string. I do note that HHVM does handle the multiple evals well. I'm trying to think how we could support it simply. It's hard to tell since the file and address of the class is the same, the difference only being the property change. 

I'd have to look closer at how a class is built up, if it inserts itself before or after creating all the properties. If the latter we could probably create some unique identifier with that knowledge to support it.
 [2020-01-03 10:28 UTC] nikic@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: nikic
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Nov 23 03:01:32 2024 UTC