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