Skip to content

Commit 8adc9b6

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 8adc9b6

File tree

4 files changed

+56
-11
lines changed

4 files changed

+56
-11
lines changed

ext/soap/php_encoding.c

Lines changed: 48 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,49 @@ 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+
HashTable *create_normalized_classmap(HashTable *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(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 create_normalized_classmap_copy(class_map);
3667+
}
3668+
} ZEND_HASH_FOREACH_END();
3669+
3670+
/* We didn't have to make an actual copy, just increment the refcount. */
3671+
GC_ADDREF(class_map);
3672+
return class_map;
3673+
}

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+
HashTable *create_normalized_classmap(HashTable *class_map);
218+
217219
extern const encode defaultEncoding[];
218220
extern int numDefaultEncodings;
219221

ext/soap/soap.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ 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+
service->class_map = create_normalized_classmap(Z_ARR_P(tmp));
820820
}
821821

822822
if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL &&
@@ -1982,7 +1982,7 @@ PHP_METHOD(SoapClient, __construct)
19821982
}
19831983
if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL &&
19841984
Z_TYPE_P(tmp) == IS_ARRAY) {
1985-
ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp);
1985+
ZVAL_ARR(Z_CLIENT_CLASSMAP_P(this_ptr), create_normalized_classmap(Z_ARR_P(tmp)));
19861986
}
19871987

19881988
if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL &&
@@ -4385,8 +4385,9 @@ static void delete_service(void *data) /* {{{ */
43854385
xmlCharEncCloseFunc(service->encoding);
43864386
}
43874387
if (service->class_map) {
4388-
zend_hash_destroy(service->class_map);
4389-
FREE_HASHTABLE(service->class_map);
4388+
zval zv;
4389+
ZVAL_ARR(&zv, service->class_map);
4390+
zval_ptr_dtor(&zv);
43904391
}
43914392
zval_ptr_dtor(&service->soap_object);
43924393
efree(service);

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)