Skip to content

Commit 0167df4

Browse files
GH-22681: avoid truncation on null bytes in Reflection*::__toString()
Given that there is not an unchecked version of `smart_str_append_printf()`, to avoid compiler warnings from using `%S` to format `zend_string` pointers (which the smart string printing machinery supports, but printf does not support) in a few places the format specifier is declared in a variable rather than being specified directly in the `smart_str_append_printf()` call.
1 parent 6a61f63 commit 0167df4

9 files changed

Lines changed: 62 additions & 32 deletions

ext/reflection/php_reflection.c

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,9 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{
308308
}
309309
/* }}} */
310310

311-
static void _const_string(smart_str *str, const char *name, zval *value, const char *indent);
311+
static void _const_string(smart_str *str, const zend_string *name, zval *value, const char *indent);
312312
static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, const char* indent);
313-
static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent);
313+
static void _property_string(smart_str *str, zend_property_info *prop, const zend_string *prop_name, const char* indent);
314314
static void _class_const_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent);
315315
static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent);
316316
static void _extension_string(smart_str *str, const zend_module_entry *module, const char *indent);
@@ -324,7 +324,10 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
324324

325325
/* TBD: Repair indenting of doc comment (or is this to be done in the parser?) */
326326
if (ce->doc_comment) {
327-
smart_str_append_printf(str, "%s%s", indent, ZSTR_VAL(ce->doc_comment));
327+
// %S is used for zend_string pointers by smart str printing, but normally
328+
// is for wide character strings and so compilers complain if this is inline
329+
const char *format = "%s%S";
330+
smart_str_append_printf(str, format, indent, ce->doc_comment);
328331
smart_str_appendc(str, '\n');
329332
}
330333

@@ -494,7 +497,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
494497
if (prop_name && ZSTR_LEN(prop_name) && ZSTR_VAL(prop_name)[0]) { /* skip all private and protected properties */
495498
if (!zend_hash_exists(&ce->properties_info, prop_name)) {
496499
count++;
497-
_property_string(&prop_str, NULL, ZSTR_VAL(prop_name), ZSTR_VAL(sub_indent));
500+
_property_string(&prop_str, NULL, prop_name, ZSTR_VAL(sub_indent));
498501
}
499502
}
500503
} ZEND_HASH_FOREACH_END();
@@ -549,7 +552,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
549552
/* }}} */
550553

551554
/* {{{ _const_string */
552-
static void _const_string(smart_str *str, const char *name, zval *value, const char *indent)
555+
static void _const_string(smart_str *str, const zend_string *name, zval *value, const char *indent)
553556
{
554557
const char *type = zend_zval_type_name(value);
555558
uint32_t flags = Z_CONSTANT_FLAGS_P(value);
@@ -579,7 +582,7 @@ static void _const_string(smart_str *str, const char *name, zval *value, const c
579582

580583
smart_str_appends(str, type);
581584
smart_str_appendc(str, ' ');
582-
smart_str_appends(str, name);
585+
smart_str_append(str, name);
583586
smart_str_appends(str, " ] { ");
584587

585588
if (Z_TYPE_P(value) == IS_ARRAY) {
@@ -610,7 +613,10 @@ static void _class_const_string(smart_str *str, const zend_string *name, zend_cl
610613
const char *type = type_str ? ZSTR_VAL(type_str) : zend_zval_type_name(&c->value);
611614

612615
if (c->doc_comment) {
613-
smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(c->doc_comment));
616+
// %S is used for zend_string pointers by smart str printing, but normally
617+
// is for wide character strings and so compilers complain if this is inline
618+
const char *format = "%s%S\n";
619+
smart_str_append_printf(str, format, indent, c->doc_comment);
614620
}
615621
smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ",
616622
indent, final, visibility, type, ZSTR_VAL(name));
@@ -826,10 +832,13 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent
826832
* What's "wrong" is that any whitespace before the doc comment start is
827833
* swallowed, leading to an unaligned comment.
828834
*/
835+
// %S is used for zend_string pointers by smart str printing, but normally
836+
// is for wide character strings and so compilers complain if this is inline
837+
const char *format = "%s%S\n";
829838
if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.doc_comment) {
830-
smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(fptr->op_array.doc_comment));
839+
smart_str_append_printf(str, format, indent, fptr->op_array.doc_comment);
831840
} else if (fptr->type == ZEND_INTERNAL_FUNCTION && fptr->internal_function.doc_comment) {
832-
smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(fptr->internal_function.doc_comment));
841+
smart_str_append_printf(str, format, indent, fptr->internal_function.doc_comment);
833842
}
834843

835844
smart_str_appendl(str, indent, strlen(indent));
@@ -939,14 +948,19 @@ static zval *property_get_default(zend_property_info *prop_info) {
939948
}
940949

941950
/* {{{ _property_string */
942-
static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent)
951+
static void _property_string(smart_str *str, zend_property_info *prop, const zend_string *prop_name, const char* indent)
943952
{
944953
if (prop && prop->doc_comment) {
945-
smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(prop->doc_comment));
954+
// %S is used for zend_string pointers by smart str printing, but normally
955+
// is for wide character strings and so compilers complain if this is inline
956+
const char *format = "%s%S\n";
957+
smart_str_append_printf(str, format, indent, prop->doc_comment);
946958
}
947959
smart_str_append_printf(str, "%sProperty [ ", indent);
948960
if (!prop) {
949-
smart_str_append_printf(str, "<dynamic> public $%s", prop_name);
961+
ZEND_ASSERT(prop_name && "Properties without info must have a name provided");
962+
smart_str_appends(str, "<dynamic> public $");
963+
smart_str_append(str, prop_name);
950964
} else {
951965
if (prop->flags & ZEND_ACC_ABSTRACT) {
952966
smart_str_appends(str, "abstract ");
@@ -989,11 +1003,15 @@ static void _property_string(smart_str *str, zend_property_info *prop, const cha
9891003
smart_str_appendc(str, ' ');
9901004
zend_string_release(type_str);
9911005
}
1006+
smart_str_appendc(str, '$');
9921007
if (!prop_name) {
9931008
const char *class_name;
994-
zend_unmangle_property_name(prop->name, &class_name, &prop_name);
1009+
const char *prop_name_cstr;
1010+
zend_unmangle_property_name(prop->name, &class_name, &prop_name_cstr);
1011+
smart_str_appends(str, prop_name_cstr);
1012+
} else {
1013+
smart_str_append(str, prop_name);
9951014
}
996-
smart_str_append_printf(str, "$%s", prop_name);
9971015

9981016
zval *default_value = property_get_default(prop);
9991017
if (default_value && !Z_ISUNDEF_P(default_value)) {
@@ -1031,9 +1049,21 @@ static void _extension_ini_string(const zend_ini_entry *ini_entry, smart_str *st
10311049
}
10321050

10331051
smart_str_appends(str, "> ]\n");
1034-
smart_str_append_printf(str, " %s Current = '%s'\n", indent, ini_entry->value ? ZSTR_VAL(ini_entry->value) : "");
1052+
if (ini_entry->value) {
1053+
// %S is used for zend_string pointers by smart str printing, but normally
1054+
// is for wide character strings and so compilers complain if this is inline
1055+
const char *format = " %s Current = '%S'\n";
1056+
smart_str_append_printf(str, format, indent, ini_entry->value);
1057+
} else {
1058+
smart_str_append_printf(str, " %s Current = ''\n", indent);
1059+
}
10351060
if (ini_entry->modified) {
1036-
smart_str_append_printf(str, " %s Default = '%s'\n", indent, ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : "");
1061+
if (ini_entry->orig_value) {
1062+
const char *format = " %s Default = '%S'\n";
1063+
smart_str_append_printf(str, format, indent, ini_entry->orig_value);
1064+
} else {
1065+
smart_str_append_printf(str, " %s Default = ''\n", indent);
1066+
}
10371067
}
10381068
smart_str_append_printf(str, " %s}\n", indent);
10391069
}
@@ -1122,7 +1152,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c
11221152

11231153
ZEND_HASH_MAP_FOREACH_PTR(EG(zend_constants), constant) {
11241154
if (ZEND_CONSTANT_MODULE_NUMBER(constant) == module->module_number) {
1125-
_const_string(&str_constants, ZSTR_VAL(constant->name), &constant->value, " ");
1155+
_const_string(&str_constants, constant->name, &constant->value, " ");
11261156
num_constants++;
11271157
}
11281158
} ZEND_HASH_FOREACH_END();
@@ -5894,7 +5924,7 @@ ZEND_METHOD(ReflectionProperty, __toString)
58945924
RETURN_THROWS();
58955925
}
58965926
GET_REFLECTION_OBJECT_PTR(ref);
5897-
_property_string(&str, ref->prop, ZSTR_VAL(ref->unmangled_name), "");
5927+
_property_string(&str, ref->prop, ref->unmangled_name, "");
58985928
RETURN_STR(smart_str_extract(&str));
58995929
}
59005930
/* }}} */
@@ -7955,7 +7985,7 @@ ZEND_METHOD(ReflectionConstant, __toString)
79557985
}
79567986

79577987
GET_REFLECTION_OBJECT_PTR(const_);
7958-
_const_string(&str, ZSTR_VAL(const_->name), &const_->value, "");
7988+
_const_string(&str, const_->name, &const_->value, "");
79597989
RETURN_STR(smart_str_extract(&str));
79607990
}
79617991

ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ echo new ReflectionClass(Demo::class);
1919

2020
?>
2121
--EXPECTF--
22-
/** F
22+
/** F%0oo */
2323
Constant [ public bool DEMO ] { 1 }
2424
string(11) "/** F%0oo */"
2525
Class [ <user> class Demo ] {
2626
@@ %s(%d) : eval()'d code %d-%d
2727

2828
- Constants [1] {
29-
/** F
29+
/** F%0oo */
3030
Constant [ public bool DEMO ] { 1 }
3131
}
3232

ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var_dump( $r->getDocComment() );
1515

1616
?>
1717
--EXPECTF--
18-
/** F
18+
/** F%0oo */
1919
Class [ <user> class Demo ] {
2020
@@ %s(%d) : eval()'d code %d-%d
2121

ext/reflection/tests/gh22681/ReflectionConstant_name.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ var_dump( $r->getName() );
1111

1212
?>
1313
--EXPECTF--
14-
Constant [ bool F ] { 1 }
14+
Constant [ bool F%0oo ] { 1 }
1515
string(4) "F%0oo"

ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Class [ <user> final class Demo implements UnitEnum ] {
2020
@@ %s(%d) : eval()'d code %d-%d
2121

2222
- Constants [1] {
23-
/** F
23+
/** F%0oo */
2424
Constant [ public Demo C ] { Object }
2525
}
2626

ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var_dump( $r->getINIEntries()['arg_separator.output'] );
1616
?>
1717
--EXPECTF--
1818
Entry [ arg_separator.output <ALL> ]
19-
Current = 'f'
19+
Current = 'f%0oo'
2020
Default = '&'
2121
}
2222

ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ var_dump( $r->getDocComment() );
2424

2525
?>
2626
--EXPECTF--
27-
/** F
27+
/** F%0oo */
2828
Function [ <user> function demo ] {
2929
@@ %s(%d) : eval()'d code %d - %d
3030
}
3131
string(11) "/** F%0oo */"
32-
/** B
32+
/** B%0ar */
3333
Method [ <user> public method demo ] {
3434
@@ %s(%d) : eval()'d code %d - %d
3535
}

ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ echo new ReflectionClass(Demo::class);
1919

2020
?>
2121
--EXPECTF--
22-
/** F
22+
/** F%0oo */
2323
Property [ public $prop = NULL ]
2424
string(11) "/** F%0oo */"
2525
Class [ <user> class Demo ] {
@@ -35,7 +35,7 @@ Class [ <user> class Demo ] {
3535
}
3636

3737
- Properties [1] {
38-
/** F
38+
/** F%0oo */
3939
Property [ public $prop = NULL ]
4040
}
4141

ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ var_dump( $r->getDocComment() );
1111
echo new ReflectionObject($obj);
1212

1313
?>
14-
--EXPECT--
15-
Property [ <dynamic> public $F ]
14+
--EXPECTF--
15+
Property [ <dynamic> public $F%0oo ]
1616
bool(false)
1717
Object of class [ <internal:Core> class stdClass ] {
1818

@@ -29,7 +29,7 @@ Object of class [ <internal:Core> class stdClass ] {
2929
}
3030

3131
- Dynamic properties [1] {
32-
Property [ <dynamic> public $F ]
32+
Property [ <dynamic> public $F%0oo ]
3333
}
3434

3535
- Methods [0] {

0 commit comments

Comments
 (0)