@@ -141,104 +141,81 @@ static bool dom_is_node_in_list(const zval *nodes, uint32_t nodesc, const xmlNod
141
141
return false;
142
142
}
143
143
144
- xmlNode * dom_zvals_to_fragment (php_libxml_ref_obj * document , xmlNode * contextNode , zval * nodes , uint32_t nodesc )
144
+ static xmlDocPtr dom_doc_from_context_node (xmlNodePtr contextNode )
145
+ {
146
+ if (contextNode -> type == XML_DOCUMENT_NODE || contextNode -> type == XML_HTML_DOCUMENT_NODE ) {
147
+ return (xmlDocPtr ) contextNode ;
148
+ } else {
149
+ return contextNode -> doc ;
150
+ }
151
+ }
152
+
153
+ xmlNode * dom_zvals_to_fragment (php_libxml_ref_obj * document , xmlNode * contextNode , zval * nodes , int nodesc )
145
154
{
146
155
xmlDoc * documentNode ;
147
156
xmlNode * fragment ;
148
157
xmlNode * newNode ;
149
- zend_class_entry * ce ;
150
158
dom_object * newNodeObj ;
151
- int stricterror ;
152
159
153
- if (document == NULL ) {
154
- php_dom_throw_error (HIERARCHY_REQUEST_ERR , 1 );
155
- return NULL ;
156
- }
157
-
158
- if (contextNode -> type == XML_DOCUMENT_NODE || contextNode -> type == XML_HTML_DOCUMENT_NODE ) {
159
- documentNode = (xmlDoc * ) contextNode ;
160
- } else {
161
- documentNode = contextNode -> doc ;
162
- }
160
+ documentNode = dom_doc_from_context_node (contextNode );
163
161
164
162
fragment = xmlNewDocFragment (documentNode );
165
163
166
164
if (!fragment ) {
167
165
return NULL ;
168
166
}
169
167
170
- stricterror = dom_get_strict_error (document );
171
-
172
168
for (uint32_t i = 0 ; i < nodesc ; i ++ ) {
173
169
if (Z_TYPE (nodes [i ]) == IS_OBJECT ) {
174
- ce = Z_OBJCE (nodes [i ]);
175
-
176
- if (instanceof_function (ce , dom_node_class_entry )) {
177
- newNodeObj = Z_DOMOBJ_P (& nodes [i ]);
178
- newNode = dom_object_get_node (newNodeObj );
170
+ newNodeObj = Z_DOMOBJ_P (& nodes [i ]);
171
+ newNode = dom_object_get_node (newNodeObj );
179
172
180
- if (newNode -> doc != documentNode ) {
181
- php_dom_throw_error (WRONG_DOCUMENT_ERR , stricterror );
182
- goto err ;
183
- }
173
+ if (newNode -> parent != NULL ) {
174
+ xmlUnlinkNode (newNode );
175
+ }
184
176
185
- if (newNode -> parent != NULL ) {
186
- xmlUnlinkNode (newNode );
187
- }
177
+ newNodeObj -> document = document ;
178
+ xmlSetTreeDoc (newNode , documentNode );
188
179
189
- newNodeObj -> document = document ;
190
- xmlSetTreeDoc (newNode , documentNode );
180
+ /* Citing from the docs (https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/libxml2-tree.html#xmlAddChild):
181
+ * "Add a new node to @parent, at the end of the child (or property) list merging adjacent TEXT nodes (in which case @cur is freed)".
182
+ * So we must take a copy if this situation arises to prevent a use-after-free. */
183
+ bool will_free = newNode -> type == XML_TEXT_NODE && fragment -> last && fragment -> last -> type == XML_TEXT_NODE ;
184
+ if (will_free ) {
185
+ newNode = xmlCopyNode (newNode , 1 );
186
+ }
191
187
192
- if (newNode -> type == XML_ATTRIBUTE_NODE ) {
193
- goto hierarchy_request_err ;
188
+ if (newNode -> type == XML_DOCUMENT_FRAG_NODE ) {
189
+ /* Unpack document fragment nodes, the behaviour differs for different libxml2 versions. */
190
+ newNode = newNode -> children ;
191
+ while (newNode ) {
192
+ xmlNodePtr next = newNode -> next ;
193
+ xmlUnlinkNode (newNode );
194
+ if (!xmlAddChild (fragment , newNode )) {
195
+ goto err ;
196
+ }
197
+ newNode = next ;
194
198
}
195
-
196
- /* Citing from the docs (https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/libxml2-tree.html#xmlAddChild):
197
- * "Add a new node to @parent, at the end of the child (or property) list merging adjacent TEXT nodes (in which case @cur is freed)".
198
- * So we must take a copy if this situation arises to prevent a use-after-free. */
199
- bool will_free = newNode -> type == XML_TEXT_NODE && fragment -> last && fragment -> last -> type == XML_TEXT_NODE ;
199
+ } else if (!xmlAddChild (fragment , newNode )) {
200
200
if (will_free ) {
201
- newNode = xmlCopyNode (newNode , 1 );
202
- }
203
-
204
- if (newNode -> type == XML_DOCUMENT_FRAG_NODE ) {
205
- /* Unpack document fragment nodes, the behaviour differs for different libxml2 versions. */
206
- newNode = newNode -> children ;
207
- while (newNode ) {
208
- xmlNodePtr next = newNode -> next ;
209
- xmlUnlinkNode (newNode );
210
- if (!xmlAddChild (fragment , newNode )) {
211
- goto hierarchy_request_err ;
212
- }
213
- newNode = next ;
214
- }
215
- } else if (!xmlAddChild (fragment , newNode )) {
216
- if (will_free ) {
217
- xmlFreeNode (newNode );
218
- }
219
- goto hierarchy_request_err ;
201
+ xmlFreeNode (newNode );
220
202
}
221
- } else {
222
- zend_argument_type_error (i + 1 , "must be of type DOMNode|string, %s given" , zend_zval_value_name (& nodes [i ]));
223
203
goto err ;
224
204
}
225
- } else if (Z_TYPE (nodes [i ]) == IS_STRING ) {
205
+ } else {
206
+ ZEND_ASSERT (Z_TYPE (nodes [i ]) == IS_STRING );
207
+
226
208
newNode = xmlNewDocText (documentNode , (xmlChar * ) Z_STRVAL (nodes [i ]));
227
209
228
210
if (!xmlAddChild (fragment , newNode )) {
229
211
xmlFreeNode (newNode );
230
- goto hierarchy_request_err ;
212
+ goto err ;
231
213
}
232
- } else {
233
- zend_argument_type_error (i + 1 , "must be of type DOMNode|string, %s given" , zend_zval_value_name (& nodes [i ]));
234
- goto err ;
235
214
}
236
215
}
237
216
238
217
return fragment ;
239
218
240
- hierarchy_request_err :
241
- php_dom_throw_error (HIERARCHY_REQUEST_ERR , stricterror );
242
219
err :
243
220
xmlFreeNode (fragment );
244
221
return NULL ;
@@ -261,17 +238,39 @@ static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fr
261
238
fragment -> last = NULL ;
262
239
}
263
240
264
- static zend_result dom_hierarchy_node_list ( xmlNodePtr parentNode , zval * nodes , uint32_t nodesc )
241
+ static zend_result dom_sanity_check_node_list_for_insertion ( php_libxml_ref_obj * document , xmlNodePtr parentNode , zval * nodes , int nodesc )
265
242
{
243
+ if (document == NULL ) {
244
+ php_dom_throw_error (HIERARCHY_REQUEST_ERR , 1 );
245
+ return FAILURE ;
246
+ }
247
+
248
+ xmlDocPtr documentNode = dom_doc_from_context_node (parentNode );
249
+
266
250
for (uint32_t i = 0 ; i < nodesc ; i ++ ) {
267
- if (Z_TYPE (nodes [i ]) == IS_OBJECT ) {
251
+ zend_uchar type = Z_TYPE (nodes [i ]);
252
+ if (type == IS_OBJECT ) {
268
253
const zend_class_entry * ce = Z_OBJCE (nodes [i ]);
269
254
270
255
if (instanceof_function (ce , dom_node_class_entry )) {
271
- if (dom_hierarchy (parentNode , dom_object_get_node (Z_DOMOBJ_P (nodes + i ))) != SUCCESS ) {
256
+ xmlNodePtr node = dom_object_get_node (Z_DOMOBJ_P (nodes + i ));
257
+
258
+ if (node -> doc != documentNode ) {
259
+ php_dom_throw_error (WRONG_DOCUMENT_ERR , dom_get_strict_error (document ));
260
+ return FAILURE ;
261
+ }
262
+
263
+ if (node -> type == XML_ATTRIBUTE_NODE || dom_hierarchy (parentNode , node ) != SUCCESS ) {
264
+ php_dom_throw_error (HIERARCHY_REQUEST_ERR , dom_get_strict_error (document ));
272
265
return FAILURE ;
273
266
}
267
+ } else {
268
+ zend_argument_type_error (i + 1 , "must be of type DOMNode|string, %s given" , zend_zval_type_name (& nodes [i ]));
269
+ return FAILURE ;
274
270
}
271
+ } else if (type != IS_STRING ) {
272
+ zend_argument_type_error (i + 1 , "must be of type DOMNode|string, %s given" , zend_zval_type_name (& nodes [i ]));
273
+ return FAILURE ;
275
274
}
276
275
}
277
276
@@ -283,8 +282,7 @@ void dom_parent_node_append(dom_object *context, zval *nodes, uint32_t nodesc)
283
282
xmlNode * parentNode = dom_object_get_node (context );
284
283
xmlNodePtr newchild , prevsib ;
285
284
286
- if (UNEXPECTED (dom_hierarchy_node_list (parentNode , nodes , nodesc ) != SUCCESS )) {
287
- php_dom_throw_error (HIERARCHY_REQUEST_ERR , dom_get_strict_error (context -> document ));
285
+ if (UNEXPECTED (dom_sanity_check_node_list_for_insertion (context -> document , parentNode , nodes , nodesc ) != SUCCESS )) {
288
286
return ;
289
287
}
290
288
@@ -328,8 +326,7 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc)
328
326
return ;
329
327
}
330
328
331
- if (UNEXPECTED (dom_hierarchy_node_list (parentNode , nodes , nodesc ) != SUCCESS )) {
332
- php_dom_throw_error (HIERARCHY_REQUEST_ERR , dom_get_strict_error (context -> document ));
329
+ if (UNEXPECTED (dom_sanity_check_node_list_for_insertion (context -> document , parentNode , nodes , nodesc ) != SUCCESS )) {
333
330
return ;
334
331
}
335
332
@@ -415,6 +412,10 @@ void dom_parent_node_after(dom_object *context, zval *nodes, uint32_t nodesc)
415
412
416
413
doc = prevsib -> doc ;
417
414
415
+ if (UNEXPECTED (dom_sanity_check_node_list_for_insertion (context -> document , parentNode , nodes , nodesc ) != SUCCESS )) {
416
+ return ;
417
+ }
418
+
418
419
php_libxml_invalidate_node_list_cache_from_doc (doc );
419
420
420
421
/* Spec step 4: convert nodes into fragment */
@@ -468,6 +469,10 @@ void dom_parent_node_before(dom_object *context, zval *nodes, uint32_t nodesc)
468
469
469
470
doc = nextsib -> doc ;
470
471
472
+ if (UNEXPECTED (dom_sanity_check_node_list_for_insertion (context -> document , parentNode , nodes , nodesc ) != SUCCESS )) {
473
+ return ;
474
+ }
475
+
471
476
php_libxml_invalidate_node_list_cache_from_doc (doc );
472
477
473
478
/* Spec step 4: convert nodes into fragment */
@@ -554,6 +559,10 @@ void dom_child_replace_with(dom_object *context, zval *nodes, uint32_t nodesc)
554
559
555
560
xmlNodePtr insertion_point = child -> next ;
556
561
562
+ if (UNEXPECTED (dom_sanity_check_node_list_for_insertion (context -> document , parentNode , nodes , nodesc ) != SUCCESS )) {
563
+ return ;
564
+ }
565
+
557
566
xmlNodePtr fragment = dom_zvals_to_fragment (context -> document , parentNode , nodes , nodesc );
558
567
if (UNEXPECTED (fragment == NULL )) {
559
568
return ;
@@ -586,8 +595,7 @@ void dom_parent_node_replace_children(dom_object *context, zval *nodes, uint32_t
586
595
587
596
xmlNodePtr thisp = dom_object_get_node (context );
588
597
/* Note: Only rule 2 of pre-insertion validity can be broken */
589
- if (dom_hierarchy_node_list (thisp , nodes , nodesc )) {
590
- php_dom_throw_error (HIERARCHY_REQUEST_ERR , dom_get_strict_error (context -> document ));
598
+ if (UNEXPECTED (dom_sanity_check_node_list_for_insertion (context -> document , thisp , nodes , nodesc ) != SUCCESS )) {
591
599
return ;
592
600
}
593
601
0 commit comments