Skip to content

Commit 76c963a

Browse files
committed
Fix bug #69280: SoapClient classmap doesn't support fully qualified class name
There's a hash table that maps type names to class name, but names with a leading backslash are not supported. The engine has logic to strip away the leading backslash that we should replicate here. It works by checking if we need to make an actual copy in case an unexpected (e.g. invalid data or leading backslash) situations are detected. Upon making a copy we normalize the data in the table. Furthermore, previously the code assumed that the key was always valid and that the structure was a non-packed hash table. This isn't necessarily the case. The new code fixes this as well.
1 parent db2c1e6 commit 76c963a

File tree

4 files changed

+55
-10
lines changed

4 files changed

+55
-10
lines changed

ext/soap/php_encoding.c

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,8 @@ static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xml
449449
zend_string *type_name;
450450

451451
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(SOAP_GLOBAL(class_map), type_name, tmp) {
452-
ZVAL_DEREF(tmp);
453-
if (Z_TYPE_P(tmp) == IS_STRING &&
454-
ZSTR_LEN(ce->name) == Z_STRLEN_P(tmp) &&
455-
zend_binary_strncasecmp(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), Z_STRVAL_P(tmp), ZSTR_LEN(ce->name), ZSTR_LEN(ce->name)) == 0 &&
456-
type_name) {
452+
if (ZSTR_LEN(ce->name) == Z_STRLEN_P(tmp) &&
453+
zend_binary_strncasecmp(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), Z_STRVAL_P(tmp), ZSTR_LEN(ce->name), ZSTR_LEN(ce->name)) == 0) {
457454

458455
/* TODO: namespace isn't stored */
459456
encodePtr enc = NULL;
@@ -1382,7 +1379,6 @@ static zval *to_zval_object_ex(zval *ret, encodeTypePtr type, xmlNodePtr data, z
13821379
zend_class_entry *tmp;
13831380

13841381
if ((classname = zend_hash_str_find_deref(SOAP_GLOBAL(class_map), type->type_str, strlen(type->type_str))) != NULL &&
1385-
Z_TYPE_P(classname) == IS_STRING &&
13861382
(tmp = zend_fetch_class(Z_STR_P(classname), ZEND_FETCH_CLASS_AUTO)) != NULL) {
13871383
ce = tmp;
13881384
}
@@ -3629,3 +3625,48 @@ void delete_encoder_persistent(zval *zv)
36293625
assert(t->details.map == NULL);
36303626
free(t);
36313627
}
3628+
3629+
/* Normalize leading backslash similarly to how the engine strips it away. */
3630+
static inline zend_string *drop_leading_backslash(zend_string *str) {
3631+
if (ZSTR_VAL(str)[0] == '\\') {
3632+
return zend_string_init(ZSTR_VAL(str) + 1, ZSTR_LEN(str) - 1, false);
3633+
} else {
3634+
return zend_string_copy(str);
3635+
}
3636+
}
3637+
3638+
static HashTable *create_normalized_classmap_copy(HashTable *class_map)
3639+
{
3640+
HashTable *normalized = zend_new_array(zend_hash_num_elements(class_map));
3641+
3642+
zend_string *key;
3643+
zval *value;
3644+
ZEND_HASH_FOREACH_STR_KEY_VAL(class_map, key, value) {
3645+
ZVAL_DEREF(value);
3646+
3647+
if (key != NULL && Z_TYPE_P(value) == IS_STRING) {
3648+
zval zv;
3649+
ZVAL_STR(&zv, drop_leading_backslash(Z_STR_P(value)));
3650+
zend_hash_add_new(normalized, key, &zv);
3651+
}
3652+
} ZEND_HASH_FOREACH_END();
3653+
3654+
return normalized;
3655+
}
3656+
3657+
void create_normalized_classmap(zval *return_value, zval *class_map)
3658+
{
3659+
/* Check if we need to make a copy. */
3660+
zend_string *key;
3661+
zval *value;
3662+
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARR_P(class_map), key, value) {
3663+
if (key == NULL || Z_TYPE_P(value) != IS_STRING || ZSTR_VAL(Z_STR_P(value))[0] == '\\') {
3664+
/* TODO: should probably throw in some of these cases to indicate programmer error,
3665+
* e.g. in the case where a non-string (after dereferencing) is provided. */
3666+
RETURN_ARR(create_normalized_classmap_copy(Z_ARR_P(class_map)));
3667+
}
3668+
} ZEND_HASH_FOREACH_END();
3669+
3670+
/* We didn't have to make an actual copy, just increment the refcount. */
3671+
RETURN_COPY(class_map);
3672+
}

ext/soap/php_encoding.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ encodePtr get_conversion(int encode);
214214
void delete_encoder(zval *zv);
215215
void delete_encoder_persistent(zval *zv);
216216

217+
void create_normalized_classmap(zval *return_value, zval *class_map);
218+
217219
extern const encode defaultEncoding[];
218220
extern int numDefaultEncodings;
219221

ext/soap/soap.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,9 @@ PHP_METHOD(SoapServer, __construct)
816816

817817
if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL &&
818818
Z_TYPE_P(tmp) == IS_ARRAY) {
819-
service->class_map = zend_array_dup(Z_ARRVAL_P(tmp));
819+
zval zv;
820+
create_normalized_classmap(&zv, tmp);
821+
service->class_map = Z_ARR(zv);
820822
}
821823

822824
if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL &&
@@ -1982,7 +1984,7 @@ PHP_METHOD(SoapClient, __construct)
19821984
}
19831985
if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL &&
19841986
Z_TYPE_P(tmp) == IS_ARRAY) {
1985-
ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp);
1987+
create_normalized_classmap(Z_CLIENT_CLASSMAP_P(this_ptr), tmp);
19861988
}
19871989

19881990
if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL &&
@@ -4384,7 +4386,7 @@ static void delete_service(void *data) /* {{{ */
43844386
if (service->encoding) {
43854387
xmlCharEncCloseFunc(service->encoding);
43864388
}
4387-
if (service->class_map) {
4389+
if (service->class_map && !(GC_FLAGS(service->class_map) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(service->class_map)) {
43884390
zend_hash_destroy(service->class_map);
43894391
FREE_HASHTABLE(service->class_map);
43904392
}

ext/soap/tests/bug69280.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ $a->TestMethod($r1);
4141
?>
4242
--EXPECT--
4343
<?xml version="1.0" encoding="UTF-8"?>
44-
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><parameters><prop>prop</prop><prop1>prop1</prop1></parameters></SOAP-ENV:Body></SOAP-ENV:Envelope>
44+
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Body><parameters xsi:type="ns1:RealClass1"><ns1:prop>prop</ns1:prop><ns1:prop1>prop1</ns1:prop1></parameters></SOAP-ENV:Body></SOAP-ENV:Envelope>

0 commit comments

Comments
 (0)