PR28069, assertion fail in dwarf.c:display_discr_list

We shouldn't be asserting on anything to do with leb128 values, or
reporting file and line numbers when something unexpected happens.
leb128 data is of indeterminate length, perfect for fuzzer mayhem.
It would only make sense to assert or report dwarf.c/readelf.c source
lines if the code had already sized and sanity checked the leb128
values.

After removing the assertions, the testcase then gave:

    <37>   DW_AT_discr_list  : 5 byte block: 0 0 0 0 0 	(label 0, label 0, label 0, label 0, <corrupt>
readelf: Warning: corrupt discr_list - unrecognized discriminant byte 0x5

    <3d>   DW_AT_encoding    : 0	(void)
    <3e>   DW_AT_identifier_case: 0	(case_sensitive)
    <3f>   DW_AT_virtuality  : 0	(none)
    <40>   DW_AT_decimal_sign: 5	(trailing separate)

So the DW_AT_discr_list was showing more data than just the 5 byte
block.  That happened due to "end" pointing a long way past the end of
block, and uvalue decrementing past zero on one of the leb128 bytes.

	PR 28069
	* dwarf.c (display_discr_list): Remove assertions.  Delete "end"
	parameter, use initial "data" pointer as the end.  Formatting.
	Don't count down bytes as they are read.
	(read_and_display_attr_value): Adjust display_discr_list call.
	(read_and_print_leb128): Don't pass __FILE__ and __LINE__ to
	report_leb_status.
	* dwarf.h (report_leb_status): Don't report file and line
	numbers.  Delete file and lnum parameters,
	(READ_ULEB, READ_SLEB): Adjust.
This commit is contained in:
Alan Modra
2021-07-10 09:34:30 +09:30
parent 34c54daa33
commit 9039747fb4
3 changed files with 26 additions and 21 deletions

View File

@ -1,3 +1,16 @@
2021-07-10 Alan Modra <amodra@gmail.com>
PR 28069
* dwarf.c (display_discr_list): Remove assertions. Delete "end"
parameter, use initial "data" pointer as the end. Formatting.
Don't count down bytes as they are read.
(read_and_display_attr_value): Adjust display_discr_list call.
(read_and_print_leb128): Don't pass __FILE__ and __LINE__ to
report_leb_status.
* dwarf.h (report_leb_status): Don't report file and line
numbers. Delete file and lnum parameters,
(READ_ULEB, READ_SLEB): Adjust.
2021-07-07 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> 2021-07-07 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
* configure.ac: Check for strnlen declaration. * configure.ac: Check for strnlen declaration.

View File

@ -2280,7 +2280,7 @@ read_and_print_leb128 (unsigned char *data,
int status; int status;
dwarf_vma val = read_leb128 (data, end, is_signed, bytes_read, &status); dwarf_vma val = read_leb128 (data, end, is_signed, bytes_read, &status);
if (status != 0) if (status != 0)
report_leb_status (status, __FILE__, __LINE__); report_leb_status (status);
else else
printf ("%s", dwarf_vmatoa (is_signed ? "d" : "u", val)); printf ("%s", dwarf_vmatoa (is_signed ? "d" : "u", val));
} }
@ -2289,9 +2289,10 @@ static void
display_discr_list (unsigned long form, display_discr_list (unsigned long form,
dwarf_vma uvalue, dwarf_vma uvalue,
unsigned char * data, unsigned char * data,
unsigned const char * end,
int level) int level)
{ {
unsigned char *end = data;
if (uvalue == 0) if (uvalue == 0)
{ {
printf ("[default]"); printf ("[default]");
@ -2320,41 +2321,32 @@ display_discr_list (unsigned long form,
return; return;
} }
bool is_signed = bool is_signed = (level > 0 && level <= MAX_CU_NESTING
(level > 0 && level <= MAX_CU_NESTING) ? level_type_signed [level - 1] : false);
? level_type_signed [level - 1] : false;
printf ("("); printf ("(");
while (uvalue) while (data < end)
{ {
unsigned char discriminant; unsigned char discriminant;
unsigned int bytes_read; unsigned int bytes_read;
SAFE_BYTE_GET_AND_INC (discriminant, data, 1, end); SAFE_BYTE_GET_AND_INC (discriminant, data, 1, end);
-- uvalue;
assert (uvalue > 0);
switch (discriminant) switch (discriminant)
{ {
case DW_DSC_label: case DW_DSC_label:
printf ("label "); printf ("label ");
read_and_print_leb128 (data, & bytes_read, end, is_signed); read_and_print_leb128 (data, & bytes_read, end, is_signed);
assert (bytes_read <= uvalue && bytes_read > 0);
uvalue -= bytes_read;
data += bytes_read; data += bytes_read;
break; break;
case DW_DSC_range: case DW_DSC_range:
printf ("range "); printf ("range ");
read_and_print_leb128 (data, & bytes_read, end, is_signed); read_and_print_leb128 (data, & bytes_read, end, is_signed);
assert (bytes_read <= uvalue && bytes_read > 0);
uvalue -= bytes_read;
data += bytes_read; data += bytes_read;
printf (".."); printf ("..");
read_and_print_leb128 (data, & bytes_read, end, is_signed); read_and_print_leb128 (data, & bytes_read, end, is_signed);
assert (bytes_read <= uvalue && bytes_read > 0);
uvalue -= bytes_read;
data += bytes_read; data += bytes_read;
break; break;
@ -2365,7 +2357,7 @@ display_discr_list (unsigned long form,
return; return;
} }
if (uvalue) if (data < end)
printf (", "); printf (", ");
} }
@ -3230,7 +3222,7 @@ read_and_display_attr_value (unsigned long attribute,
case DW_AT_discr_list: case DW_AT_discr_list:
printf ("\t"); printf ("\t");
display_discr_list (form, uvalue, data, end, level); display_discr_list (form, uvalue, data, level);
break; break;
case DW_AT_frame_base: case DW_AT_frame_base:

View File

@ -264,12 +264,12 @@ extern unsigned char * get_build_id (void *);
#endif #endif
static inline void static inline void
report_leb_status (int status, const char *file, unsigned long lnum) report_leb_status (int status)
{ {
if ((status & 1) != 0) if ((status & 1) != 0)
error (_("%s:%lu: end of data encountered whilst reading LEB\n"), file, lnum); error (_("end of data encountered whilst reading LEB\n"));
else if ((status & 2) != 0) else if ((status & 2) != 0)
error (_("%s:%lu: read LEB value is too large to store in destination variable\n"), file, lnum); error (_("read LEB value is too large to store in destination variable\n"));
} }
#define SKIP_ULEB(start, end) \ #define SKIP_ULEB(start, end) \
@ -302,7 +302,7 @@ report_leb_status (int status, const char *file, unsigned long lnum)
(var) = _val; \ (var) = _val; \
if ((var) != _val) \ if ((var) != _val) \
_status |= 2; \ _status |= 2; \
report_leb_status (_status, __FILE__, __LINE__); \ report_leb_status (_status); \
} \ } \
while (0) while (0)
@ -318,6 +318,6 @@ report_leb_status (int status, const char *file, unsigned long lnum)
(var) = _val; \ (var) = _val; \
if ((var) != _val) \ if ((var) != _val) \
_status |= 2; \ _status |= 2; \
report_leb_status (_status, __FILE__, __LINE__); \ report_leb_status (_status); \
} \ } \
while (0) while (0)