Skip to content

Commit 75093a2

Browse files
committed
Fix GH-21421: SoapClient typemap property breaks engine assumptions
The conversion away from resources introduced the contents of the typemap property, which internally uses IS_PTR zvals. These should never be exposed because to userland they break engine assumptions. To solve this, we hide this in an internal field. We also disable cloning in the process which is broken in most cases because it doesn't clone internal data.
1 parent 284fd77 commit 75093a2

5 files changed

Lines changed: 156 additions & 45 deletions

File tree

ext/soap/php_soap.h

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -223,35 +223,34 @@ static zend_always_inline zval *php_soap_deref(zval *zv) {
223223
#define Z_CLIENT_TRACE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 4))
224224
#define Z_CLIENT_COMPRESSION_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 5))
225225
#define Z_CLIENT_SDL_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 6))
226-
#define Z_CLIENT_TYPEMAP_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 7))
227-
#define Z_CLIENT_HTTPSOCKET_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 8))
228-
#define Z_CLIENT_HTTPURL_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 9))
229-
#define Z_CLIENT_LOGIN_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 10))
230-
#define Z_CLIENT_PASSWORD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 11))
231-
#define Z_CLIENT_USE_DIGEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 12))
232-
#define Z_CLIENT_DIGEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 13))
233-
#define Z_CLIENT_PROXY_HOST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 14))
234-
#define Z_CLIENT_PROXY_PORT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 15))
235-
#define Z_CLIENT_PROXY_LOGIN_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 16))
236-
#define Z_CLIENT_PROXY_PASSWORD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 17))
237-
#define Z_CLIENT_EXCEPTIONS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 18))
238-
#define Z_CLIENT_ENCODING_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 19))
239-
#define Z_CLIENT_CLASSMAP_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 20))
240-
#define Z_CLIENT_FEATURES_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 21))
241-
#define Z_CLIENT_CONNECTION_TIMEOUT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 22))
242-
#define Z_CLIENT_STREAM_CONTEXT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 23))
243-
#define Z_CLIENT_USER_AGENT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 24))
244-
#define Z_CLIENT_KEEP_ALIVE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 25))
245-
#define Z_CLIENT_SSL_METHOD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 26))
246-
#define Z_CLIENT_SOAP_VERSION_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 27))
247-
#define Z_CLIENT_USE_PROXY_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 28))
248-
#define Z_CLIENT_COOKIES_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 29))
249-
#define Z_CLIENT_DEFAULT_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 30))
250-
#define Z_CLIENT_SOAP_FAULT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 31))
251-
#define Z_CLIENT_LAST_REQUEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 32))
252-
#define Z_CLIENT_LAST_RESPONSE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 33))
253-
#define Z_CLIENT_LAST_REQUEST_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 34))
254-
#define Z_CLIENT_LAST_RESPONSE_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 35))
226+
#define Z_CLIENT_HTTPSOCKET_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 7))
227+
#define Z_CLIENT_HTTPURL_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 8))
228+
#define Z_CLIENT_LOGIN_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 9))
229+
#define Z_CLIENT_PASSWORD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 10))
230+
#define Z_CLIENT_USE_DIGEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 11))
231+
#define Z_CLIENT_DIGEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 12))
232+
#define Z_CLIENT_PROXY_HOST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 13))
233+
#define Z_CLIENT_PROXY_PORT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 14))
234+
#define Z_CLIENT_PROXY_LOGIN_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 15))
235+
#define Z_CLIENT_PROXY_PASSWORD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 16))
236+
#define Z_CLIENT_EXCEPTIONS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 17))
237+
#define Z_CLIENT_ENCODING_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 18))
238+
#define Z_CLIENT_CLASSMAP_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 19))
239+
#define Z_CLIENT_FEATURES_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 20))
240+
#define Z_CLIENT_CONNECTION_TIMEOUT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 21))
241+
#define Z_CLIENT_STREAM_CONTEXT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 22))
242+
#define Z_CLIENT_USER_AGENT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 23))
243+
#define Z_CLIENT_KEEP_ALIVE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 24))
244+
#define Z_CLIENT_SSL_METHOD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 25))
245+
#define Z_CLIENT_SOAP_VERSION_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 26))
246+
#define Z_CLIENT_USE_PROXY_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 27))
247+
#define Z_CLIENT_COOKIES_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 28))
248+
#define Z_CLIENT_DEFAULT_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 29))
249+
#define Z_CLIENT_SOAP_FAULT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 30))
250+
#define Z_CLIENT_LAST_REQUEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 31))
251+
#define Z_CLIENT_LAST_RESPONSE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 32))
252+
#define Z_CLIENT_LAST_REQUEST_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 33))
253+
#define Z_CLIENT_LAST_RESPONSE_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 34))
255254

256255
typedef struct soap_url_object {
257256
php_url *url;

ext/soap/soap.c

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ zend_class_entry* soap_var_class_entry;
185185
zend_class_entry *soap_url_class_entry;
186186
zend_class_entry *soap_sdl_class_entry;
187187

188+
static zend_object_handlers soap_client_object_handlers;
188189
static zend_object_handlers soap_server_object_handlers;
189190
static zend_object_handlers soap_url_object_handlers;
190191
static zend_object_handlers soap_sdl_object_handlers;
@@ -194,10 +195,36 @@ typedef struct {
194195
zend_object std;
195196
} soap_server_object;
196197

198+
typedef struct {
199+
HashTable *typemap;
200+
zend_object std;
201+
} soap_client_object;
202+
203+
static inline soap_client_object *soap_client_object_fetch(zend_object *obj) {
204+
return (soap_client_object *) ((char *) obj - XtOffsetOf(soap_client_object, std));
205+
}
206+
197207
static inline soap_server_object *soap_server_object_fetch(zend_object *obj) {
198208
return (soap_server_object *) ((char *) obj - XtOffsetOf(soap_server_object, std));
199209
}
200210

211+
static zend_object *soap_client_object_create(zend_class_entry *ce)
212+
{
213+
soap_client_object *obj = zend_object_alloc(sizeof(soap_client_object), ce);
214+
zend_object_std_init(&obj->std, ce);
215+
object_properties_init(&obj->std, ce);
216+
return &obj->std;
217+
}
218+
219+
static void soap_client_object_free(zend_object *obj) {
220+
soap_client_object *client_obj = soap_client_object_fetch(obj);
221+
if (client_obj->typemap) {
222+
zend_hash_destroy(client_obj->typemap);
223+
FREE_HASHTABLE(client_obj->typemap);
224+
}
225+
zend_object_std_dtor(obj);
226+
}
227+
201228
static zend_object *soap_server_object_create(zend_class_entry *ce)
202229
{
203230
soap_server_object *obj = zend_object_alloc(sizeof(soap_server_object), ce);
@@ -496,6 +523,13 @@ PHP_MINIT_FUNCTION(soap)
496523

497524
/* Register SoapClient class */
498525
soap_class_entry = register_class_SoapClient();
526+
soap_class_entry->create_object = soap_client_object_create;
527+
soap_class_entry->default_object_handlers = &soap_client_object_handlers;
528+
529+
memcpy(&soap_client_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
530+
soap_client_object_handlers.offset = XtOffsetOf(soap_client_object, std);
531+
soap_client_object_handlers.free_obj = soap_client_object_free;
532+
soap_client_object_handlers.clone_obj = NULL;
499533

500534
/* Register SoapVar class */
501535
soap_var_class_entry = register_class_SoapVar();
@@ -1978,6 +2012,7 @@ PHP_FUNCTION(is_soap_fault)
19782012
/* SoapClient functions */
19792013

19802014
/* {{{ SoapClient constructor */
2015+
/* FIXME: double construct call will break this class */
19812016
PHP_METHOD(SoapClient, __construct)
19822017
{
19832018

@@ -2201,10 +2236,7 @@ PHP_METHOD(SoapClient, __construct)
22012236
}
22022237

22032238
if (typemap_ht) {
2204-
HashTable *typemap = soap_create_typemap(sdl, typemap_ht);
2205-
if (typemap) {
2206-
ZVAL_ARR(Z_CLIENT_TYPEMAP_P(this_ptr), typemap);
2207-
}
2239+
soap_client_object_fetch(Z_OBJ_P(this_ptr))->typemap = soap_create_typemap(sdl, typemap_ht);
22082240
}
22092241
SOAP_CLIENT_END_CODE();
22102242
}
@@ -2333,10 +2365,7 @@ static void do_soap_call(zend_execute_data *execute_data,
23332365
sdl = Z_SOAP_SDL_P(tmp)->sdl;
23342366
}
23352367

2336-
tmp = Z_CLIENT_TYPEMAP_P(this_ptr);
2337-
if (Z_TYPE_P(tmp) == IS_ARRAY) {
2338-
typemap = Z_ARR_P(tmp);
2339-
}
2368+
typemap = soap_client_object_fetch(Z_OBJ_P(this_ptr))->typemap;
23402369

23412370
clear_soap_fault(this_ptr);
23422371

ext/soap/soap.stub.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,6 @@ class SoapClient
540540
private bool $trace = false;
541541
private ?int $compression = null;
542542
private ?Soap\Sdl $sdl = null;
543-
private ?array $typemap = null;
544543
/** @var resource|null */
545544
private $httpsocket = null;
546545
private ?Soap\Url $httpurl = null;

ext/soap/soap_arginfo.h

Lines changed: 1 addition & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/soap/tests/bugs/gh21421.phpt

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
--TEST--
2+
GH-21421 (SoapClient typemap property breaks engine assumptions)
3+
--EXTENSIONS--
4+
soap
5+
--FILE--
6+
<?php
7+
$options = array(
8+
'uri' => 'http://schemas.nothing.com',
9+
'location' => 'test://',
10+
'typemap' => array(array("type_ns" => "http://schemas.nothing.com",
11+
"type_name" => "book",
12+
"from_xml" => "book_from_xml"))
13+
);
14+
$client = new SoapClient(NULL, $options);
15+
var_dump($client);
16+
?>
17+
--EXPECTF--
18+
object(SoapClient)#%d (35) {
19+
["uri":"SoapClient":private]=>
20+
string(26) "http://schemas.nothing.com"
21+
["style":"SoapClient":private]=>
22+
NULL
23+
["use":"SoapClient":private]=>
24+
NULL
25+
["location":"SoapClient":private]=>
26+
string(7) "test://"
27+
["trace":"SoapClient":private]=>
28+
bool(false)
29+
["compression":"SoapClient":private]=>
30+
NULL
31+
["sdl":"SoapClient":private]=>
32+
NULL
33+
["httpsocket":"SoapClient":private]=>
34+
NULL
35+
["httpurl":"SoapClient":private]=>
36+
NULL
37+
["_login":"SoapClient":private]=>
38+
NULL
39+
["_password":"SoapClient":private]=>
40+
NULL
41+
["_use_digest":"SoapClient":private]=>
42+
bool(false)
43+
["_digest":"SoapClient":private]=>
44+
NULL
45+
["_proxy_host":"SoapClient":private]=>
46+
NULL
47+
["_proxy_port":"SoapClient":private]=>
48+
NULL
49+
["_proxy_login":"SoapClient":private]=>
50+
NULL
51+
["_proxy_password":"SoapClient":private]=>
52+
NULL
53+
["_exceptions":"SoapClient":private]=>
54+
bool(true)
55+
["_encoding":"SoapClient":private]=>
56+
NULL
57+
["_classmap":"SoapClient":private]=>
58+
NULL
59+
["_features":"SoapClient":private]=>
60+
NULL
61+
["_connection_timeout":"SoapClient":private]=>
62+
int(0)
63+
["_stream_context":"SoapClient":private]=>
64+
resource(%d) of type (stream-context)
65+
["_user_agent":"SoapClient":private]=>
66+
NULL
67+
["_keep_alive":"SoapClient":private]=>
68+
bool(true)
69+
["_ssl_method":"SoapClient":private]=>
70+
NULL
71+
["_soap_version":"SoapClient":private]=>
72+
int(1)
73+
["_use_proxy":"SoapClient":private]=>
74+
NULL
75+
["_cookies":"SoapClient":private]=>
76+
array(0) {
77+
}
78+
["__default_headers":"SoapClient":private]=>
79+
NULL
80+
["__soap_fault":"SoapClient":private]=>
81+
NULL
82+
["__last_request":"SoapClient":private]=>
83+
NULL
84+
["__last_response":"SoapClient":private]=>
85+
NULL
86+
["__last_request_headers":"SoapClient":private]=>
87+
NULL
88+
["__last_response_headers":"SoapClient":private]=>
89+
NULL
90+
}

0 commit comments

Comments
 (0)