diff mbox

Fix for PR70498 in Libiberty Demangler

Message ID 5C58DC48-8E92-4581-A1DC-1491A3DC7CEB@gmail.com
State New
Headers show

Commit Message

Marcel Böhme April 2, 2016, 9:49 a.m. UTC
> On 2 Apr 2016, at 1:44 AM, Bernd Schmidt <bschmidt@redhat.com> wrote:
> 
> On 04/01/2016 07:41 PM, Pedro Alves wrote:
>> On 04/01/2016 11:21 AM, Marcel Böhme wrote:
>>>  static inline void
>>> -d_append_num (struct d_print_info *dpi, long l)
>>> +d_append_num (struct d_print_info *dpi, int l)
>>>  {
>>>    char buf[25];
>>>    sprintf (buf,"%ld", l);
>> 
>> Doesn't this warn about %ld format vs int type mismatch?
> 
> Well spotted. Marcel, please correct this issue and check for other warnings. Unless libiberty is somehow exempt from -Werror, this should have shown up in a bootstrap.

Bootstrapped and regression tested on x86_64-pc-linux-gnu. Test cases added + checked PR70498 is resolved. 

The patch now also accounts for overflows in d_compact_number which is supposed to return -1 in case of negative numbers.

Thanks,
- Marcel

--

Comments

Bernd Schmidt April 4, 2016, 1:24 p.m. UTC | #1
On 04/02/2016 11:49 AM, Marcel Böhme wrote:
>
> Bootstrapped and regression tested on x86_64-pc-linux-gnu. Test cases
> added + checked PR70498 is resolved.

Richard - any objections from an RM perspective to check in such
potentially security-related fixes now even if not regressions?

> The patch now also accounts for overflows in d_compact_number which
> is supposed to return -1 in case of negative numbers.

I take it this isn't for the normal 'n' case, but for instances where we 
encounter overflows in d_number?


Bernd
Marcel Böhme April 4, 2016, 2:25 p.m. UTC | #2
> On 4 Apr 2016, at 9:24 PM, Bernd Schmidt <bschmidt@redhat.com> wrote:
> 
>> 
>> The patch now also accounts for overflows in d_compact_number which
>> is supposed to return -1 in case of negative numbers.
> 
> I take it this isn't for the normal 'n' case, but for instances where we encounter overflows in d_number?
Yes.

Best regards,
- Marcel
Bernd Schmidt April 8, 2016, 3:12 p.m. UTC | #3
I've been looking through this patch. I had intended to commit it, but 
after looking through it a little more carefully I think there are a few 
things left to solve.

So, d_number/d_compact_number now return ints rather than longs, which 
makes sense since the lengths in things like struct demangle_component's 
s_name are integers. However, s_number there is defined as a long, so 
this does mean a tighter limit for things like 
d_template_param/d_make_template_param. Cc'ing Jason for an opinion on 
whether that's a problem or not (I suspect it isn't - t).

> -static long
> +static int
>   d_compact_number (struct d_info *di)
>   {
> -  long num;
> +  int num;
>     if (d_peek_char (di) == '_')
>       num = 0;
>     else if (d_peek_char (di) == 'n')
> @@ -2957,7 +2957,7 @@ d_compact_number (struct d_info *di)
>     else
>       num = d_number (di) + 1;
>
> -  if (! d_check_char (di, '_'))
> +  if (num < 0 || ! d_check_char (di, '_'))
>       return -1;
>     return num;
>   }

Shouldn't we check for overflows before performing the +1 addition (i.e. 
0 <= num < INT_MAX)? Ideally we'd also have a way to signal from 
d_number if we had an overflow while parsing that number.

There's also this, in d_expression_1:

	  index = d_compact_number (di) + 1;
	  if (index == 0)
	    return NULL;

which probably ought to have the same kind of check (I'll note that at 
this point we've accumulated two "+1"s, I'll assume that's what we want).

Please include a ChangeLog entry with the next patch.


Bernd
Marcel Böhme April 13, 2016, 1:04 p.m. UTC | #4
Hi Bernd,

> -static long
> +static int
>  d_compact_number (struct d_info *di)
>  {
> -  long num;
> +  int num;
>    if (d_peek_char (di) == '_')
>      num = 0;
>    else if (d_peek_char (di) == 'n')
> @@ -2957,7 +2957,7 @@ d_compact_number (struct d_info *di)
>    else
>      num = d_number (di) + 1;
> 
> -  if (! d_check_char (di, '_'))
> +  if (num < 0 || ! d_check_char (di, '_'))
>      return -1;
>    return num;
>  }

> Shouldn't we check for overflows before performing the +1 addition (i.e. 0 <= num < INT_MAX)? Ideally we'd also have a way to signal from d_number if we had an overflow while parsing that number.
Without an overflow signal, d_number will already be prone to return a negative number for supposedly non-negative numbers (those not preceded with ’n’). In that case an overflow check would be unnecessary in d_compact_number which is supposed to always return a positive number or a negative one (-1). If you decide in favour of an overflow signal, it must be handled by the call-sites. Not sure what the “default behaviour” should be then. Otherwise, we can simply assume that the call sites for d_number can handle negative numbers.

Kindly advice.

> 
> There's also this, in d_expression_1:
> 
> 	  index = d_compact_number (di) + 1;
> 	  if (index == 0)
> 	    return NULL;
> 
> which probably ought to have the same kind of check (I'll note that at this point we've accumulated two "+1"s, I'll assume that's what we want).
Yes. There should be an overflow check here. 

Thanks!
- Marcel
Bernd Schmidt April 15, 2016, 11:38 a.m. UTC | #5
On 04/13/2016 03:04 PM, Marcel Böhme wrote:
> Hi Bernd,
>> Shouldn't we check for overflows before performing the +1 addition
>> (i.e. 0 <= num < INT_MAX)? Ideally we'd also have a way to signal
>> from d_number if we had an overflow while parsing that number.
> Without an overflow signal, d_number will already be prone to return
> a negative number for supposedly non-negative numbers (those not
> preceded with ’n’). In that case an overflow check would be
> unnecessary in d_compact_number which is supposed to always return a
> positive number or a negative one (-1). If you decide in favour of an
> overflow signal, it must be handled by the call-sites. Not sure what
> the “default behaviour” should be then. Otherwise, we can simply
> assume that the call sites for d_number can handle negative numbers.

Shouldn't we look into fixing d_number eventually so it can signal error?

>> index = d_compact_number (di) + 1; if (index == 0) return NULL;
>>
>> which probably ought to have the same kind of check (I'll note that
>> at this point we've accumulated two "+1"s, I'll assume that's what
>> we want).
> Yes. There should be an overflow check here.

Could you update the patch for that?


Bernd
diff mbox

Patch

Index: libiberty/cp-demangle.c
===================================================================
--- libiberty/cp-demangle.c	(revision 234663)
+++ libiberty/cp-demangle.c	(working copy)
@@ -398,7 +398,7 @@  d_make_dtor (struct d_info *, enum gnu_v
              struct demangle_component *);
 
 static struct demangle_component *
-d_make_template_param (struct d_info *, long);
+d_make_template_param (struct d_info *, int);
 
 static struct demangle_component *
 d_make_sub (struct d_info *, const char *, int);
@@ -421,9 +421,9 @@  static struct demangle_component *d_unqu
 
 static struct demangle_component *d_source_name (struct d_info *);
 
-static long d_number (struct d_info *);
+static int d_number (struct d_info *);
 
-static struct demangle_component *d_identifier (struct d_info *, long);
+static struct demangle_component *d_identifier (struct d_info *, int);
 
 static struct demangle_component *d_operator_name (struct d_info *);
 
@@ -1119,7 +1119,7 @@  d_make_dtor (struct d_info *di, enum gnu
 /* Add a new template parameter.  */
 
 static struct demangle_component *
-d_make_template_param (struct d_info *di, long i)
+d_make_template_param (struct d_info *di, int i)
 {
   struct demangle_component *p;
 
@@ -1135,7 +1135,7 @@  d_make_template_param (struct d_info *di
 /* Add a new function parameter.  */
 
 static struct demangle_component *
-d_make_function_param (struct d_info *di, long i)
+d_make_function_param (struct d_info *di, int i)
 {
   struct demangle_component *p;
 
@@ -1620,7 +1620,7 @@  d_unqualified_name (struct d_info *di)
 static struct demangle_component *
 d_source_name (struct d_info *di)
 {
-  long len;
+  int len;
   struct demangle_component *ret;
 
   len = d_number (di);
@@ -1633,12 +1633,12 @@  d_source_name (struct d_info *di)
 
 /* number ::= [n] <(non-negative decimal integer)>  */
 
-static long
+static int
 d_number (struct d_info *di)
 {
   int negative;
   char peek;
-  long ret;
+  int ret;
 
   negative = 0;
   peek = d_peek_char (di);
@@ -1681,7 +1681,7 @@  d_number_component (struct d_info *di)
 /* identifier ::= <(unqualified source code identifier)>  */
 
 static struct demangle_component *
-d_identifier (struct d_info *di, long len)
+d_identifier (struct d_info *di, int len)
 {
   const char *name;
 
@@ -1702,7 +1702,7 @@  d_identifier (struct d_info *di, long le
   /* Look for something which looks like a gcc encoding of an
      anonymous namespace, and replace it with a more user friendly
      name.  */
-  if (len >= (long) ANONYMOUS_NAMESPACE_PREFIX_LEN + 2
+  if (len >= (int) ANONYMOUS_NAMESPACE_PREFIX_LEN + 2
       && memcmp (name, ANONYMOUS_NAMESPACE_PREFIX,
 		 ANONYMOUS_NAMESPACE_PREFIX_LEN) == 0)
     {
@@ -1870,7 +1870,7 @@  d_java_resource (struct d_info *di)
 {
   struct demangle_component *p = NULL;
   struct demangle_component *next = NULL;
-  long len, i;
+  int len, i;
   char c;
   const char *str;
 
@@ -2012,7 +2012,7 @@  d_special_name (struct d_info *di)
 	case 'C':
 	  {
 	    struct demangle_component *derived_type;
-	    long offset;
+	    int offset;
 	    struct demangle_component *base_type;
 
 	    derived_type = cplus_demangle_type (di);
@@ -2946,10 +2946,10 @@  d_pointer_to_member_type (struct d_info
 
 /* <non-negative number> _ */
 
-static long
+static int
 d_compact_number (struct d_info *di)
 {
-  long num;
+  int num;
   if (d_peek_char (di) == '_')
     num = 0;
   else if (d_peek_char (di) == 'n')
@@ -2957,7 +2957,7 @@  d_compact_number (struct d_info *di)
   else
     num = d_number (di) + 1;
 
-  if (! d_check_char (di, '_'))
+  if (num < 0 || ! d_check_char (di, '_'))
     return -1;
   return num;
 }
@@ -2969,7 +2969,7 @@  d_compact_number (struct d_info *di)
 static struct demangle_component *
 d_template_param (struct d_info *di)
 {
-  long param;
+  int param;
 
   if (! d_check_char (di, 'T'))
     return NULL;
@@ -3502,7 +3502,7 @@  d_local_name (struct d_info *di)
 static int
 d_discriminator (struct d_info *di)
 {
-  long discrim;
+  int discrim;
 
   if (d_peek_char (di) != '_')
     return 1;
@@ -3558,7 +3558,7 @@  static struct demangle_component *
 d_unnamed_type (struct d_info *di)
 {
   struct demangle_component *ret;
-  long num;
+  int num;
 
   if (! d_check_char (di, 'U'))
     return NULL;
@@ -4086,10 +4086,10 @@  d_append_string (struct d_print_info *dp
 }
 
 static inline void
-d_append_num (struct d_print_info *dpi, long l)
+d_append_num (struct d_print_info *dpi, int l)
 {
   char buf[25];
-  sprintf (buf,"%ld", l);
+  sprintf (buf,"%d", l);
   d_append_string (dpi, buf);
 }
 
Index: libiberty/testsuite/demangle-expected
===================================================================
--- libiberty/testsuite/demangle-expected	(revision 234663)
+++ libiberty/testsuite/demangle-expected	(working copy)
@@ -4422,12 +4422,17 @@ 
_Z3fooI1FEN1XIXszdtcl1PclcvT__EEE5arrayEE4TypeEv
X<sizeof ((P(((F)())())).array)>::Type foo<F>()
#
-# Tests a use-after-free problem
+# Tests a use-after-free problem PR70481

_Q.__0
::Q.(void)
#
-# Tests a use-after-free problem
+# Tests a use-after-free problem PR70481

_Q10-__9cafebabe.
cafebabe.::-(void)
+#
+# Tests write access violation PR70498
+
+_Z80800000000000000000000
+_Z80800000000000000000000