Recursion in as_info_where

Message ID Y9oIhhJR9qaUEmFI@squeak.grove.modra.org
State Repeat Merge
Headers
Series Recursion in as_info_where |

Checks

Context Check Description
snail/binutils-gdb-check warning Git am fail log

Commit Message

Alan Modra Feb. 1, 2023, 6:36 a.m. UTC
  This function has a gas_assert, ie. possible call to as_abort, which
calls as_report_context, which calls as_info_where.  Attached fuzzer
testcase managed to trigger a stack overflow.

	* messages.c (as_info_where): Don't gas_assert.
  

Comments

Jan Beulich Feb. 1, 2023, 9:24 a.m. UTC | #1
On 01.02.2023 07:36, Alan Modra via Binutils wrote:
> This function has a gas_assert, ie. possible call to as_abort, which
> calls as_report_context, which calls as_info_where.  Attached fuzzer
> testcase managed to trigger a stack overflow.
> 
> 	* messages.c (as_info_where): Don't gas_assert.
> 
> diff --git a/gas/messages.c b/gas/messages.c
> index 0db075d779c..7c018acf69f 100644
> --- a/gas/messages.c
> +++ b/gas/messages.c
> @@ -141,8 +141,6 @@ as_info_where (const char *file, unsigned int line, unsigned int indent,
>    va_list args;
>    char buffer[2000];
>  
> -  gas_assert (file != NULL && line > 0 && indent <= INT_MAX);

If this go in the way, isn't it that the assertion actually triggered?
In which case shouldn't the cause for it triggering be addressed
instead, to avoid subsequent knock-on damage (e.g. from de-referencing
"file"? (I may want to play with the testcase a little myself.)

Jan
  
Alan Modra Feb. 1, 2023, 11:59 a.m. UTC | #2
On Wed, Feb 01, 2023 at 10:24:25AM +0100, Jan Beulich wrote:
> On 01.02.2023 07:36, Alan Modra via Binutils wrote:
> > This function has a gas_assert, ie. possible call to as_abort, which
> > calls as_report_context, which calls as_info_where.  Attached fuzzer
> > testcase managed to trigger a stack overflow.
> > 
> > 	* messages.c (as_info_where): Don't gas_assert.
> > 
> > diff --git a/gas/messages.c b/gas/messages.c
> > index 0db075d779c..7c018acf69f 100644
> > --- a/gas/messages.c
> > +++ b/gas/messages.c
> > @@ -141,8 +141,6 @@ as_info_where (const char *file, unsigned int line, unsigned int indent,
> >    va_list args;
> >    char buffer[2000];
> >  
> > -  gas_assert (file != NULL && line > 0 && indent <= INT_MAX);
> 
> If this go in the way, isn't it that the assertion actually triggered?
> In which case shouldn't the cause for it triggering be addressed
> instead, to avoid subsequent knock-on damage (e.g. from de-referencing
> "file"? (I may want to play with the testcase a little myself.)

The testcase is really weird, something that no programmer would ever
write.  It failed the assert with line == 0.  I'll let you discover
the horrible "# line file" with embedded \0 that gets you there.  :-)
I'm not motivated to fix that sort of insanity.
  
Jan Beulich Feb. 3, 2023, 1:44 p.m. UTC | #3
On 01.02.2023 12:59, Alan Modra wrote:
> On Wed, Feb 01, 2023 at 10:24:25AM +0100, Jan Beulich wrote:
>> On 01.02.2023 07:36, Alan Modra via Binutils wrote:
>>> This function has a gas_assert, ie. possible call to as_abort, which
>>> calls as_report_context, which calls as_info_where.  Attached fuzzer
>>> testcase managed to trigger a stack overflow.
>>>
>>> 	* messages.c (as_info_where): Don't gas_assert.
>>>
>>> diff --git a/gas/messages.c b/gas/messages.c
>>> index 0db075d779c..7c018acf69f 100644
>>> --- a/gas/messages.c
>>> +++ b/gas/messages.c
>>> @@ -141,8 +141,6 @@ as_info_where (const char *file, unsigned int line, unsigned int indent,
>>>    va_list args;
>>>    char buffer[2000];
>>>  
>>> -  gas_assert (file != NULL && line > 0 && indent <= INT_MAX);
>>
>> If this go in the way, isn't it that the assertion actually triggered?
>> In which case shouldn't the cause for it triggering be addressed
>> instead, to avoid subsequent knock-on damage (e.g. from de-referencing
>> "file"? (I may want to play with the testcase a little myself.)
> 
> The testcase is really weird, something that no programmer would ever
> write.  It failed the assert with line == 0.  I'll let you discover
> the horrible "# line file" with embedded \0 that gets you there.  :-)

Sure. That's an interaction bug between read_a_source_file()'s bumping
of line numbers and s_linefile() trying to compensate. I have a fix for
that, but I'll want to give it wider testing before posting.

As to the assertion here, I'd prefer if we would keep it, and do e.g.
the below instead. Thoughts?

Jan

--- a/gas/messages.c
+++ b/gas/messages.c
@@ -140,6 +140,12 @@ as_info_where (const char *file, unsigne
 {
   va_list args;
   char buffer[2000];
+  static bool active;
+
+  /* We can come back here if e.g. the assertion below triggers.  */
+  if (active)
+    return;
+  active = true;
 
   gas_assert (file != NULL && line > 0 && indent <= INT_MAX);
 
@@ -148,6 +154,8 @@ as_info_where (const char *file, unsigne
   va_end (args);
   fprintf (stderr, "%s:%u: %*s%s%s\n",
 	   file, line, (int)indent, "", _("Info: "), buffer);
+
+  active = false;
 }
 
 /* Send to stderr a string as a warning, and locate warning
  
Alan Modra Feb. 6, 2023, 1:41 a.m. UTC | #4
On Fri, Feb 03, 2023 at 02:44:06PM +0100, Jan Beulich wrote:
> On 01.02.2023 12:59, Alan Modra wrote:
> > On Wed, Feb 01, 2023 at 10:24:25AM +0100, Jan Beulich wrote:
> >> On 01.02.2023 07:36, Alan Modra via Binutils wrote:
> >>> This function has a gas_assert, ie. possible call to as_abort, which
> >>> calls as_report_context, which calls as_info_where.  Attached fuzzer
> >>> testcase managed to trigger a stack overflow.
> >>>
> >>> 	* messages.c (as_info_where): Don't gas_assert.
> >>>
> >>> diff --git a/gas/messages.c b/gas/messages.c
> >>> index 0db075d779c..7c018acf69f 100644
> >>> --- a/gas/messages.c
> >>> +++ b/gas/messages.c
> >>> @@ -141,8 +141,6 @@ as_info_where (const char *file, unsigned int line, unsigned int indent,
> >>>    va_list args;
> >>>    char buffer[2000];
> >>>  
> >>> -  gas_assert (file != NULL && line > 0 && indent <= INT_MAX);
> >>
> >> If this go in the way, isn't it that the assertion actually triggered?
> >> In which case shouldn't the cause for it triggering be addressed
> >> instead, to avoid subsequent knock-on damage (e.g. from de-referencing
> >> "file"? (I may want to play with the testcase a little myself.)
> > 
> > The testcase is really weird, something that no programmer would ever
> > write.  It failed the assert with line == 0.  I'll let you discover
> > the horrible "# line file" with embedded \0 that gets you there.  :-)
> 
> Sure. That's an interaction bug between read_a_source_file()'s bumping
> of line numbers and s_linefile() trying to compensate. I have a fix for
> that, but I'll want to give it wider testing before posting.
> 
> As to the assertion here, I'd prefer if we would keep it, and do e.g.
> the below instead. Thoughts?

I think it is bad practice for a low-level error handling function to
assert.
  

Patch

diff --git a/gas/messages.c b/gas/messages.c
index 0db075d779c..7c018acf69f 100644
--- a/gas/messages.c
+++ b/gas/messages.c
@@ -141,8 +141,6 @@  as_info_where (const char *file, unsigned int line, unsigned int indent,
   va_list args;
   char buffer[2000];
 
-  gas_assert (file != NULL && line > 0 && indent <= INT_MAX);
-
   va_start (args, format);
   vsnprintf (buffer, sizeof (buffer), format, args);
   va_end (args);