Fix several crashes of C++ demangler on fuzzed input.

libiberty/
	* cp-demangle.c (d_dump): Fix syntax error.
	(d_identifier): Adjust type of len to match d_source_name.
	(d_expression_1): Fix out-of-bounds access.  Check code variable for
	NULL before dereferencing it.
	(d_find_pack): Do not recurse for FIXED_TYPE, DEFAULT_ARG and NUMBER.
	(d_print_comp_inner): Add NULL pointer check.
	* cp-demangle.h (d_peek_next_char): Define as inline function when
	CHECK_DEMANGLER is defined.
	(d_advance): Likewise.
	* testsuite/demangle-expected: Add new testcases.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@225727 138bc75d-0d04-0410-961f-82ee72b054a4
This commit is contained in:
Mikhail Maltsev 2015-11-28 16:39:29 +00:00 committed by Pedro Alves
parent 6a8796db36
commit d81bf7ddc2
4 changed files with 94 additions and 8 deletions

View file

@ -52,6 +52,19 @@
* configure: Regenerated. * configure: Regenerated.
2015-07-13 Mikhail Maltsev <maltsevm@gmail.com>
* cp-demangle.c (d_dump): Fix syntax error.
(d_identifier): Adjust type of len to match d_source_name.
(d_expression_1): Fix out-of-bounds access. Check code variable for
NULL before dereferencing it.
(d_find_pack): Do not recurse for FIXED_TYPE, DEFAULT_ARG and NUMBER.
(d_print_comp_inner): Add NULL pointer check.
* cp-demangle.h (d_peek_next_char): Define as inline function when
CHECK_DEMANGLER is defined.
(d_advance): Likewise.
* testsuite/demangle-expected: Add new testcases.
2015-07-09 Uros Bizjak <ubizjak@gmail.com> 2015-07-09 Uros Bizjak <ubizjak@gmail.com>
* getruntime.c (RUSAGE_SELF): Define if not already defined. * getruntime.c (RUSAGE_SELF): Define if not already defined.

View file

@ -93,7 +93,11 @@
CP_DEMANGLE_DEBUG CP_DEMANGLE_DEBUG
If defined, turns on debugging mode, which prints information on If defined, turns on debugging mode, which prints information on
stdout about the mangled string. This is not generally useful. stdout about the mangled string. This is not generally useful.
*/
CHECK_DEMANGLER
If defined, additional sanity checks will be performed. It will
cause some slowdown, but will allow to catch out-of-bound access
errors earlier. This macro is intended for testing and debugging. */
#if defined (_AIX) && !defined (__GNUC__) #if defined (_AIX) && !defined (__GNUC__)
#pragma alloca #pragma alloca
@ -419,7 +423,7 @@ static struct demangle_component *d_source_name (struct d_info *);
static long d_number (struct d_info *); static long d_number (struct d_info *);
static struct demangle_component *d_identifier (struct d_info *, int); static struct demangle_component *d_identifier (struct d_info *, long);
static struct demangle_component *d_operator_name (struct d_info *); static struct demangle_component *d_operator_name (struct d_info *);
@ -715,7 +719,7 @@ d_dump (struct demangle_component *dc, int indent)
case DEMANGLE_COMPONENT_FIXED_TYPE: case DEMANGLE_COMPONENT_FIXED_TYPE:
printf ("fixed-point type, accum? %d, sat? %d\n", printf ("fixed-point type, accum? %d, sat? %d\n",
dc->u.s_fixed.accum, dc->u.s_fixed.sat); dc->u.s_fixed.accum, dc->u.s_fixed.sat);
d_dump (dc->u.s_fixed.length, indent + 2) d_dump (dc->u.s_fixed.length, indent + 2);
break; break;
case DEMANGLE_COMPONENT_ARGLIST: case DEMANGLE_COMPONENT_ARGLIST:
printf ("argument list\n"); printf ("argument list\n");
@ -1664,7 +1668,7 @@ d_number_component (struct d_info *di)
/* identifier ::= <(unqualified source code identifier)> */ /* identifier ::= <(unqualified source code identifier)> */
static struct demangle_component * static struct demangle_component *
d_identifier (struct d_info *di, int len) d_identifier (struct d_info *di, long len)
{ {
const char *name; const char *name;
@ -1685,7 +1689,7 @@ d_identifier (struct d_info *di, int len)
/* Look for something which looks like a gcc encoding of an /* Look for something which looks like a gcc encoding of an
anonymous namespace, and replace it with a more user friendly anonymous namespace, and replace it with a more user friendly
name. */ name. */
if (len >= (int) ANONYMOUS_NAMESPACE_PREFIX_LEN + 2 if (len >= (long) ANONYMOUS_NAMESPACE_PREFIX_LEN + 2
&& memcmp (name, ANONYMOUS_NAMESPACE_PREFIX, && memcmp (name, ANONYMOUS_NAMESPACE_PREFIX,
ANONYMOUS_NAMESPACE_PREFIX_LEN) == 0) ANONYMOUS_NAMESPACE_PREFIX_LEN) == 0)
{ {
@ -3174,6 +3178,8 @@ d_expression_1 (struct d_info *di)
struct demangle_component *type = NULL; struct demangle_component *type = NULL;
if (peek == 't') if (peek == 't')
type = cplus_demangle_type (di); type = cplus_demangle_type (di);
if (!d_peek_next_char (di))
return NULL;
d_advance (di, 2); d_advance (di, 2);
return d_make_comp (di, DEMANGLE_COMPONENT_INITIALIZER_LIST, return d_make_comp (di, DEMANGLE_COMPONENT_INITIALIZER_LIST,
type, d_exprlist (di, 'E')); type, d_exprlist (di, 'E'));
@ -3248,6 +3254,8 @@ d_expression_1 (struct d_info *di)
struct demangle_component *left; struct demangle_component *left;
struct demangle_component *right; struct demangle_component *right;
if (code == NULL)
return NULL;
if (op_is_new_cast (op)) if (op_is_new_cast (op))
left = cplus_demangle_type (di); left = cplus_demangle_type (di);
else else
@ -3275,7 +3283,9 @@ d_expression_1 (struct d_info *di)
struct demangle_component *second; struct demangle_component *second;
struct demangle_component *third; struct demangle_component *third;
if (!strcmp (code, "qu")) if (code == NULL)
return NULL;
else if (!strcmp (code, "qu"))
{ {
/* ?: expression. */ /* ?: expression. */
first = d_expression_1 (di); first = d_expression_1 (di);
@ -4204,6 +4214,9 @@ d_find_pack (struct d_print_info *dpi,
case DEMANGLE_COMPONENT_CHARACTER: case DEMANGLE_COMPONENT_CHARACTER:
case DEMANGLE_COMPONENT_FUNCTION_PARAM: case DEMANGLE_COMPONENT_FUNCTION_PARAM:
case DEMANGLE_COMPONENT_UNNAMED_TYPE: case DEMANGLE_COMPONENT_UNNAMED_TYPE:
case DEMANGLE_COMPONENT_FIXED_TYPE:
case DEMANGLE_COMPONENT_DEFAULT_ARG:
case DEMANGLE_COMPONENT_NUMBER:
return NULL; return NULL;
case DEMANGLE_COMPONENT_EXTENDED_OPERATOR: case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
@ -4439,6 +4452,11 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
local_name = d_right (typed_name); local_name = d_right (typed_name);
if (local_name->type == DEMANGLE_COMPONENT_DEFAULT_ARG) if (local_name->type == DEMANGLE_COMPONENT_DEFAULT_ARG)
local_name = local_name->u.s_unary_num.sub; local_name = local_name->u.s_unary_num.sub;
if (local_name == NULL)
{
d_print_error (dpi);
return;
}
while (local_name->type == DEMANGLE_COMPONENT_RESTRICT_THIS while (local_name->type == DEMANGLE_COMPONENT_RESTRICT_THIS
|| local_name->type == DEMANGLE_COMPONENT_VOLATILE_THIS || local_name->type == DEMANGLE_COMPONENT_VOLATILE_THIS
|| local_name->type == DEMANGLE_COMPONENT_CONST_THIS || local_name->type == DEMANGLE_COMPONENT_CONST_THIS

View file

@ -135,12 +135,37 @@ struct d_info
- call d_check_char(di, '\0') - call d_check_char(di, '\0')
Everything else is safe. */ Everything else is safe. */
#define d_peek_char(di) (*((di)->n)) #define d_peek_char(di) (*((di)->n))
#define d_peek_next_char(di) ((di)->n[1]) #ifndef CHECK_DEMANGLER
#define d_advance(di, i) ((di)->n += (i)) # define d_peek_next_char(di) ((di)->n[1])
# define d_advance(di, i) ((di)->n += (i))
#endif
#define d_check_char(di, c) (d_peek_char(di) == c ? ((di)->n++, 1) : 0) #define d_check_char(di, c) (d_peek_char(di) == c ? ((di)->n++, 1) : 0)
#define d_next_char(di) (d_peek_char(di) == '\0' ? '\0' : *((di)->n++)) #define d_next_char(di) (d_peek_char(di) == '\0' ? '\0' : *((di)->n++))
#define d_str(di) ((di)->n) #define d_str(di) ((di)->n)
#ifdef CHECK_DEMANGLER
static inline char
d_peek_next_char (const struct d_info *di)
{
if (!di->n[0])
abort ();
return di->n[1];
}
static inline void
d_advance (struct d_info *di, int i)
{
if (i < 0)
abort ();
while (i--)
{
if (!di->n[0])
abort ();
di->n++;
}
}
#endif
/* Functions and arrays in cp-demangle.c which are referenced by /* Functions and arrays in cp-demangle.c which are referenced by
functions in cp-demint.c. */ functions in cp-demint.c. */
#ifdef IN_GLIBCPP_V3 #ifdef IN_GLIBCPP_V3

View file

@ -4091,6 +4091,36 @@ void g<1>(A<1>&, B<static_cast<bool>(1)>&)
_ZNKSt7complexIiE4realB5cxx11Ev _ZNKSt7complexIiE4realB5cxx11Ev
std::complex<int>::real[abi:cxx11]() const std::complex<int>::real[abi:cxx11]() const
# #
# Some more crashes revealed by fuzz-testing:
# Check for NULL pointer when demangling trinary operators
--format=gnu-v3
_Z1fAv32_f
_Z1fAv32_f
# Do not overflow when decoding identifier length
--format=gnu-v3
_Z11111111111
_Z11111111111
# Check out-of-bounds access when decoding braced initializer list
--format=gnu-v3
_ZDTtl
_ZDTtl
# Check for NULL pointer when demangling DEMANGLE_COMPONENT_LOCAL_NAME
--format=gnu-v3
_ZZN1fEEd_lEv
_ZZN1fEEd_lEv
# Handle DEMANGLE_COMPONENT_FIXED_TYPE in d_find_pack
--format=gnu-v3
_Z1fDpDFT_
_Z1fDpDFT_
# Likewise, DEMANGLE_COMPONENT_DEFAULT_ARG
--format=gnu-v3
_Z1fIDpZ1fEd_E
_Z1fIDpZ1fEd_E
# Likewise, DEMANGLE_COMPONENT_NUMBER
--format=gnu-v3
_Z1fDpDv1_c
f((char __vector(1))...)
#
# Ada (GNAT) tests. # Ada (GNAT) tests.
# #
# Simple test. # Simple test.