Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
@ 2026-01-03 14:55 Tom de Vries
  2026-01-05 16:38 ` Tom Tromey
  2026-01-05 19:57 ` Andrew Burgess
  0 siblings, 2 replies; 14+ messages in thread
From: Tom de Vries @ 2026-01-03 14:55 UTC (permalink / raw)
  To: gdb-patches

PR gdb/33754 reports a heap-buffer-overflow here in args_complete_p:
...
  while (*input != '\0')
...

Fix this by introducing a lambda function at that safely handles all char
array accesses.

Also:
- factor out char array accesses using new variables c and next_c, and
- check for end-of-string after skip_spaces.

Tested on x86_64-linux.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33754
---
 gdb/infcmd.c | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 875bbe1ee69..ceacfd05683 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -126,17 +126,27 @@ static bool
 args_complete_p (const std::string &args)
 {
   const char *input = args.c_str ();
+  const char *end = input + args.length ();
   bool squote = false, dquote = false;
 
-  while (*input != '\0')
+  auto at = [&] (const char *s)
+    {
+      return s > end ? '\0' : *s;
+    };
+
+  while (at (input) != '\0')
     {
       input = skip_spaces (input);
+      char c = at (input);
+      if (c == '\0')
+	break;
+      char next_c = at (input + 1);
 
       if (squote)
 	{
 	  /* Inside a single quoted argument, look for the closing single
 	     quote.  */
-	  if (*input == '\'')
+	  if (c == '\'')
 	    squote = false;
 	}
       else if (dquote)
@@ -148,10 +158,10 @@ args_complete_p (const std::string &args)
 	     and we don't skip the entire '\\' then we'll only skip the
 	     first '\', in which case we might see the second '\' as a '\"'
 	     sequence, which would be wrong.  */
-	  if (*input == '\\' && strchr ("\"\\", *(input + 1)) != nullptr)
+	  if (c == '\\' && strchr ("\"\\", next_c) != nullptr)
 	    ++input;
 	  /* Otherwise, just look for the closing double quote.  */
-	  else if (*input == '"')
+	  else if (c == '"')
 	    dquote = false;
 	}
       else
@@ -162,7 +172,7 @@ args_complete_p (const std::string &args)
 	     a quoted argument.  The '\\' we need to skip so we don't just
 	     skip the first '\' and then incorrectly consider the second
 	     '\' are part of a '\"' or '\'' sequence.  */
-	  if (*input == '\\' && strchr ("\"\\'", *(input + 1)) != nullptr)
+	  if (c == '\\' && strchr ("\"\\'", next_c) != nullptr)
 	    ++input;
 	  /* Otherwise, check for the start of a single or double quoted
 	     argument.  Single quotes have no special meaning on Windows
@@ -170,10 +180,10 @@ args_complete_p (const std::string &args)
 	     host to determine what is, or isn't a special character, when
 	     really, this is a function of the target.  */
 #ifndef _WIN32
-	  else if (*input == '\'')
+	  else if (c == '\'')
 	    squote = true;
 #endif
-	  else if (*input == '"')
+	  else if (c == '"')
 	    dquote = true;
 	}
 

base-commit: 0a153c58a0ab68c6fa349d2ad0bf6a42e043ab23
-- 
2.51.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-03 14:55 [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p Tom de Vries
@ 2026-01-05 16:38 ` Tom Tromey
  2026-01-06 14:51   ` Tom de Vries
  2026-01-05 19:57 ` Andrew Burgess
  1 sibling, 1 reply; 14+ messages in thread
From: Tom Tromey @ 2026-01-05 16:38 UTC (permalink / raw)
  To: Tom de Vries; +Cc: gdb-patches

>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:

Tom> +  auto at = [&] (const char *s)
Tom> +    {
Tom> +      return s > end ? '\0' : *s;
Tom> +    };

I think it would better to avoid stepping off the end at the points
where the pointer is incremented.

Tom> +  while (at (input) != '\0')

Like this could be

   for (input = skip_spaces (input); *input != '\0'; input = skip_spaces (input))

and then there's like one or two spots to check in the loop.

Tom

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-03 14:55 [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p Tom de Vries
  2026-01-05 16:38 ` Tom Tromey
@ 2026-01-05 19:57 ` Andrew Burgess
  2026-01-05 20:02   ` Andrew Burgess
                     ` (2 more replies)
  1 sibling, 3 replies; 14+ messages in thread
From: Andrew Burgess @ 2026-01-05 19:57 UTC (permalink / raw)
  To: Tom de Vries, gdb-patches

Tom de Vries <tdevries@suse.de> writes:

> PR gdb/33754 reports a heap-buffer-overflow here in args_complete_p:
> ...
>   while (*input != '\0')
> ...
>
> Fix this by introducing a lambda function at that safely handles all char
> array accesses.

Sorry to be a bore, but after reading this commit, and the bug report,
it's still not obvious to me where the overflow actually occurs.

I totally accept that this code is broken, but as I introduced this bug,
I wanted to learn from this mistake, but this commit doesn't really
explain what mistake is being fixed.

Do you think you could explain what's actually going wrong here?

Thanks,
Andrew




>
> Also:
> - factor out char array accesses using new variables c and next_c, and
> - check for end-of-string after skip_spaces.
>
> Tested on x86_64-linux.
>
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33754
> ---
>  gdb/infcmd.c | 24 +++++++++++++++++-------
>  1 file changed, 17 insertions(+), 7 deletions(-)
>
> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
> index 875bbe1ee69..ceacfd05683 100644
> --- a/gdb/infcmd.c
> +++ b/gdb/infcmd.c
> @@ -126,17 +126,27 @@ static bool
>  args_complete_p (const std::string &args)
>  {
>    const char *input = args.c_str ();
> +  const char *end = input + args.length ();
>    bool squote = false, dquote = false;
>  
> -  while (*input != '\0')
> +  auto at = [&] (const char *s)
> +    {
> +      return s > end ? '\0' : *s;
> +    };
> +
> +  while (at (input) != '\0')
>      {
>        input = skip_spaces (input);
> +      char c = at (input);
> +      if (c == '\0')
> +	break;
> +      char next_c = at (input + 1);
>  
>        if (squote)
>  	{
>  	  /* Inside a single quoted argument, look for the closing single
>  	     quote.  */
> -	  if (*input == '\'')
> +	  if (c == '\'')
>  	    squote = false;
>  	}
>        else if (dquote)
> @@ -148,10 +158,10 @@ args_complete_p (const std::string &args)
>  	     and we don't skip the entire '\\' then we'll only skip the
>  	     first '\', in which case we might see the second '\' as a '\"'
>  	     sequence, which would be wrong.  */
> -	  if (*input == '\\' && strchr ("\"\\", *(input + 1)) != nullptr)
> +	  if (c == '\\' && strchr ("\"\\", next_c) != nullptr)
>  	    ++input;
>  	  /* Otherwise, just look for the closing double quote.  */
> -	  else if (*input == '"')
> +	  else if (c == '"')
>  	    dquote = false;
>  	}
>        else
> @@ -162,7 +172,7 @@ args_complete_p (const std::string &args)
>  	     a quoted argument.  The '\\' we need to skip so we don't just
>  	     skip the first '\' and then incorrectly consider the second
>  	     '\' are part of a '\"' or '\'' sequence.  */
> -	  if (*input == '\\' && strchr ("\"\\'", *(input + 1)) != nullptr)
> +	  if (c == '\\' && strchr ("\"\\'", next_c) != nullptr)
>  	    ++input;
>  	  /* Otherwise, check for the start of a single or double quoted
>  	     argument.  Single quotes have no special meaning on Windows
> @@ -170,10 +180,10 @@ args_complete_p (const std::string &args)
>  	     host to determine what is, or isn't a special character, when
>  	     really, this is a function of the target.  */
>  #ifndef _WIN32
> -	  else if (*input == '\'')
> +	  else if (c == '\'')
>  	    squote = true;
>  #endif
> -	  else if (*input == '"')
> +	  else if (c == '"')
>  	    dquote = true;
>  	}
>  
>
> base-commit: 0a153c58a0ab68c6fa349d2ad0bf6a42e043ab23
> -- 
> 2.51.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-05 19:57 ` Andrew Burgess
@ 2026-01-05 20:02   ` Andrew Burgess
  2026-01-05 20:09   ` Tom Tromey
  2026-01-06  8:47   ` Tom de Vries
  2 siblings, 0 replies; 14+ messages in thread
From: Andrew Burgess @ 2026-01-05 20:02 UTC (permalink / raw)
  To: Tom de Vries, gdb-patches

Andrew Burgess <aburgess@redhat.com> writes:

> Tom de Vries <tdevries@suse.de> writes:
>
>> PR gdb/33754 reports a heap-buffer-overflow here in args_complete_p:
>> ...
>>   while (*input != '\0')
>> ...
>>
>> Fix this by introducing a lambda function at that safely handles all char
>> array accesses.
>
> Sorry to be a bore, but after reading this commit, and the bug report,
> it's still not obvious to me where the overflow actually occurs.
>
> I totally accept that this code is broken, but as I introduced this bug,
> I wanted to learn from this mistake, but this commit doesn't really
> explain what mistake is being fixed.
>
> Do you think you could explain what's actually going wrong here?

Literally after hitting send, it occurred to me, is the problem maybe
these two lines:

  if (*input == '\\' && strchr ("\"\\", *(input + 1)) != nullptr)
    ++input;

And the other one in the 'else' block?  I think if *(input + 1) is '\0',
then the strchr call will return non-nullptr, which wasn't the desired
behaviour, and could result in stepping outside the string.

If this is the case then I think the correct fix would be checking if
the character at 'input + 1' is NULL or not, see the possible patch
below (there's no commit message or anything for it yet).

Thanks,
Andrew

---

diff --git i/gdb/infcmd.c w/gdb/infcmd.c
index 875bbe1ee69..7dd3392c96a 100644
--- i/gdb/infcmd.c
+++ w/gdb/infcmd.c
@@ -148,7 +148,9 @@ args_complete_p (const std::string &args)
 	     and we don't skip the entire '\\' then we'll only skip the
 	     first '\', in which case we might see the second '\' as a '\"'
 	     sequence, which would be wrong.  */
-	  if (*input == '\\' && strchr ("\"\\", *(input + 1)) != nullptr)
+	  if (*input == '\\'
+	      && *(input + 1) != '\0'
+	      && strchr ("\"\\", *(input + 1)) != nullptr)
 	    ++input;
 	  /* Otherwise, just look for the closing double quote.  */
 	  else if (*input == '"')
@@ -162,7 +164,9 @@ args_complete_p (const std::string &args)
 	     a quoted argument.  The '\\' we need to skip so we don't just
 	     skip the first '\' and then incorrectly consider the second
 	     '\' are part of a '\"' or '\'' sequence.  */
-	  if (*input == '\\' && strchr ("\"\\'", *(input + 1)) != nullptr)
+	  if (*input == '\\'
+	      && *(input + 1) != '\0'
+	      && strchr ("\"\\'", *(input + 1)) != nullptr)
 	    ++input;
 	  /* Otherwise, check for the start of a single or double quoted
 	     argument.  Single quotes have no special meaning on Windows



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-05 19:57 ` Andrew Burgess
  2026-01-05 20:02   ` Andrew Burgess
@ 2026-01-05 20:09   ` Tom Tromey
  2026-01-06  8:47   ` Tom de Vries
  2 siblings, 0 replies; 14+ messages in thread
From: Tom Tromey @ 2026-01-05 20:09 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Tom de Vries, gdb-patches

>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:

Andrew> Sorry to be a bore, but after reading this commit, and the bug report,
Andrew> it's still not obvious to me where the overflow actually occurs.

I believe in this code:

	  if (*input == '\\' && strchr ("\"\\'", *(input + 1)) != nullptr)
	    ++input;

if *input == '\\' but this is also the last character of the string,
then strchr will return the address of the \0, then ++input will advance
past it.

I think sticking "&& input[1] != '\0'" in there might be sufficient.

Though perhaps there's also an issue if the string ends with spaces,
because:

      input = skip_spaces (input);
...
      ++input;

Tom

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-05 19:57 ` Andrew Burgess
  2026-01-05 20:02   ` Andrew Burgess
  2026-01-05 20:09   ` Tom Tromey
@ 2026-01-06  8:47   ` Tom de Vries
  2026-01-06  9:29     ` Tom de Vries
                       ` (2 more replies)
  2 siblings, 3 replies; 14+ messages in thread
From: Tom de Vries @ 2026-01-06  8:47 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 1/5/26 8:57 PM, Andrew Burgess wrote:
> Tom de Vries <tdevries@suse.de> writes:
> 
>> PR gdb/33754 reports a heap-buffer-overflow here in args_complete_p:
>> ...
>>    while (*input != '\0')
>> ...
>>
>> Fix this by introducing a lambda function at that safely handles all char
>> array accesses.
> 
> Sorry to be a bore, but after reading this commit, and the bug report,
> it's still not obvious to me where the overflow actually occurs.
> 
> I totally accept that this code is broken, but as I introduced this bug,
> I wanted to learn from this mistake, but this commit doesn't really
> explain what mistake is being fixed.
> 
> Do you think you could explain what's actually going wrong here?
> 

Hi Andrew,

agreed, it's not spelled out, sorry about that.

So, the heap-buffer-overflow happens with:
...
(gdb) p args
$1 = "\"first arg\" \"\" \"third-arg\" \"'\" \"\\\"\" \" \" \"\" "
...
and it's the fact that we don't check for '\0' after skip_spaces that is 
the problem.  I think it should be possible to reproduce the problem 
with args == " ".

So a minimal fix for this problem is:
...
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 1a7daf1461b..fdcd4e4ba96 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -131,6 +131,8 @@ args_complete_p (const std::string &args)
    while (*input != '\0')
      {
        input = skip_spaces (input);
+      if (*input == '\0')
+	break;

        if (squote)
  	{
...

But the strchr problem is also there, so this:
...
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 1a7daf1461b..4bcd523f79b 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -177,6 +177,8 @@ args_complete_p (const std::string &args)
  	    dquote = true;
  	}

+      if (*input == '\0')
+	break;
        ++input;
      }

...
would catch both, I think.  Not that I'm suggesting this fix.

Thanks,
- Tom

> Thanks,
> Andrew
> 
> 
> 
> 
>>
>> Also:
>> - factor out char array accesses using new variables c and next_c, and
>> - check for end-of-string after skip_spaces.
>>
>> Tested on x86_64-linux.
>>
>> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33754
>> ---
>>   gdb/infcmd.c | 24 +++++++++++++++++-------
>>   1 file changed, 17 insertions(+), 7 deletions(-)
>>
>> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
>> index 875bbe1ee69..ceacfd05683 100644
>> --- a/gdb/infcmd.c
>> +++ b/gdb/infcmd.c
>> @@ -126,17 +126,27 @@ static bool
>>   args_complete_p (const std::string &args)
>>   {
>>     const char *input = args.c_str ();
>> +  const char *end = input + args.length ();
>>     bool squote = false, dquote = false;
>>   
>> -  while (*input != '\0')
>> +  auto at = [&] (const char *s)
>> +    {
>> +      return s > end ? '\0' : *s;
>> +    };
>> +
>> +  while (at (input) != '\0')
>>       {
>>         input = skip_spaces (input);
>> +      char c = at (input);
>> +      if (c == '\0')
>> +	break;
>> +      char next_c = at (input + 1);
>>   
>>         if (squote)
>>   	{
>>   	  /* Inside a single quoted argument, look for the closing single
>>   	     quote.  */
>> -	  if (*input == '\'')
>> +	  if (c == '\'')
>>   	    squote = false;
>>   	}
>>         else if (dquote)
>> @@ -148,10 +158,10 @@ args_complete_p (const std::string &args)
>>   	     and we don't skip the entire '\\' then we'll only skip the
>>   	     first '\', in which case we might see the second '\' as a '\"'
>>   	     sequence, which would be wrong.  */
>> -	  if (*input == '\\' && strchr ("\"\\", *(input + 1)) != nullptr)
>> +	  if (c == '\\' && strchr ("\"\\", next_c) != nullptr)
>>   	    ++input;
>>   	  /* Otherwise, just look for the closing double quote.  */
>> -	  else if (*input == '"')
>> +	  else if (c == '"')
>>   	    dquote = false;
>>   	}
>>         else
>> @@ -162,7 +172,7 @@ args_complete_p (const std::string &args)
>>   	     a quoted argument.  The '\\' we need to skip so we don't just
>>   	     skip the first '\' and then incorrectly consider the second
>>   	     '\' are part of a '\"' or '\'' sequence.  */
>> -	  if (*input == '\\' && strchr ("\"\\'", *(input + 1)) != nullptr)
>> +	  if (c == '\\' && strchr ("\"\\'", next_c) != nullptr)
>>   	    ++input;
>>   	  /* Otherwise, check for the start of a single or double quoted
>>   	     argument.  Single quotes have no special meaning on Windows
>> @@ -170,10 +180,10 @@ args_complete_p (const std::string &args)
>>   	     host to determine what is, or isn't a special character, when
>>   	     really, this is a function of the target.  */
>>   #ifndef _WIN32
>> -	  else if (*input == '\'')
>> +	  else if (c == '\'')
>>   	    squote = true;
>>   #endif
>> -	  else if (*input == '"')
>> +	  else if (c == '"')
>>   	    dquote = true;
>>   	}
>>   
>>
>> base-commit: 0a153c58a0ab68c6fa349d2ad0bf6a42e043ab23
>> -- 
>> 2.51.0
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-06  8:47   ` Tom de Vries
@ 2026-01-06  9:29     ` Tom de Vries
  2026-01-06 14:53     ` Tom de Vries
  2026-01-07 10:46     ` Andrew Burgess
  2 siblings, 0 replies; 14+ messages in thread
From: Tom de Vries @ 2026-01-06  9:29 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 1/6/26 9:47 AM, Tom de Vries wrote:
> and it's the fact that we don't check for '\0' after skip_spaces that is 
> the problem.  I think it should be possible to reproduce the problem 
> with args == " ".

I've written a unit test that reproduces the problem with args == " ".

Thanks,
- Tom

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 1a7daf1461b..88948a343da 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -56,6 +56,7 @@
  #include <optional>
  #include "source.h"
  #include "cli/cli-style.h"
+#include "gdbsupport/selftest.h"

  /* Local functions: */

@@ -123,7 +124,7 @@ show_inferior_tty_command (struct ui_file *file, int 
from_tty,
     finished.  */

  static bool
-args_complete_p (const std::string &args)
+args_complete_p (const std::string &args, const char **end = nullptr)
  {
    const char *input = args.c_str ();
    bool squote = false, dquote = false;
@@ -180,9 +185,28 @@ args_complete_p (const std::string &args)
        ++input;
      }

+  if (end != nullptr)
+    *end = input;
    return (!dquote && !squote);
  }

+#if GDB_SELF_TEST
+namespace selftests {
+
+static void
+infcmd_args_complete_p_tests (void)
+{
+  const char *end;
+
+  /* Regression test for heap-buffer-overflow reported in PR33754.  */
+  std::string s1 = " ";
+  SELF_CHECK (args_complete_p (s1, &end));
+  SELF_CHECK (end == s1.data () + s1.size ());
+}
+
+} /* namespace selftests */
+#endif /* GDB_SELF_TEST */
+
  /* Build a complete inferior argument string (all arguments to pass to the
     inferior) and return it.  ARGS is the initial part of the inferior
     arguments string, which might be the complete inferior arguments, in
@@ -3634,4 +3658,9 @@ Show whether `finish' prints the return value."), 
nullptr,
  			   nullptr,
  			   show_print_finish,
  			   &setprintlist, &showprintlist);
+
+#if GDB_SELF_TEST
+  selftests::register_test ("infcmd-args-complete-p",
+			    selftests::infcmd_args_complete_p_tests);
+#endif
  }


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-05 16:38 ` Tom Tromey
@ 2026-01-06 14:51   ` Tom de Vries
  0 siblings, 0 replies; 14+ messages in thread
From: Tom de Vries @ 2026-01-06 14:51 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 1/5/26 5:38 PM, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
> 
> Tom> +  auto at = [&] (const char *s)
> Tom> +    {
> Tom> +      return s > end ? '\0' : *s;
> Tom> +    };
> 
> I think it would better to avoid stepping off the end at the points
> where the pointer is incremented.
> 
> Tom> +  while (at (input) != '\0')
> 
> Like this could be
> 
>     for (input = skip_spaces (input); *input != '\0'; input = skip_spaces (input))
> 
> and then there's like one or two spots to check in the loop.

I've submitted a v2 ( 
https://sourceware.org/pipermail/gdb-patches/2026-January/223715.html ). 
  I didn't do the transformation you suggested here, I tried to do 
something minimal.

I could submit a follow-up patch to do some refactoring in this 
function, though I'd likely do something like:
...
       while (true)
         {
           input = skip_spaces (input);
           if (*input == '\0')
              break;
...
instead of the for loop you're suggesting.

Thanks,
- Tom

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-06  8:47   ` Tom de Vries
  2026-01-06  9:29     ` Tom de Vries
@ 2026-01-06 14:53     ` Tom de Vries
  2026-01-07 10:46     ` Andrew Burgess
  2 siblings, 0 replies; 14+ messages in thread
From: Tom de Vries @ 2026-01-06 14:53 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 1/6/26 9:47 AM, Tom de Vries wrote:
> On 1/5/26 8:57 PM, Andrew Burgess wrote:
>> Tom de Vries <tdevries@suse.de> writes:
>>
>>> PR gdb/33754 reports a heap-buffer-overflow here in args_complete_p:
>>> ...
>>>    while (*input != '\0')
>>> ...
>>>
>>> Fix this by introducing a lambda function at that safely handles all 
>>> char
>>> array accesses.
>>
>> Sorry to be a bore, but after reading this commit, and the bug report,
>> it's still not obvious to me where the overflow actually occurs.
>>
>> I totally accept that this code is broken, but as I introduced this bug,
>> I wanted to learn from this mistake, but this commit doesn't really
>> explain what mistake is being fixed.
>>
>> Do you think you could explain what's actually going wrong here?
>>
> 
> Hi Andrew,
> 
> agreed, it's not spelled out, sorry about that.
> 
> So, the heap-buffer-overflow happens with:
> ...
> (gdb) p args
> $1 = "\"first arg\" \"\" \"third-arg\" \"'\" \"\\\"\" \" \" \"\" "
> ...
> and it's the fact that we don't check for '\0' after skip_spaces that is 
> the problem.  I think it should be possible to reproduce the problem 
> with args == " ".
> 
> So a minimal fix for this problem is:
> ...
> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
> index 1a7daf1461b..fdcd4e4ba96 100644
> --- a/gdb/infcmd.c
> +++ b/gdb/infcmd.c
> @@ -131,6 +131,8 @@ args_complete_p (const std::string &args)
>     while (*input != '\0')
>       {
>         input = skip_spaces (input);
> +      if (*input == '\0')
> +    break;
> 
>         if (squote)
>       {
> ...
> 
> But the strchr problem is also there, so this:
> ...
> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
> index 1a7daf1461b..4bcd523f79b 100644
> --- a/gdb/infcmd.c
> +++ b/gdb/infcmd.c
> @@ -177,6 +177,8 @@ args_complete_p (const std::string &args)
>           dquote = true;
>       }
> 
> +      if (*input == '\0')
> +    break;
>         ++input;
>       }
> 
> ...
> would catch both, I think.  Not that I'm suggesting this fix.
> 

I've submitted a v2 ( 
https://sourceware.org/pipermail/gdb-patches/2026-January/223715.html ) 
handling both problems explicitly.

Thanks,
- Tom

> Thanks,
> - Tom
> 
>> Thanks,
>> Andrew
>>
>>
>>
>>
>>>
>>> Also:
>>> - factor out char array accesses using new variables c and next_c, and
>>> - check for end-of-string after skip_spaces.
>>>
>>> Tested on x86_64-linux.
>>>
>>> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33754
>>> ---
>>>   gdb/infcmd.c | 24 +++++++++++++++++-------
>>>   1 file changed, 17 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
>>> index 875bbe1ee69..ceacfd05683 100644
>>> --- a/gdb/infcmd.c
>>> +++ b/gdb/infcmd.c
>>> @@ -126,17 +126,27 @@ static bool
>>>   args_complete_p (const std::string &args)
>>>   {
>>>     const char *input = args.c_str ();
>>> +  const char *end = input + args.length ();
>>>     bool squote = false, dquote = false;
>>> -  while (*input != '\0')
>>> +  auto at = [&] (const char *s)
>>> +    {
>>> +      return s > end ? '\0' : *s;
>>> +    };
>>> +
>>> +  while (at (input) != '\0')
>>>       {
>>>         input = skip_spaces (input);
>>> +      char c = at (input);
>>> +      if (c == '\0')
>>> +    break;
>>> +      char next_c = at (input + 1);
>>>         if (squote)
>>>       {
>>>         /* Inside a single quoted argument, look for the closing single
>>>            quote.  */
>>> -      if (*input == '\'')
>>> +      if (c == '\'')
>>>           squote = false;
>>>       }
>>>         else if (dquote)
>>> @@ -148,10 +158,10 @@ args_complete_p (const std::string &args)
>>>            and we don't skip the entire '\\' then we'll only skip the
>>>            first '\', in which case we might see the second '\' as a 
>>> '\"'
>>>            sequence, which would be wrong.  */
>>> -      if (*input == '\\' && strchr ("\"\\", *(input + 1)) != nullptr)
>>> +      if (c == '\\' && strchr ("\"\\", next_c) != nullptr)
>>>           ++input;
>>>         /* Otherwise, just look for the closing double quote.  */
>>> -      else if (*input == '"')
>>> +      else if (c == '"')
>>>           dquote = false;
>>>       }
>>>         else
>>> @@ -162,7 +172,7 @@ args_complete_p (const std::string &args)
>>>            a quoted argument.  The '\\' we need to skip so we don't just
>>>            skip the first '\' and then incorrectly consider the second
>>>            '\' are part of a '\"' or '\'' sequence.  */
>>> -      if (*input == '\\' && strchr ("\"\\'", *(input + 1)) != nullptr)
>>> +      if (c == '\\' && strchr ("\"\\'", next_c) != nullptr)
>>>           ++input;
>>>         /* Otherwise, check for the start of a single or double quoted
>>>            argument.  Single quotes have no special meaning on Windows
>>> @@ -170,10 +180,10 @@ args_complete_p (const std::string &args)
>>>            host to determine what is, or isn't a special character, when
>>>            really, this is a function of the target.  */
>>>   #ifndef _WIN32
>>> -      else if (*input == '\'')
>>> +      else if (c == '\'')
>>>           squote = true;
>>>   #endif
>>> -      else if (*input == '"')
>>> +      else if (c == '"')
>>>           dquote = true;
>>>       }
>>>
>>> base-commit: 0a153c58a0ab68c6fa349d2ad0bf6a42e043ab23
>>> -- 
>>> 2.51.0
>>
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-06  8:47   ` Tom de Vries
  2026-01-06  9:29     ` Tom de Vries
  2026-01-06 14:53     ` Tom de Vries
@ 2026-01-07 10:46     ` Andrew Burgess
  2026-01-07 11:21       ` Tom de Vries
  2 siblings, 1 reply; 14+ messages in thread
From: Andrew Burgess @ 2026-01-07 10:46 UTC (permalink / raw)
  To: Tom de Vries, gdb-patches

Tom de Vries <tdevries@suse.de> writes:

> On 1/5/26 8:57 PM, Andrew Burgess wrote:
>> Tom de Vries <tdevries@suse.de> writes:
>> 
>>> PR gdb/33754 reports a heap-buffer-overflow here in args_complete_p:
>>> ...
>>>    while (*input != '\0')
>>> ...
>>>
>>> Fix this by introducing a lambda function at that safely handles all char
>>> array accesses.
>> 
>> Sorry to be a bore, but after reading this commit, and the bug report,
>> it's still not obvious to me where the overflow actually occurs.
>> 
>> I totally accept that this code is broken, but as I introduced this bug,
>> I wanted to learn from this mistake, but this commit doesn't really
>> explain what mistake is being fixed.
>> 
>> Do you think you could explain what's actually going wrong here?
>> 
>
> Hi Andrew,
>
> agreed, it's not spelled out, sorry about that.
>
> So, the heap-buffer-overflow happens with:
> ...
> (gdb) p args
> $1 = "\"first arg\" \"\" \"third-arg\" \"'\" \"\\\"\" \" \" \"\" "
> ...
> and it's the fact that we don't check for '\0' after skip_spaces that is 
> the problem.  I think it should be possible to reproduce the problem 
> with args == " ".

Thanks for breaking it down for me.  I don't really like the original
lambda function approach that was proposed, I'd prefer to just see the
correct checks added to the loop.  More inline below...

>
> So a minimal fix for this problem is:
> ...
> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
> index 1a7daf1461b..fdcd4e4ba96 100644
> --- a/gdb/infcmd.c
> +++ b/gdb/infcmd.c
> @@ -131,6 +131,8 @@ args_complete_p (const std::string &args)
>     while (*input != '\0')
>       {
>         input = skip_spaces (input);
> +      if (*input == '\0')
> +	break;

I think I prefer this to Tom's proposed 'for' loop, but I don't feel
super strongly each way.

>
>         if (squote)
>   	{
> ...
>
> But the strchr problem is also there, so this:
> ...
> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
> index 1a7daf1461b..4bcd523f79b 100644
> --- a/gdb/infcmd.c
> +++ b/gdb/infcmd.c
> @@ -177,6 +177,8 @@ args_complete_p (const std::string &args)
>   	    dquote = true;
>   	}
>
> +      if (*input == '\0')
> +	break;

I'd replace this with 'gdb_assert (*input != '\0');', and then use
something like the extra check I proposed next to the strchr calls.  Or
maybe we should add a new helper function in gdbsupport/ like:

  static char *
  strchr_not_null (char *s, int c)
  {
    if (c == '\0')
      return nullptr;
  
    return strchr (s, c);
  }
  
  static const char *
  strchr_not_null (const char *s, int c)
  {
    return strchr_not_null (const_cast<char *> (s), c);
  }

which wraps the null check.  Either would be fine with me.

I also liked the selftests you added, I extended them to:

  static void
  check_str (const std::string &str, bool complete_p)
  {
    const char *end;
  
    SELF_CHECK (args_complete_p (str, &end) == complete_p);
    SELF_CHECK (end == str.data () + str.size ());
  }
  
  static void
  infcmd_args_complete_p_tests (void)
  {
    check_str (" ", true);
    check_str ("\\", true);
    check_str ("\"\\", false);
  }

which covers all the bugs that are being fixed here.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-07 10:46     ` Andrew Burgess
@ 2026-01-07 11:21       ` Tom de Vries
  2026-01-07 15:01         ` Andrew Burgess
  0 siblings, 1 reply; 14+ messages in thread
From: Tom de Vries @ 2026-01-07 11:21 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 1/7/26 11:46 AM, Andrew Burgess wrote:
> Tom de Vries <tdevries@suse.de> writes:
> 
>> On 1/5/26 8:57 PM, Andrew Burgess wrote:
>>> Tom de Vries <tdevries@suse.de> writes:
>>>
>>>> PR gdb/33754 reports a heap-buffer-overflow here in args_complete_p:
>>>> ...
>>>>     while (*input != '\0')
>>>> ...
>>>>
>>>> Fix this by introducing a lambda function at that safely handles all char
>>>> array accesses.
>>>
>>> Sorry to be a bore, but after reading this commit, and the bug report,
>>> it's still not obvious to me where the overflow actually occurs.
>>>
>>> I totally accept that this code is broken, but as I introduced this bug,
>>> I wanted to learn from this mistake, but this commit doesn't really
>>> explain what mistake is being fixed.
>>>
>>> Do you think you could explain what's actually going wrong here?
>>>
>>
>> Hi Andrew,
>>
>> agreed, it's not spelled out, sorry about that.
>>
>> So, the heap-buffer-overflow happens with:
>> ...
>> (gdb) p args
>> $1 = "\"first arg\" \"\" \"third-arg\" \"'\" \"\\\"\" \" \" \"\" "
>> ...
>> and it's the fact that we don't check for '\0' after skip_spaces that is
>> the problem.  I think it should be possible to reproduce the problem
>> with args == " ".
> 
> Thanks for breaking it down for me.  I don't really like the original
> lambda function approach that was proposed, I'd prefer to just see the
> correct checks added to the loop.  More inline below...
> 
>>
>> So a minimal fix for this problem is:
>> ...
>> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
>> index 1a7daf1461b..fdcd4e4ba96 100644
>> --- a/gdb/infcmd.c
>> +++ b/gdb/infcmd.c
>> @@ -131,6 +131,8 @@ args_complete_p (const std::string &args)
>>      while (*input != '\0')
>>        {
>>          input = skip_spaces (input);
>> +      if (*input == '\0')
>> +	break;
> 
> I think I prefer this to Tom's proposed 'for' loop, but I don't feel
> super strongly each way.
> 
>>
>>          if (squote)
>>    	{
>> ...
>>
>> But the strchr problem is also there, so this:
>> ...
>> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
>> index 1a7daf1461b..4bcd523f79b 100644
>> --- a/gdb/infcmd.c
>> +++ b/gdb/infcmd.c
>> @@ -177,6 +177,8 @@ args_complete_p (const std::string &args)
>>    	    dquote = true;
>>    	}
>>
>> +      if (*input == '\0')
>> +	break;
> 
> I'd replace this with 'gdb_assert (*input != '\0');', and then use
> something like the extra check I proposed next to the strchr calls.  Or
> maybe we should add a new helper function in gdbsupport/ like:
> 
>    static char *
>    strchr_not_null (char *s, int c)
>    {
>      if (c == '\0')
>        return nullptr;
>    
>      return strchr (s, c);
>    }
>    
>    static const char *
>    strchr_not_null (const char *s, int c)
>    {
>      return strchr_not_null (const_cast<char *> (s), c);
>    }
> 
> which wraps the null check.  Either would be fine with me.
> 
> I also liked the selftests you added, I extended them to:
> 
>    static void
>    check_str (const std::string &str, bool complete_p)
>    {
>      const char *end;
>    
>      SELF_CHECK (args_complete_p (str, &end) == complete_p);
>      SELF_CHECK (end == str.data () + str.size ());
>    }
>    
>    static void
>    infcmd_args_complete_p_tests (void)
>    {
>      check_str (" ", true);
>      check_str ("\\", true);
>      check_str ("\"\\", false);
>    }
> 
> which covers all the bugs that are being fixed here.
> 

Hi Andrew,

thanks for the comments.

But by now, a v2 was submitted, approved and committed.

So perhaps you want to submit a refactoring patch addressing some of 
your insights here.  Otherwise, I can take it further.  Let me know what 
you prefer.

Thanks,
- Tom

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-07 11:21       ` Tom de Vries
@ 2026-01-07 15:01         ` Andrew Burgess
  2026-01-07 18:13           ` Tom Tromey
  0 siblings, 1 reply; 14+ messages in thread
From: Andrew Burgess @ 2026-01-07 15:01 UTC (permalink / raw)
  To: Tom de Vries, gdb-patches

Tom de Vries <tdevries@suse.de> writes:

> On 1/7/26 11:46 AM, Andrew Burgess wrote:
>> Tom de Vries <tdevries@suse.de> writes:
>> 
>>> On 1/5/26 8:57 PM, Andrew Burgess wrote:
>>>> Tom de Vries <tdevries@suse.de> writes:
>>>>
>>>>> PR gdb/33754 reports a heap-buffer-overflow here in args_complete_p:
>>>>> ...
>>>>>     while (*input != '\0')
>>>>> ...
>>>>>
>>>>> Fix this by introducing a lambda function at that safely handles all char
>>>>> array accesses.
>>>>
>>>> Sorry to be a bore, but after reading this commit, and the bug report,
>>>> it's still not obvious to me where the overflow actually occurs.
>>>>
>>>> I totally accept that this code is broken, but as I introduced this bug,
>>>> I wanted to learn from this mistake, but this commit doesn't really
>>>> explain what mistake is being fixed.
>>>>
>>>> Do you think you could explain what's actually going wrong here?
>>>>
>>>
>>> Hi Andrew,
>>>
>>> agreed, it's not spelled out, sorry about that.
>>>
>>> So, the heap-buffer-overflow happens with:
>>> ...
>>> (gdb) p args
>>> $1 = "\"first arg\" \"\" \"third-arg\" \"'\" \"\\\"\" \" \" \"\" "
>>> ...
>>> and it's the fact that we don't check for '\0' after skip_spaces that is
>>> the problem.  I think it should be possible to reproduce the problem
>>> with args == " ".
>> 
>> Thanks for breaking it down for me.  I don't really like the original
>> lambda function approach that was proposed, I'd prefer to just see the
>> correct checks added to the loop.  More inline below...
>> 
>>>
>>> So a minimal fix for this problem is:
>>> ...
>>> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
>>> index 1a7daf1461b..fdcd4e4ba96 100644
>>> --- a/gdb/infcmd.c
>>> +++ b/gdb/infcmd.c
>>> @@ -131,6 +131,8 @@ args_complete_p (const std::string &args)
>>>      while (*input != '\0')
>>>        {
>>>          input = skip_spaces (input);
>>> +      if (*input == '\0')
>>> +	break;
>> 
>> I think I prefer this to Tom's proposed 'for' loop, but I don't feel
>> super strongly each way.
>> 
>>>
>>>          if (squote)
>>>    	{
>>> ...
>>>
>>> But the strchr problem is also there, so this:
>>> ...
>>> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
>>> index 1a7daf1461b..4bcd523f79b 100644
>>> --- a/gdb/infcmd.c
>>> +++ b/gdb/infcmd.c
>>> @@ -177,6 +177,8 @@ args_complete_p (const std::string &args)
>>>    	    dquote = true;
>>>    	}
>>>
>>> +      if (*input == '\0')
>>> +	break;
>> 
>> I'd replace this with 'gdb_assert (*input != '\0');', and then use
>> something like the extra check I proposed next to the strchr calls.  Or
>> maybe we should add a new helper function in gdbsupport/ like:
>> 
>>    static char *
>>    strchr_not_null (char *s, int c)
>>    {
>>      if (c == '\0')
>>        return nullptr;
>>    
>>      return strchr (s, c);
>>    }
>>    
>>    static const char *
>>    strchr_not_null (const char *s, int c)
>>    {
>>      return strchr_not_null (const_cast<char *> (s), c);
>>    }
>> 
>> which wraps the null check.  Either would be fine with me.
>> 
>> I also liked the selftests you added, I extended them to:
>> 
>>    static void
>>    check_str (const std::string &str, bool complete_p)
>>    {
>>      const char *end;
>>    
>>      SELF_CHECK (args_complete_p (str, &end) == complete_p);
>>      SELF_CHECK (end == str.data () + str.size ());
>>    }
>>    
>>    static void
>>    infcmd_args_complete_p_tests (void)
>>    {
>>      check_str (" ", true);
>>      check_str ("\\", true);
>>      check_str ("\"\\", false);
>>    }
>> 
>> which covers all the bugs that are being fixed here.
>> 
>
> Hi Andrew,
>
> thanks for the comments.
>
> But by now, a v2 was submitted, approved and committed.
>
> So perhaps you want to submit a refactoring patch addressing some of 
> your insights here.  Otherwise, I can take it further.  Let me know what 
> you prefer.

No, that's fine.  I do with more people would post vN patches as follow
ups to their original messages, it would make tracking revisions so much
easier.

Maybe one day we'll move off the mailing list model.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-07 15:01         ` Andrew Burgess
@ 2026-01-07 18:13           ` Tom Tromey
  2026-01-12 11:42             ` Andrew Burgess
  0 siblings, 1 reply; 14+ messages in thread
From: Tom Tromey @ 2026-01-07 18:13 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Tom de Vries, gdb-patches

Andrew> Maybe one day we'll move off the mailing list model.

What would it take?

What I'd like ideally is that landing patches be done via the UI.
That way we could have some guarantee that what lands is what is
reviewed.  I don't think this is possible with the current setup though.

For my part I think my main worry is that I'd miss patches that I want
to be involved in.

Anyway it seems clear that the current approach isn't working extremely
well.  I don't know about anyone else but I have >3500 "pending"
messages in gdb-patches, some dating back years.  And probably more than
half of those are actual unreviewed patches.

Tom

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p
  2026-01-07 18:13           ` Tom Tromey
@ 2026-01-12 11:42             ` Andrew Burgess
  0 siblings, 0 replies; 14+ messages in thread
From: Andrew Burgess @ 2026-01-12 11:42 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Tom de Vries, gdb-patches

Tom Tromey <tom@tromey.com> writes:

> Andrew> Maybe one day we'll move off the mailing list model.
>
> What would it take?

I cannot imagine a time in the near future when everyone involved in GDB
would agree to such a change.  But maybe if the majority of the largest
contributors wanted the change then we could figure something out?

>
> What I'd like ideally is that landing patches be done via the UI.
> That way we could have some guarantee that what lands is what is
> reviewed.  I don't think this is possible with the current setup though.

This would be my ideal too.  This opens the door real automated
pre-commit testing.  There are setups which do this sort of thing with a
mailing list (if I remember correctly), but it still relies on people
manually checking prior to pushing.

> For my part I think my main worry is that I'd miss patches that I want
> to be involved in.

True.  But at least for me, that's already the case as....

> Anyway it seems clear that the current approach isn't working extremely
> well.  I don't know about anyone else but I have >3500 "pending"
> messages in gdb-patches, some dating back years.  And probably more than
> half of those are actual unreviewed patches.

...I too have a huge backlog of unread patches.  I am trying to do more
reviews, but I just cannot read patches fast enough.  Anyway, I haven't
investigated, but my ideal system would be managed via a web UI, but
could still send users email when new patches are posted.  That way, if
folks find it easier to track their todo list via email, they could
still do that.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-01-12 11:44 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-03 14:55 [PATCH] [gdb] Fix heap-buffer-overflow in args_complete_p Tom de Vries
2026-01-05 16:38 ` Tom Tromey
2026-01-06 14:51   ` Tom de Vries
2026-01-05 19:57 ` Andrew Burgess
2026-01-05 20:02   ` Andrew Burgess
2026-01-05 20:09   ` Tom Tromey
2026-01-06  8:47   ` Tom de Vries
2026-01-06  9:29     ` Tom de Vries
2026-01-06 14:53     ` Tom de Vries
2026-01-07 10:46     ` Andrew Burgess
2026-01-07 11:21       ` Tom de Vries
2026-01-07 15:01         ` Andrew Burgess
2026-01-07 18:13           ` Tom Tromey
2026-01-12 11:42             ` Andrew Burgess

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox