diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 4eedbffc5a4b..7f845c956ed3 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -2312,13 +2312,15 @@ static xmlNodePtr to_xml_array(encodeTypePtr type, zval *data, int style, xmlNod if (EG(exception)) { goto iterator_done; } - array_set_zval_key(Z_ARRVAL(array_copy), &key, val); - zval_ptr_dtor(val); + zend_result status = array_set_zval_key(Z_ARRVAL(array_copy), &key, val); zval_ptr_dtor(&key); + if (status == FAILURE) { + goto iterator_done; + } } else { + Z_TRY_ADDREF_P(val); add_next_index_zval(&array_copy, val); } - Z_TRY_ADDREF_P(val); iter->funcs->move_forward(iter); if (EG(exception)) { diff --git a/ext/soap/tests/bugs/gh22895.phpt b/ext/soap/tests/bugs/gh22895.phpt new file mode 100644 index 000000000000..1d60935707c1 --- /dev/null +++ b/ext/soap/tests/bugs/gh22895.phpt @@ -0,0 +1,77 @@ +--TEST-- +GH-22895 (Heap use-after-free while encoding a Traversable with an illegal key) +--CREDITS-- +Amorsec +--EXTENSIONS-- +soap +--FILE-- +i++; + } + + public function rewind(): void + { + $this->i = 0; + } + + public function valid(): bool + { + return $this->i < 2; + } +} + +$client = new LocalSoapClient(null, [ + 'location' => 'http://127.0.0.1/', + 'uri' => 'urn:audit', + 'trace' => 1, +]); + +$multiple = new MultipleIterator(); +$multiple->attachIterator(new ArrayIterator([0])); + +foreach ([$multiple, new ArrayKeyIterator()] as $iterator) { + try { + $client->__soapCall('audit', [new SoapVar($iterator, SOAP_ENC_ARRAY)]); + } catch (TypeError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } +} + +/* A key the encoder can use must still be serialized, without leaking. */ +$client->__soapCall('audit', [new SoapVar(new ArrayIterator(['a' => 1]), SOAP_ENC_ARRAY)]); +echo $client->__getLastRequest(); +?> +--EXPECT-- +TypeError: Cannot access offset of type array on array +TypeError: Cannot access offset of type array on array + +1