Hi Mark,

On Thu, Mar 27, 2025 at 4:51 AM Mark Wielaard <m...@klomp.org> wrote:
>
> Hi Aaron,
>
> On Thu, Mar 27, 2025 at 12:07:18AM -0400, Aaron Merey wrote:
> > Ensure that dwarf_lock is held before accessing next_tu_offset and
> > next_cu_offset.
> >
> > This fixes a TOCTOU bug in __libdw_findcu that causes NULL to be
> > incorrectly returned.
>
> Could you explain what the issue is in a few more words?
>
> I am not sure I fully follow what the lock protects here precisely.
> The patch and the mention of TOCTOU seem to indicate it is about this in 
> particular:
>
>   struct Dwarf_CU **found = eu_tfind (&fake, tree, findcu_cb);
>   if (found != NULL)
>     return *found;
>
> Does that mean that despite eu_tfind internally taking a lock on the
> tree lock, the returned result can still change after the call? If
> that is the case how exactly does that happen?

Multiple threads might call eu_tfind before the desired Dwarf_CU has
been added to the search_tree. In this case `found == NULL` for both
threads. The first thread to grab the mutex will successfully find the
Dwarf_CU, add it to the search_tree and return it. For the second
thread, `if (start < *next_offset)` will evaluate to true because the
first thread will have already updated *next_offset. __libdw_seterrno
(DWARF_E_INVALID_DWARF) gets set and NULL is returned.

>
> Or isn't this the part that needs guarding?

Yes this part needs guarding. The mutex_lock could be moved from the
beginning of __libdw_findcu to immediately before the call to
eu_tfind.

> Asking because if it is this eu_tfind check return != NULL construct
> then there are more places that might need extra guarding (or we have
> to write better eu-search wrappers?)

Yes there are other eu_tfind calls that are susceptible to this bug.
If eu_tfind returns NULL, a lock should continue to be held until the
thread has a chance to add the result to the search_tree. I will work
on a patch to improve the eu-search wrappers.

Aaron

>
> Thanks,
>
> Mark
>
> > Signed-off-by: Aaron Merey <ame...@redhat.com>
> > ---
> >  libdw/libdw_findcu.c | 9 ++++++---
> >  1 file changed, 6 insertions(+), 3 deletions(-)
> >
> > diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c
> > index 8805af9b..0e4dcc37 100644
> > --- a/libdw/libdw_findcu.c
> > +++ b/libdw/libdw_findcu.c
> > @@ -240,6 +240,8 @@ struct Dwarf_CU *
> >  internal_function
> >  __libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool v4_debug_types)
> >  {
> > +  mutex_lock (dbg->dwarf_lock);
> > +
> >    search_tree *tree = v4_debug_types ? &dbg->tu_tree : &dbg->cu_tree;
> >    Dwarf_Off *next_offset
> >      = v4_debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
> > @@ -249,9 +251,10 @@ __libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool 
> > v4_debug_types)
> >    struct Dwarf_CU **found = eu_tfind (&fake, tree, findcu_cb);
> >    struct Dwarf_CU *result = NULL;
> >    if (found != NULL)
> > -    return *found;
> > -
> > -  mutex_lock (dbg->dwarf_lock);
> > +    {
> > +      mutex_unlock (dbg->dwarf_lock);
> > +      return *found;
> > +    }
> >
> >    if (start < *next_offset)
> >      __libdw_seterrno (DWARF_E_INVALID_DWARF);
> > --
> > 2.49.0
> >
>

Reply via email to