From 1abf2967f955858764a6de5d7b7fe247cb637853 Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Thu, 6 Aug 2020 17:51:57 +0200
Subject: [PATCH 109/139] Fix exponential runtime and memory in xi:fallback
 processing

When creating XML_XINCLUDE_START nodes, the children of the original
xi:include node must be freed, otherwise fallback content is copied
twice, doubling runtime and memory consumption for each nested
xi:fallback/xi:include pair.

Found with libFuzzer.
---
 result/XInclude/fallback5.xml     |  51 +++++++++++++++++
 result/XInclude/fallback5.xml.rdr | 116 ++++++++++++++++++++++++++++++++++++++
 test/XInclude/docs/fallback5.xml  |  83 +++++++++++++++++++++++++++
 xinclude.c                        |   8 +++
 4 files changed, 258 insertions(+)
 create mode 100644 result/XInclude/fallback5.xml
 create mode 100644 result/XInclude/fallback5.xml.rdr
 create mode 100644 test/XInclude/docs/fallback5.xml

diff --git a/result/XInclude/fallback5.xml b/result/XInclude/fallback5.xml
new file mode 100644
index 0000000..0ba503d
--- /dev/null
+++ b/result/XInclude/fallback5.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<a>
+ 
+   <elem/>
+   
+     <elem/>
+     
+       <elem/>
+       
+         <elem/>
+         
+           <elem/>
+           
+             <elem/>
+             
+               <elem/>
+               
+                 <elem/>
+                 
+                   <elem/>
+                   
+                     <elem/>
+                     
+                       <elem/>
+                       
+                         <elem/>
+                         
+                           <elem/>
+                           
+                             <elem/>
+                             
+                               <elem/>
+                               
+                                 <elem/>
+                                
+                              
+                            
+                          
+                        
+                      
+                    
+                  
+                
+              
+            
+          
+        
+      
+    
+  
+</a>
diff --git a/result/XInclude/fallback5.xml.rdr b/result/XInclude/fallback5.xml.rdr
new file mode 100644
index 0000000..0e1dab7
--- /dev/null
+++ b/result/XInclude/fallback5.xml.rdr
@@ -0,0 +1,116 @@
+0 1 a 0 0
+1 14 #text 0 1 
+ 
+1 14 #text 0 1 
+   
+1 1 elem 1 0
+1 14 #text 0 1 
+   
+1 14 #text 0 1 
+     
+1 1 elem 1 0
+1 14 #text 0 1 
+     
+1 14 #text 0 1 
+       
+1 1 elem 1 0
+1 14 #text 0 1 
+       
+1 14 #text 0 1 
+         
+1 1 elem 1 0
+1 14 #text 0 1 
+         
+1 14 #text 0 1 
+           
+1 1 elem 1 0
+1 14 #text 0 1 
+           
+1 14 #text 0 1 
+             
+1 1 elem 1 0
+1 14 #text 0 1 
+             
+1 14 #text 0 1 
+               
+1 1 elem 1 0
+1 14 #text 0 1 
+               
+1 14 #text 0 1 
+                 
+1 1 elem 1 0
+1 14 #text 0 1 
+                 
+1 14 #text 0 1 
+                   
+1 1 elem 1 0
+1 14 #text 0 1 
+                   
+1 14 #text 0 1 
+                     
+1 1 elem 1 0
+1 14 #text 0 1 
+                     
+1 14 #text 0 1 
+                       
+1 1 elem 1 0
+1 14 #text 0 1 
+                       
+1 14 #text 0 1 
+                         
+1 1 elem 1 0
+1 14 #text 0 1 
+                         
+1 14 #text 0 1 
+                           
+1 1 elem 1 0
+1 14 #text 0 1 
+                           
+1 14 #text 0 1 
+                             
+1 1 elem 1 0
+1 14 #text 0 1 
+                             
+1 14 #text 0 1 
+                               
+1 1 elem 1 0
+1 14 #text 0 1 
+                               
+1 14 #text 0 1 
+                                 
+1 1 elem 1 0
+1 14 #text 0 1 
+                                
+1 14 #text 0 1 
+                              
+1 14 #text 0 1 
+                            
+1 14 #text 0 1 
+                          
+1 14 #text 0 1 
+                        
+1 14 #text 0 1 
+                      
+1 14 #text 0 1 
+                    
+1 14 #text 0 1 
+                  
+1 14 #text 0 1 
+                
+1 14 #text 0 1 
+              
+1 14 #text 0 1 
+            
+1 14 #text 0 1 
+          
+1 14 #text 0 1 
+        
+1 14 #text 0 1 
+      
+1 14 #text 0 1 
+    
+1 14 #text 0 1 
+  
+1 14 #text 0 1 
+
+0 15 a 0 0
diff --git a/test/XInclude/docs/fallback5.xml b/test/XInclude/docs/fallback5.xml
new file mode 100644
index 0000000..d3ad424
--- /dev/null
+++ b/test/XInclude/docs/fallback5.xml
@@ -0,0 +1,83 @@
+<a>
+ <xi:include href="a01.xml" xmlns:xi="http://www.w3.org/2001/XInclude">
+  <xi:fallback>
+   <elem/>
+   <xi:include href="a02.xml">
+    <xi:fallback>
+     <elem/>
+     <xi:include href="a03.xml">
+      <xi:fallback>
+       <elem/>
+       <xi:include href="a04.xml">
+        <xi:fallback>
+         <elem/>
+         <xi:include href="a05.xml">
+          <xi:fallback>
+           <elem/>
+           <xi:include href="a06.xml">
+            <xi:fallback>
+             <elem/>
+             <xi:include href="a07.xml">
+              <xi:fallback>
+               <elem/>
+               <xi:include href="a08.xml">
+                <xi:fallback>
+                 <elem/>
+                 <xi:include href="a09.xml">
+                  <xi:fallback>
+                   <elem/>
+                   <xi:include href="a10.xml">
+                    <xi:fallback>
+                     <elem/>
+                     <xi:include href="a11.xml">
+                      <xi:fallback>
+                       <elem/>
+                       <xi:include href="a12.xml">
+                        <xi:fallback>
+                         <elem/>
+                         <xi:include href="a13.xml">
+                          <xi:fallback>
+                           <elem/>
+                           <xi:include href="a14.xml">
+                            <xi:fallback>
+                             <elem/>
+                             <xi:include href="a15.xml">
+                              <xi:fallback>
+                               <elem/>
+                               <xi:include href="a16.xml">
+                                <xi:fallback>
+                                 <elem/>
+                                </xi:fallback>
+                               </xi:include>
+                              </xi:fallback>
+                             </xi:include>
+                            </xi:fallback>
+                           </xi:include>
+                          </xi:fallback>
+                         </xi:include>
+                        </xi:fallback>
+                       </xi:include>
+                      </xi:fallback>
+                     </xi:include>
+                    </xi:fallback>
+                   </xi:include>
+                  </xi:fallback>
+                 </xi:include>
+                </xi:fallback>
+               </xi:include>
+              </xi:fallback>
+             </xi:include>
+            </xi:fallback>
+           </xi:include>
+          </xi:fallback>
+         </xi:include>
+        </xi:fallback>
+       </xi:include>
+      </xi:fallback>
+     </xi:include>
+    </xi:fallback>
+   </xi:include>
+  </xi:fallback>
+ </xi:include>
+</a>
+
diff --git a/xinclude.c b/xinclude.c
index 2917d45..5ea87ad 100644
--- a/xinclude.c
+++ b/xinclude.c
@@ -2260,11 +2260,19 @@ xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) {
 	xmlUnlinkNode(cur);
 	xmlFreeNode(cur);
     } else {
+        xmlNodePtr child, next;
+
 	/*
 	 * Change the current node as an XInclude start one, and add an
 	 * XInclude end one
 	 */
 	cur->type = XML_XINCLUDE_START;
+        /* Remove fallback children */
+        for (child = cur->children; child != NULL; child = next) {
+            next = child->next;
+            xmlUnlinkNode(child);
+            xmlFreeNode(child);
+        }
 	end = xmlNewDocNode(cur->doc, cur->ns, cur->name, NULL);
 	if (end == NULL) {
 	    xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref,
-- 
1.8.3.1

