Subject: Infinite loop in __cxa_finalize
To: None <tech-userlevel@NetBSD.org>
From: Krister Walfridsson <cato@df.lth.se>
List: tech-userlevel
Date: 08/04/2007 14:40:42
The gcc testsuite finds two bugs in the NetBSD implementation of
__cxa_atexit() and __cxa_finalize().

One bug is that programs registering atexit handlers from atexit
handlers may go into an infinite loop, as demonstrated by:

   #include <stdlib.h>

   void two(void) {
   }

   void one(void) {
           atexit(two);
   }

   int main(void) {
           atexit(one);
           return 0;
   }

The reason is that the struct atexit_handler representing this function
is marked as unused before calling __cxa_atexit().  __cxa_atexit() does
therefore reuse this structure, but it has not been removed from the
list of handlers yet, so the list becomes a loop when the element is
re-inserted into the list.

The patch below fix the problem.  Is it OK to commit?

    /Krister


Index: atexit.c
===================================================================
RCS file: /cvsroot/src/lib/libc/stdlib/atexit.c,v
retrieving revision 1.17
diff -u -p -r1.17 atexit.c
--- atexit.c	12 Jun 2005 05:21:28 -0000	1.17
+++ atexit.c	4 Aug 2007 12:32:15 -0000
@@ -101,7 +101,7 @@ atexit_handler_alloc(void *dso)
  	if (dso == NULL) {
  		for (i = 0; i < NSTATIC_HANDLERS; i++) {
  			ah = &atexit_handler0[i];
-			if (ah->ah_atexit == NULL) {
+			if (ah->ah_atexit == NULL && ah->ah_next == NULL) {
  				/* Slot is free. */
  				return (ah);
  			}
@@ -207,7 +207,9 @@ __cxa_finalize(void *dso)

  			if (call_depth == 1) {
  				*prevp = ah->ah_next;
-				if (! STATIC_HANDLER_P(ah)) {
+				if (STATIC_HANDLER_P(ah))
+					ah->ah_next = NULL;
+				else {
  					ah->ah_next = dead_handlers;
  					dead_handlers = ah;
  				}