* 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-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