From d16cbc9078a8901c3c1b2902cd9968c654c3cc4c Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Thu, 9 Apr 2020 16:25:28 +0200 Subject: [PATCH 1/2] Fix #69804: ::getStaticPropertyValue() throws on protected props `ReflectionClass` allows reading of the values of private and protected constants, and also to get private and protected static methods. Therefore getting the values of private and protected static properties is also permissible, especially since `::getStaticProperties()` already allows to do so. --- ext/reflection/php_reflection.c | 7 +++- ext/reflection/tests/006.phpt | 12 +++++++ ...ctionClass_getStaticPropertyValue_001.phpt | 34 +++++++++++-------- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 3fa4533e4e093..c385ca43a2a97 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3834,7 +3834,7 @@ ZEND_METHOD(reflection_class, getStaticProperties) ZEND_METHOD(reflection_class, getStaticPropertyValue) { reflection_object *intern; - zend_class_entry *ce; + zend_class_entry *ce, *old_scope; zend_string *name; zval *prop, *def_value = NULL; @@ -3847,7 +3847,12 @@ ZEND_METHOD(reflection_class, getStaticPropertyValue) if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) { return; } + + old_scope = EG(fake_scope); + EG(fake_scope) = ce; prop = zend_std_get_static_property(ce, name, 1); + EG(fake_scope) = old_scope; + if (!prop) { if (def_value) { ZVAL_COPY(return_value, def_value); diff --git a/ext/reflection/tests/006.phpt b/ext/reflection/tests/006.phpt index ab22f5c5c976c..7ee67150dbe66 100644 --- a/ext/reflection/tests/006.phpt +++ b/ext/reflection/tests/006.phpt @@ -88,16 +88,28 @@ TestDerived::testing(); string(3) "pub" string(3) "pub" string(7) "updated" +string(3) "pro" +string(3) "pro" EXCEPTION +string(3) "pri" +string(3) "pri" EXCEPTION string(7) "updated" string(7) "updated" string(7) "updated" +string(3) "pro" +string(3) "pro" EXCEPTION +string(3) "pri" +string(3) "pri" EXCEPTION string(7) "updated" string(7) "updated" string(7) "updated" +string(3) "pro" +string(3) "pro" EXCEPTION +string(3) "pri" +string(3) "pri" EXCEPTION ===DONE=== diff --git a/ext/reflection/tests/ReflectionClass_getStaticPropertyValue_001.phpt b/ext/reflection/tests/ReflectionClass_getStaticPropertyValue_001.phpt index 365ec89ff14a2..269f2c4d8c905 100644 --- a/ext/reflection/tests/ReflectionClass_getStaticPropertyValue_001.phpt +++ b/ext/reflection/tests/ReflectionClass_getStaticPropertyValue_001.phpt @@ -19,29 +19,28 @@ class B extends A { echo "Retrieving static values from A:\n"; $rcA = new ReflectionClass('A'); -var_dump($rcA->getStaticPropertyValue("privateOverridden", "default value")); -var_dump($rcA->getStaticPropertyValue("\0A\0privateOverridden")); -var_dump($rcA->getStaticPropertyValue("protectedOverridden", "default value")); -var_dump($rcA->getStaticPropertyValue("\0*\0protectedOverridden")); +var_dump($rcA->getStaticPropertyValue("privateDoesNotExist", "default value")); +var_dump($rcA->getStaticPropertyValue("privateOverridden")); +var_dump($rcA->getStaticPropertyValue("protectedDoesNotExist", "default value")); +var_dump($rcA->getStaticPropertyValue("protectedOverridden")); var_dump($rcA->getStaticPropertyValue("publicOverridden")); echo "\nRetrieving static values from B:\n"; $rcB = new ReflectionClass('B'); -var_dump($rcB->getStaticPropertyValue("\0A\0privateOverridden")); -var_dump($rcB->getStaticPropertyValue("\0B\0privateOverridden")); -var_dump($rcB->getStaticPropertyValue("\0*\0protectedOverridden")); +var_dump($rcB->getStaticPropertyValue("privateOverridden")); +var_dump($rcB->getStaticPropertyValue("protectedOverridden")); var_dump($rcB->getStaticPropertyValue("publicOverridden")); echo "\nRetrieving non-existent values from A with no default value:\n"; try { - var_dump($rcA->getStaticPropertyValue("protectedOverridden")); + var_dump($rcA->getStaticPropertyValue("protectedDoesNotExist")); echo "you should not see this"; } catch (Exception $e) { echo $e->getMessage() . "\n"; } try { - var_dump($rcA->getStaticPropertyValue("privateOverridden")); + var_dump($rcA->getStaticPropertyValue("privateDoesNotExist")); echo "you should not see this"; } catch (Exception $e) { echo $e->getMessage() . "\n"; @@ -51,9 +50,16 @@ try { --EXPECTF-- Retrieving static values from A: string(13) "default value" +string(16) "original private" +string(13) "default value" +string(18) "original protected" +string(15) "original public" + +Retrieving static values from B: +string(15) "changed private" +string(17) "changed protected" +string(14) "changed public" -Fatal error: Uncaught ReflectionException: Class A does not have a property named in %s:%d -Stack trace: -#0 %s(%d): ReflectionClass->getStaticPropertyValue('\x00A\x00privateOverr...') -#1 {main} - thrown in %s on line %d +Retrieving non-existent values from A with no default value: +Class A does not have a property named protectedDoesNotExist +Class A does not have a property named privateDoesNotExist From 9d64e1851a57a5eefbda9924e996481e37cff5c1 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 15 Apr 2020 15:15:21 +0200 Subject: [PATCH 2/2] Allow ::setStaticPropertyValue() to modify private and protected Forbidding to modify private and protected properties renders this method useless, as modifying public properties can be done directly. --- ext/reflection/php_reflection.c | 5 ++- ext/reflection/tests/006.phpt | 28 ++++++------ ...ctionClass_getStaticPropertyValue_001.phpt | 2 +- ...ctionClass_setStaticPropertyValue_001.phpt | 44 +++++++++++++------ 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index c385ca43a2a97..e22b42507062a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3872,7 +3872,7 @@ ZEND_METHOD(reflection_class, getStaticPropertyValue) ZEND_METHOD(reflection_class, setStaticPropertyValue) { reflection_object *intern; - zend_class_entry *ce; + zend_class_entry *ce, *old_scope; zend_string *name; zval *variable_ptr, *value; @@ -3885,7 +3885,10 @@ ZEND_METHOD(reflection_class, setStaticPropertyValue) if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) { return; } + old_scope = EG(fake_scope); + EG(fake_scope) = ce; variable_ptr = zend_std_get_static_property(ce, name, 1); + EG(fake_scope) = old_scope; if (!variable_ptr) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Class %s does not have a property named %s", ZSTR_VAL(ce->name), ZSTR_VAL(name)); diff --git a/ext/reflection/tests/006.phpt b/ext/reflection/tests/006.phpt index 7ee67150dbe66..0e33e0a6a17d4 100644 --- a/ext/reflection/tests/006.phpt +++ b/ext/reflection/tests/006.phpt @@ -90,26 +90,26 @@ string(3) "pub" string(7) "updated" string(3) "pro" string(3) "pro" -EXCEPTION +string(7) "updated" string(3) "pri" string(3) "pri" -EXCEPTION string(7) "updated" string(7) "updated" string(7) "updated" -string(3) "pro" -string(3) "pro" -EXCEPTION -string(3) "pri" -string(3) "pri" -EXCEPTION string(7) "updated" string(7) "updated" string(7) "updated" -string(3) "pro" -string(3) "pro" -EXCEPTION -string(3) "pri" -string(3) "pri" -EXCEPTION +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" +string(7) "updated" ===DONE=== diff --git a/ext/reflection/tests/ReflectionClass_getStaticPropertyValue_001.phpt b/ext/reflection/tests/ReflectionClass_getStaticPropertyValue_001.phpt index 269f2c4d8c905..c63ee330d4a8e 100644 --- a/ext/reflection/tests/ReflectionClass_getStaticPropertyValue_001.phpt +++ b/ext/reflection/tests/ReflectionClass_getStaticPropertyValue_001.phpt @@ -47,7 +47,7 @@ try { } ?> ---EXPECTF-- +--EXPECT-- Retrieving static values from A: string(13) "default value" string(16) "original private" diff --git a/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_001.phpt b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_001.phpt index bfefce369e00d..41a6482f66d14 100644 --- a/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_001.phpt +++ b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_001.phpt @@ -19,41 +19,59 @@ class B extends A { echo "Set static values in A:\n"; $rcA = new ReflectionClass('A'); -$rcA->setStaticPropertyValue("\0A\0privateOverridden", "new value 1"); -$rcA->setStaticPropertyValue("\0*\0protectedOverridden", "new value 2"); +$rcA->setStaticPropertyValue("privateOverridden", "new value 1"); +$rcA->setStaticPropertyValue("protectedOverridden", "new value 2"); $rcA->setStaticPropertyValue("publicOverridden", "new value 3"); print_r($rcA->getStaticProperties()); echo "\nSet static values in B:\n"; $rcB = new ReflectionClass('B'); -$rcB->setStaticPropertyValue("\0A\0privateOverridden", "new value 4"); -$rcB->setStaticPropertyValue("\0B\0privateOverridden", "new value 5"); -$rcB->setStaticPropertyValue("\0*\0protectedOverridden", "new value 6"); +$rcB->setStaticPropertyValue("privateOverridden", "new value 4"); +$rcB->setStaticPropertyValue("privateOverridden", "new value 5"); +$rcB->setStaticPropertyValue("protectedOverridden", "new value 6"); $rcB->setStaticPropertyValue("publicOverridden", "new value 7"); print_r($rcA->getStaticProperties()); print_r($rcB->getStaticProperties()); echo "\nSet non-existent values from A with no default value:\n"; try { - var_dump($rcA->setStaticPropertyValue("protectedOverridden", "new value 8")); + var_dump($rcA->setStaticPropertyValue("protectedDoesNotExist", "new value 8")); echo "you should not see this"; } catch (Exception $e) { echo $e->getMessage() . "\n"; } try { - var_dump($rcA->setStaticPropertyValue("privateOverridden", "new value 9")); + var_dump($rcA->setStaticPropertyValue("privateDoesNotExist", "new value 9")); echo "you should not see this"; } catch (Exception $e) { echo $e->getMessage() . "\n"; } ?> ---EXPECTF-- +--EXPECT-- Set static values in A: +Array +( + [privateOverridden] => new value 1 + [protectedOverridden] => new value 2 + [publicOverridden] => new value 3 +) -Fatal error: Uncaught ReflectionException: Class A does not have a property named in %s:%d -Stack trace: -#0 %s(%d): ReflectionClass->setStaticPropertyValue('\x00A\x00privateOverr...', 'new value 1') -#1 {main} - thrown in %s on line %d +Set static values in B: +Array +( + [privateOverridden] => new value 1 + [protectedOverridden] => new value 2 + [publicOverridden] => new value 3 +) +Array +( + [privateOverridden] => new value 5 + [protectedOverridden] => new value 6 + [publicOverridden] => new value 7 +) + +Set non-existent values from A with no default value: +Class A does not have a property named protectedDoesNotExist +Class A does not have a property named privateDoesNotExist