From e1744e11b1c2b36f91a8847b61bafb8c5e7407ae Mon Sep 17 00:00:00 2001
From: nickc <nickc@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Fri, 7 Dec 2018 10:33:30 +0000
Subject: [PATCH] Add a recursion limit to libiberty's demangling code. The
 limit is enabled by default, but can be disabled via a new demangling option.

include	* demangle.h (DMGL_NO_RECURSE_LIMIT): Define.
        (DEMANGLE_RECURSION_LIMIT): Define

	PR 87681
	PR 87675
	PR 87636
	PR 87350
	PR 87335
libiberty * cp-demangle.h (struct d_info): Add recursion_level field.
	* cp-demangle.c (d_function_type): Add recursion counter.
	If the recursion limit is reached and the check is not disabled,
	then return with a failure result.
	(cplus_demangle_init_info): Initialise the recursion_level field.
        (d_demangle_callback): If the recursion limit is enabled, check
	for a mangled string that is so long that there is not enough
	stack space for the local arrays.
        * cplus-dem.c (struct work): Add recursion_level field.
	(squangle_mop_up): Set the numb and numk fields to zero.
	(work_stuff_copy_to_from): Handle the case where a btypevec or
	ktypevec field is NULL.
	(demangle_nested_args): Add recursion counter.  If
	the recursion limit is not disabled and reached, return with a
	failure result.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@266886 138bc75d-0d04-0410-961f-82ee72b054a4

CVE: CVE-2018-18484
Upstream-Status: Backport [https://github.com/gcc-mirror/gcc/commit/03e51746ed98d9106803f6009ebd71ea670ad3b9]
Signed-off-by: Anuj Mittal <anuj.mittal@intel.com>
---
 include/ChangeLog       |  5 +++++
 include/demangle.h      | 11 +++++++++++
 libiberty/ChangeLog     | 23 ++++++++++++++++++++++
 libiberty/cp-demangle.c | 51 ++++++++++++++++++++++++++++++++++++++-----------
 libiberty/cp-demangle.h |  3 +++
 libiberty/cplus-dem.c   | 37 +++++++++++++++++++++++++++++++++--
 6 files changed, 117 insertions(+), 13 deletions(-)

diff --git a/include/ChangeLog b/include/ChangeLog
index 02ab336..88b0648 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -2,6 +2,11 @@
 
 	* GCC 8.3.0 released.
 
+2018-12-07  Nick Clifton  <nickc@redhat.com>
+
+	* demangle.h (DMGL_NO_RECURSE_LIMIT): Define.
+	(DEMANGLE_RECURSION_LIMIT): Define
+
 2018-07-26  Release Manager
 
 	* GCC 8.2.0 released.
diff --git a/include/demangle.h b/include/demangle.h
index b8d57cf..9bb8a19 100644
--- a/include/demangle.h
+++ b/include/demangle.h
@@ -68,6 +68,17 @@ extern "C" {
 /* If none of these are set, use 'current_demangling_style' as the default. */
 #define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM|DMGL_HP|DMGL_EDG|DMGL_GNU_V3|DMGL_JAVA|DMGL_GNAT|DMGL_DLANG|DMGL_RUST)
 
+/* Disable a limit on the depth of recursion in mangled strings.
+   Note if this limit is disabled then stack exhaustion is possible when
+   demangling pathologically complicated strings.  Bug reports about stack
+   exhaustion when the option is enabled will be rejected.  */  
+#define DMGL_NO_RECURSE_LIMIT (1 << 18)	
+
+/* If DMGL_NO_RECURSE_LIMIT is not enabled, then this is the value used as
+   the maximum depth of recursion allowed.  It should be enough for any
+   real-world mangled name.  */
+#define DEMANGLE_RECURSION_LIMIT 1024
+  
 /* Enumeration of possible demangling styles.
 
    Lucid and ARM styles are still kept logically distinct, even though
diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog
index 1dd05da..2f77038 100644
--- a/libiberty/ChangeLog
+++ b/libiberty/ChangeLog
@@ -9,6 +9,29 @@
 	(simple_object_copy_lto_debug_sections): Create file in binary
 	mode.
 
+2018-12-07  Nick Clifton  <nickc@redhat.com>
+
+	PR 87681
+	PR 87675
+	PR 87636
+	PR 87350
+	PR 87335
+	* cp-demangle.h (struct d_info): Add recursion_level field.
+	* cp-demangle.c (d_function_type): Add recursion counter.
+	If the recursion limit is reached and the check is not disabled,
+	then return with a failure result.
+	(cplus_demangle_init_info): Initialise the recursion_level field.
+	 (d_demangle_callback): If the recursion limit is enabled, check
+	for a mangled string that is so long that there is not enough
+	stack space for the local arrays.
+	 * cplus-dem.c (struct work): Add recursion_level field.
+	(squangle_mop_up): Set the numb and numk fields to zero.
+	(work_stuff_copy_to_from): Handle the case where a btypevec or
+	ktypevec field is NULL.
+	(demangle_nested_args): Add recursion counter.  If
+	the recursion limit is not disabled and reached, return with a
+	failure result.
+
 2018-07-26  Release Manager
 
 	* GCC 8.2.0 released.
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 3f2a097..c374e46 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -2843,21 +2843,35 @@ d_ref_qualifier (struct d_info *di, struct demangle_component *sub)
 static struct demangle_component *
 d_function_type (struct d_info *di)
 {
-  struct demangle_component *ret;
+  struct demangle_component *ret = NULL;
 
-  if (! d_check_char (di, 'F'))
-    return NULL;
-  if (d_peek_char (di) == 'Y')
+  if ((di->options & DMGL_NO_RECURSE_LIMIT) == 0)
     {
-      /* Function has C linkage.  We don't print this information.
-	 FIXME: We should print it in verbose mode.  */
-      d_advance (di, 1);
+      if (di->recursion_level > DEMANGLE_RECURSION_LIMIT)
+	/* FIXME: There ought to be a way to report
+	   that the recursion limit has been reached.  */
+	return NULL;
+
+      di->recursion_level ++;
     }
-  ret = d_bare_function_type (di, 1);
-  ret = d_ref_qualifier (di, ret);
 
-  if (! d_check_char (di, 'E'))
-    return NULL;
+  if (d_check_char (di, 'F'))
+    {
+      if (d_peek_char (di) == 'Y')
+	{
+	  /* Function has C linkage.  We don't print this information.
+	     FIXME: We should print it in verbose mode.  */
+	  d_advance (di, 1);
+	}
+      ret = d_bare_function_type (di, 1);
+      ret = d_ref_qualifier (di, ret);
+      
+      if (! d_check_char (di, 'E'))
+	ret = NULL;
+    }
+
+  if ((di->options & DMGL_NO_RECURSE_LIMIT) == 0)
+    di->recursion_level --;
   return ret;
 }
 
@@ -6188,6 +6202,7 @@ cplus_demangle_init_info (const char *mangled, int options, size_t len,
   di->expansion = 0;
   di->is_expression = 0;
   di->is_conversion = 0;
+  di->recursion_level = 0;
 }
 
 /* Internal implementation for the demangler.  If MANGLED is a g++ v3 ABI
@@ -6227,6 +6242,20 @@ d_demangle_callback (const char *mangled, int options,
 
   cplus_demangle_init_info (mangled, options, strlen (mangled), &di);
 
+  /* PR 87675 - Check for a mangled string that is so long
+     that we do not have enough stack space to demangle it.  */
+  if (((options & DMGL_NO_RECURSE_LIMIT) == 0)
+      /* This check is a bit arbitrary, since what we really want to do is to
+	 compare the sizes of the di.comps and di.subs arrays against the
+	 amount of stack space remaining.  But there is no portable way to do
+	 this, so instead we use the recursion limit as a guide to the maximum
+	 size of the arrays.  */
+      && (unsigned long) di.num_comps > DEMANGLE_RECURSION_LIMIT)
+    {
+      /* FIXME: We need a way to indicate that a stack limit has been reached.  */
+      return 0;
+    }
+
   {
 #ifdef CP_DYNAMIC_ARRAYS
     __extension__ struct demangle_component comps[di.num_comps];
diff --git a/libiberty/cp-demangle.h b/libiberty/cp-demangle.h
index 51b8a24..d87a830 100644
--- a/libiberty/cp-demangle.h
+++ b/libiberty/cp-demangle.h
@@ -122,6 +122,9 @@ struct d_info
   /* Non-zero if we are parsing the type operand of a conversion
      operator, but not when in an expression.  */
   int is_conversion;
+  /* If DMGL_NO_RECURSE_LIMIT is not active then this is set to
+     the current recursion level.  */
+  unsigned int recursion_level;
 };
 
 /* To avoid running past the ending '\0', don't:
diff --git a/libiberty/cplus-dem.c b/libiberty/cplus-dem.c
index 6d58bd8..8b9646f 100644
--- a/libiberty/cplus-dem.c
+++ b/libiberty/cplus-dem.c
@@ -146,6 +146,7 @@ struct work_stuff
   int *proctypevec;     /* Indices of currently processed remembered typevecs.  */
   int proctypevec_size;
   int nproctypes;
+  unsigned int recursion_level;
 };
 
 #define PRINT_ANSI_QUALIFIERS (work -> options & DMGL_ANSI)
@@ -1292,12 +1293,14 @@ squangle_mop_up (struct work_stuff *work)
       free ((char *) work -> btypevec);
       work->btypevec = NULL;
       work->bsize = 0;
+      work->numb = 0;
     }
   if (work -> ktypevec != NULL)
     {
       free ((char *) work -> ktypevec);
       work->ktypevec = NULL;
       work->ksize = 0;
+      work->numk = 0;
     }
 }
 
@@ -1331,8 +1334,15 @@ work_stuff_copy_to_from (struct work_stuff *to, struct work_stuff *from)
 
   for (i = 0; i < from->numk; i++)
     {
-      int len = strlen (from->ktypevec[i]) + 1;
+      int len;
+
+      if (from->ktypevec[i] == NULL)
+	{
+	  to->ktypevec[i] = NULL;
+	  continue;
+	}
 
+      len = strlen (from->ktypevec[i]) + 1;
       to->ktypevec[i] = XNEWVEC (char, len);
       memcpy (to->ktypevec[i], from->ktypevec[i], len);
     }
@@ -1342,8 +1352,15 @@ work_stuff_copy_to_from (struct work_stuff *to, struct work_stuff *from)
 
   for (i = 0; i < from->numb; i++)
     {
-      int len = strlen (from->btypevec[i]) + 1;
+      int len;
+
+      if (from->btypevec[i] == NULL)
+	{
+	  to->btypevec[i] = NULL;
+	  continue;
+	}
 
+      len = strlen (from->btypevec[i]) + 1;
       to->btypevec[i] = XNEWVEC (char , len);
       memcpy (to->btypevec[i], from->btypevec[i], len);
     }
@@ -1401,6 +1418,7 @@ delete_non_B_K_work_stuff (struct work_stuff *work)
 
       free ((char*) work->tmpl_argvec);
       work->tmpl_argvec = NULL;
+      work->ntmpl_args = 0;
     }
   if (work->previous_argument)
     {
@@ -4477,6 +4495,7 @@ remember_Btype (struct work_stuff *work, const char *start,
 }
 
 /* Lose all the info related to B and K type codes. */
+
 static void
 forget_B_and_K_types (struct work_stuff *work)
 {
@@ -4502,6 +4521,7 @@ forget_B_and_K_types (struct work_stuff *work)
 	}
     }
 }
+
 /* Forget the remembered types, but not the type vector itself.  */
 
 static void
@@ -4696,6 +4716,16 @@ demangle_nested_args (struct work_stuff *work, const char **mangled,
   int result;
   int saved_nrepeats;
 
+  if ((work->options & DMGL_NO_RECURSE_LIMIT) == 0)
+    {
+      if (work->recursion_level > DEMANGLE_RECURSION_LIMIT)
+	/* FIXME: There ought to be a way to report
+	   that the recursion limit has been reached.  */
+	return 0;
+
+      work->recursion_level ++;
+    }
+
   /* The G++ name-mangling algorithm does not remember types on nested
      argument lists, unless -fsquangling is used, and in that case the
      type vector updated by remember_type is not used.  So, we turn
@@ -4722,6 +4752,9 @@ demangle_nested_args (struct work_stuff *work, const char **mangled,
   --work->forgetting_types;
   work->nrepeats = saved_nrepeats;
 
+  if ((work->options & DMGL_NO_RECURSE_LIMIT) == 0)
+    --work->recursion_level;
+
   return result;
 }
 
-- 
2.7.4

