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 >