Skip to content

Commit 8f9a8c0

Browse files
committed
Fix phpGH-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. Closes phpGH-21422.
1 parent 2918cae commit 8f9a8c0

File tree

6 files changed

+158
-45
lines changed

6 files changed

+158
-45
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ PHP NEWS
104104
- Soap:
105105
. Soap::__setCookie() when cookie name is a digit is now not stored and
106106
represented as a string anymore but a int. (David Carlier)
107+
. Fixed bug GH-21421 (SoapClient typemap property breaks engine assumptions).
108+
(ndossche)
107109

108110
- Sockets:
109111
. Added the TCP_USER_TIMEOUT constant for Linux to set the maximum time in

ext/soap/php_soap.h

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

259258
typedef struct soap_url_object {
260259
php_uri *uri;

ext/soap/soap.c

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ zend_class_entry* soap_var_class_entry;
190190
zend_class_entry *soap_url_class_entry;
191191
zend_class_entry *soap_sdl_class_entry;
192192

193+
static zend_object_handlers soap_client_object_handlers;
193194
static zend_object_handlers soap_server_object_handlers;
194195
static zend_object_handlers soap_url_object_handlers;
195196
static zend_object_handlers soap_sdl_object_handlers;
@@ -201,10 +202,36 @@ typedef struct {
201202
zend_object std;
202203
} soap_server_object;
203204

205+
typedef struct {
206+
HashTable *typemap;
207+
zend_object std;
208+
} soap_client_object;
209+
210+
static inline soap_client_object *soap_client_object_fetch(zend_object *obj) {
211+
return (soap_client_object *) ((char *) obj - XtOffsetOf(soap_client_object, std));
212+
}
213+
204214
static inline soap_server_object *soap_server_object_fetch(zend_object *obj) {
205215
return (soap_server_object *) ((char *) obj - XtOffsetOf(soap_server_object, std));
206216
}
207217

218+
static zend_object *soap_client_object_create(zend_class_entry *ce)
219+
{
220+
soap_client_object *obj = zend_object_alloc(sizeof(soap_client_object), ce);
221+
zend_object_std_init(&obj->std, ce);
222+
object_properties_init(&obj->std, ce);
223+
return &obj->std;
224+
}
225+
226+
static void soap_client_object_free(zend_object *obj) {
227+
soap_client_object *client_obj = soap_client_object_fetch(obj);
228+
if (client_obj->typemap) {
229+
zend_hash_destroy(client_obj->typemap);
230+
FREE_HASHTABLE(client_obj->typemap);
231+
}
232+
zend_object_std_dtor(obj);
233+
}
234+
208235
static zend_object *soap_server_object_create(zend_class_entry *ce)
209236
{
210237
soap_server_object *obj = zend_object_alloc(sizeof(soap_server_object), ce);
@@ -503,6 +530,13 @@ PHP_MINIT_FUNCTION(soap)
503530

504531
/* Register SoapClient class */
505532
soap_class_entry = register_class_SoapClient();
533+
soap_class_entry->create_object = soap_client_object_create;
534+
soap_class_entry->default_object_handlers = &soap_client_object_handlers;
535+
536+
memcpy(&soap_client_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
537+
soap_client_object_handlers.offset = XtOffsetOf(soap_client_object, std);
538+
soap_client_object_handlers.free_obj = soap_client_object_free;
539+
soap_client_object_handlers.clone_obj = NULL;
506540

507541
/* Register SoapVar class */
508542
soap_var_class_entry = register_class_SoapVar();
@@ -1993,6 +2027,7 @@ PHP_FUNCTION(is_soap_fault)
19932027
/* SoapClient functions */
19942028

19952029
/* {{{ SoapClient constructor */
2030+
/* FIXME: double construct call will break this class */
19962031
PHP_METHOD(SoapClient, __construct)
19972032
{
19982033

@@ -2216,10 +2251,7 @@ PHP_METHOD(SoapClient, __construct)
22162251
}
22172252

22182253
if (typemap_ht) {
2219-
HashTable *typemap = soap_create_typemap(sdl, typemap_ht);
2220-
if (typemap) {
2221-
ZVAL_ARR(Z_CLIENT_TYPEMAP_P(this_ptr), typemap);
2222-
}
2254+
soap_client_object_fetch(Z_OBJ_P(this_ptr))->typemap = soap_create_typemap(sdl, typemap_ht);
22232255
}
22242256
SOAP_CLIENT_END_CODE();
22252257
}
@@ -2347,10 +2379,7 @@ static void do_soap_call(zend_execute_data *execute_data,
23472379
sdl = Z_SOAP_SDL_P(tmp)->sdl;
23482380
}
23492381

2350-
tmp = Z_CLIENT_TYPEMAP_P(this_ptr);
2351-
if (Z_TYPE_P(tmp) == IS_ARRAY) {
2352-
typemap = Z_ARR_P(tmp);
2353-
}
2382+
typemap = soap_client_object_fetch(Z_OBJ_P(this_ptr))->typemap;
23542383

23552384
clear_soap_fault(this_ptr);
23562385

ext/soap/soap.stub.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,6 @@ class SoapClient
541541
private bool $trace = false;
542542
private ?int $compression = null;
543543
private ?Soap\Sdl $sdl = null;
544-
private ?array $typemap = null;
545544
/** @var resource|null */
546545
private $httpsocket = null;
547546
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)