On Tue, Feb 4, 2025 at 10:12 PM Qing Zhao <qing.z...@oracle.com> wrote:
>
> Hi,
>
> One of our big application segv in libgcc code while unwinding the stack.
>
> This is a random crash while the application throws a c++ exception and
> unwinds the stack. Incidents are random and never can be reproduced by any
> test case.
>
> The libgcc that is used is based on GCC 8.
>
> Fortunately, we got some information through the stack trace, and narrowed
> down the crash is in the routine "init_object" of libgcc/unwind-dw2-pde.c:
>
> 777           count = classify_object_over_fdes (ob, ob->u.single);
>
> when loading the 2nd parameter of the call to "classify_object_over_fdes".
> i.e, the address that is pointed by "ob->u.single" is invalid.
>
> And the outer caller we can track back is the following line 1066 of the 
> routine
> "_Unwind_Find_FDE":
>
> 1060   /* Classify and search the objects we've not yet processed.  */
> 1061   while ((ob = unseen_objects))
> 1062     {
> 1063       struct object **p;
> 1064
> 1065       unseen_objects = ob->next;
> 1066       f = search_object (ob, pc);
>
> Then we suspected that the construction of the static variable 
> "unseen_objects"
> might have some bug.
>
> Then after studying the source code that constructs "unseen_objects" in
> libgcc/unwind-dw2-pde.c, I found an issue in the routines "__register_frame"
> and "__register_frame_table" as following:
>
>  127 void
>  128 __register_frame (void *begin)
>  129 {
>  130   struct object *ob;
>  131
>  132   /* If .eh_frame is empty, don't register at all.  */
>  133   if (*(uword *) begin == 0)
>  134     return;
>  135
>  136   ob = malloc (sizeof (struct object));
>  137   __register_frame_info (begin, ob);
>  138 }
>
>  181 void
>  182 __register_frame_table (void *begin)
>  183 {
>  184   struct object *ob = malloc (sizeof (struct object));
>  185   __register_frame_info_table (begin, ob);
>  186 }
>  187
>
> In the above, one obvious issue in the source code is at line 136, line 137,
> and line 184 and line 185: the return value of call to "malloc" is not checked
> against NULL before it was passed as the second parameter of the follwoing 
> call.
>
> This might cause unpredicted random behavior.
>
> I checked the latest trunk GCC libgcc, the same issue is there too.
>
> This patch is for the latest trunk GCC.
>
> Please let me know any comments on this?

I think I've seen this elsewhere -- the issue is the unwind register API does
not allow for failures but I also think calling abort() is bad.

Are you calling this from a JIT context or so?  We're assuming that at program
start malloc() will not fail.

The proper solution is probably to add an alternate ABI which has a way to fail
during registration.

Richard.

> thanks.
>
> Qing
>
>
> =======================
>
> Fix two issues in libgcc/unwind-dw2-fde.c:
>
> A. The returned value of call to malloc is not checked against NULL.
>    This might cause unpredicted behavior during stack unwinding.
>
> B. Check for null begin parameter (as well as pointer to null) in
>    __register_frame_info_table_bases and __register_frame_table.
>
> libgcc/ChangeLog:
>
>         * unwind-dw2-fde.c (__register_frame): Check the return value of call
>         to malloc.
>         (__register_frame_info_table_bases): Check for null begin parameter.
>         (__register_frame_table): Check the return value of call to malloc,
>         Check for null begin parameter.
> ---
>  libgcc/unwind-dw2-fde.c | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
>
> diff --git a/libgcc/unwind-dw2-fde.c b/libgcc/unwind-dw2-fde.c
> index cdfd3974c99..f0bc29d682a 100644
> --- a/libgcc/unwind-dw2-fde.c
> +++ b/libgcc/unwind-dw2-fde.c
> @@ -169,6 +169,8 @@ __register_frame (void *begin)
>      return;
>
>    ob = malloc (sizeof (struct object));
> +  if (ob == NULL)
> +    abort ();
>    __register_frame_info (begin, ob);
>  }
>
> @@ -180,6 +182,10 @@ void
>  __register_frame_info_table_bases (void *begin, struct object *ob,
>                                    void *tbase, void *dbase)
>  {
> +  /* If .eh_frame is empty, don't register at all.  */
> +  if ((const uword *) begin == 0 || *(const uword *) begin == 0)
> +    return;
> +
>    ob->pc_begin = (void *)-1;
>    ob->tbase = tbase;
>    ob->dbase = dbase;
> @@ -210,7 +216,13 @@ __register_frame_info_table (void *begin, struct object 
> *ob)
>  void
>  __register_frame_table (void *begin)
>  {
> +    /* If .eh_frame is empty, don't register at all.  */
> +  if (*(uword *) begin == 0)
> +    return;
> +
>    struct object *ob = malloc (sizeof (struct object));
> +  if (ob == NULL)
> +    abort ();
>    __register_frame_info_table (begin, ob);
>  }
>
> --
> 2.31.1
>

Reply via email to