From: Bernd Edlinger <bernd.edlinger@hotmail.de>
To: Tom de Vries <tdevries@suse.de>,
"gdb-patches@sourceware.org" <gdb-patches@sourceware.org>
Subject: Re: [PATCH v3 1/2] [gdb/symtab] Fix an out of bounds array access in find_epilogue_using_linetable
Date: Sun, 7 Apr 2024 10:17:16 +0200 [thread overview]
Message-ID: <PAXP193MB129604753C1E9B0BCD423A88E4012@PAXP193MB1296.EURP193.PROD.OUTLOOK.COM> (raw)
In-Reply-To: <544ecc72-916f-436e-b4f2-093bea6882a9@suse.de>
On 4/6/24 10:29, Tom de Vries wrote:
> On 4/6/24 07:03, Bernd Edlinger wrote:
>> On 4/5/24 17:10, Tom de Vries wrote:
>>>
>>> diff --git a/gdb/symtab.c b/gdb/symtab.c
>>> index 86603dfebc3..0c126d99cd4 100644
>>> --- a/gdb/symtab.c
>>> +++ b/gdb/symtab.c
>>> @@ -4166,10 +4166,14 @@ find_epilogue_using_linetable (CORE_ADDR func_addr)
>>> = unrelocated_addr (end_pc - objfile->text_section_offset ());
>>> const linetable *linetable = sal.symtab->linetable ();
>>> - /* This should find the last linetable entry of the current function.
>>> - It is probably where the epilogue begins, but since the DWARF 5
>>> - spec doesn't guarantee it, we iterate backwards through the function
>>> - until we either find it or are sure that it doesn't exist. */
>>> + if (linetable->nitems == 0)
>>> + {
>>> + /* Empty line table. */
>>> + return {};
>>> + }
>>> +
Hmm, this can be an assertion, because
the line table was found by find_pc_line (start_pc, 0);
so the linetable is guaranteed to be non-empty.
BTW: empty linetables are usually NULL pointers,
so that probably the assertion should
also assert like
gdb_assert (linetable != NULL && linetable->nitems > 0);
>>> + /* Find the first linetable entry after the current function. Note that
>>> + this also may be an end_sequence entry. */
>>> auto it = std::lower_bound
>>> (linetable->item, linetable->item + linetable->nitems, unrel_end,
>>> [] (const linetable_entry <e, unrelocated_addr pc)
>>> @@ -4177,13 +4181,74 @@ find_epilogue_using_linetable (CORE_ADDR func_addr)
>>> return lte.unrelocated_pc () < pc;
>>> });
>>> - while (it->unrelocated_pc () >= unrel_start)
>>> - {
>>> - if (it->epilogue_begin)
>>> - return {it->pc (objfile)};
>>> - it --;
>>> - }
>>> + if (it == linetable->item + linetable->nitems)
>>> + {
>>> + /* We couldn't find either:
>>> + - a linetable entry starting the function after the current
>>> + function, or
>>> + - an end_sequence entry that terminates the current function
>>> + at unrel_end.
>>> + This can happen when the linetable doesn't describe the full
>>> + extent of the function. Even though this is a corner case, which
>>> + may not happen other than in dwarf assembly test-cases, let's
>>> + handle this.
>>> +
>>> + Move to the last entry in the linetable, and check that it's an
>>> + end_sequence terminating the current function. */
>>> + gdb_assert (it != &linetable->item[0]);
>>> + it--;
>>> + if (!(it->line == 0
>>> + && unrel_start <= it->unrelocated_pc ()
>>> + && it->unrelocated_pc () < unrel_end))
>>> + return {};
>>
>> Why is this check necessary here, and not also when
>> this is not the last function in the line-table?
>>
>> And why is this check necessary at all?
>>
>
> It spells out as much as possible the specific conditions of the corner-case we're handling.
>
> We could also just simply handle the cornercase by returning {}, I went forth and back a bit on that, and decided to support it on the basis that at least currently we have dwarf assembly test-cases in the testsuite that trigger this path, though I've submitted a series to clean that up.
>
> But I'm still on the fence about this, if you prefer a "return {}" I'm fine with that.
>
>>> + }
>>> + else
>>> + gdb_assert (unrel_end <= it->unrelocated_pc ());
>>
>> Why do you not check that 'it' points to an end_sequence
>> at exactly unrel_end?
>> It could be anything at an address much higher PC than unrel_end.
>
> This assert spells out the post-condition of the call to std::lower_bound, in case it found an entry.
>
> If there's debug info where one line entry straddles two functions, the call returns the entry after it, which doesn't have address unrel_end.
>
> Having said that, we can unsupport such a scenario by doing:
> ...
> else
> {
> if (unrel_end < it->unrelocated_pc ())
> return {};
> gdb_assert (unrel_end == it->unrelocated_pc ());
> ...
>
I think we should not look at the `it` in any case.
If there is an inconsistency in the debug info, a
debug message that can be enabled in maintainer mode
would be good enough.
But even in this case, I would prefer a best effort,
and continue whenever possible.
If you look at skip_prologue_using_linetable
you see that it does stop the search immediately, when
it->unrelocated_pc() reaches unrel_end, or when the
end of the linetable is reached:
for (;
(it < linetable->item + linetable->nitems
&& it->unrelocated_pc () < unrel_end);
it++)
if (it->prologue_end)
return {it->pc (objfile)};
Therefore I would like find_epilogue_using_linetable
to use the same algorithm just in reverse direction.
Which means always do `it--` first before using `it`.
After all this is just a partial function range,
it can end with a jump or a return, and in both
cases the linetable entry at unrel_end can belong
to a completely different function, and it is not
guaranteed to be an end_sequence entry.
BTW: I am not sure what happens if the function
has multiple line tables, e.g. because of inline
functions, or #include to pull in parts of the function
body. In that case I would expect that the line
table found by find_pc_line (start_pc, 0);
may be covering the prologue area, while the epilogue
may be missing.
Maybe find_pc_line (end_pc - 1, 0); would be better
candidate for a line table covering the epilogue area?
Thanks.
Bernd.
> Thanks,
> - Tom
next prev parent reply other threads:[~2024-04-07 8:15 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-05 15:10 Tom de Vries
2024-04-05 15:10 ` [PATCH v3 2/2] [gdb/testsuite] Add gdb.dwarf2/dw2-epilogue-begin-2.exp Tom de Vries
2024-04-06 5:03 ` [PATCH v3 1/2] [gdb/symtab] Fix an out of bounds array access in find_epilogue_using_linetable Bernd Edlinger
2024-04-06 8:29 ` Tom de Vries
2024-04-07 8:17 ` Bernd Edlinger [this message]
2024-04-08 12:58 ` Tom de Vries
2024-04-08 14:41 ` Bernd Edlinger
2024-04-09 9:37 ` Tom de Vries
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=PAXP193MB129604753C1E9B0BCD423A88E4012@PAXP193MB1296.EURP193.PROD.OUTLOOK.COM \
--to=bernd.edlinger@hotmail.de \
--cc=gdb-patches@sourceware.org \
--cc=tdevries@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).