Register _cleanup function with atexit

 * Register cleanup function with atexit
   instead of calling it explicitly on
   exit()
 * abort() no longer calls _cleanup:
   Flushing stdio buffers on abort is no
   longer required by POSIX.
 * dlmalloc no longer need to reset cleanup
   (see above)
 * Upstream findfp.c makebuf.c setvbuf.cexit.c
   to openbsd versions.

Bug: 14415367
Change-Id: I277058852485a9d3dbb13e5c232db5f9948d78ac
diff --git a/libc/Android.mk b/libc/Android.mk
index 2d56af0..82dcc55 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -70,7 +70,6 @@
     bionic/sigsetmask.c \
     bionic/system_properties_compat.c \
     bionic/unlockpt.c \
-    stdio/findfp.c \
     stdio/snprintf.c\
     stdio/sprintf.c \
     stdlib/atexit.c \
@@ -227,9 +226,7 @@
     upstream-freebsd/lib/libc/stdio/fclose.c \
     upstream-freebsd/lib/libc/stdio/flags.c \
     upstream-freebsd/lib/libc/stdio/fopen.c \
-    upstream-freebsd/lib/libc/stdio/makebuf.c \
     upstream-freebsd/lib/libc/stdio/mktemp.c \
-    upstream-freebsd/lib/libc/stdio/setvbuf.c \
     upstream-freebsd/lib/libc/stdlib/abs.c \
     upstream-freebsd/lib/libc/stdlib/getopt_long.c \
     upstream-freebsd/lib/libc/stdlib/imaxabs.c \
@@ -279,7 +276,6 @@
     upstream-netbsd/lib/libc/stdlib/div.c \
     upstream-netbsd/lib/libc/stdlib/drand48.c \
     upstream-netbsd/lib/libc/stdlib/erand48.c \
-    upstream-netbsd/lib/libc/stdlib/exit.c \
     upstream-netbsd/lib/libc/stdlib/jrand48.c \
     upstream-netbsd/lib/libc/stdlib/ldiv.c \
     upstream-netbsd/lib/libc/stdlib/lldiv.c \
@@ -381,6 +377,7 @@
     upstream-openbsd/lib/libc/stdio/fgetwc.c \
     upstream-openbsd/lib/libc/stdio/fgetws.c \
     upstream-openbsd/lib/libc/stdio/fileno.c \
+    upstream-openbsd/lib/libc/stdio/findfp.c \
     upstream-openbsd/lib/libc/stdio/fprintf.c \
     upstream-openbsd/lib/libc/stdio/fpurge.c \
     upstream-openbsd/lib/libc/stdio/fputc.c \
@@ -407,6 +404,7 @@
     upstream-openbsd/lib/libc/stdio/gets.c \
     upstream-openbsd/lib/libc/stdio/getwc.c \
     upstream-openbsd/lib/libc/stdio/getwchar.c \
+    upstream-openbsd/lib/libc/stdio/makebuf.c \
     upstream-openbsd/lib/libc/stdio/perror.c \
     upstream-openbsd/lib/libc/stdio/printf.c \
     upstream-openbsd/lib/libc/stdio/putc.c \
@@ -422,6 +420,7 @@
     upstream-openbsd/lib/libc/stdio/scanf.c \
     upstream-openbsd/lib/libc/stdio/setbuf.c \
     upstream-openbsd/lib/libc/stdio/setbuffer.c \
+    upstream-openbsd/lib/libc/stdio/setvbuf.c \
     upstream-openbsd/lib/libc/stdio/sscanf.c \
     upstream-openbsd/lib/libc/stdio/stdio.c \
     upstream-openbsd/lib/libc/stdio/swprintf.c \
@@ -451,6 +450,7 @@
     upstream-openbsd/lib/libc/stdlib/atoi.c \
     upstream-openbsd/lib/libc/stdlib/atol.c \
     upstream-openbsd/lib/libc/stdlib/atoll.c \
+    upstream-openbsd/lib/libc/stdlib/exit.c \
     upstream-openbsd/lib/libc/stdlib/getenv.c \
     upstream-openbsd/lib/libc/stdlib/setenv.c \
     upstream-openbsd/lib/libc/stdlib/strtoimax.c \
diff --git a/libc/bionic/abort.cpp b/libc/bionic/abort.cpp
index 6fcdfda..69ac0e5 100644
--- a/libc/bionic/abort.cpp
+++ b/libc/bionic/abort.cpp
@@ -32,8 +32,6 @@
 #include <unistd.h>
 #include "atexit.h"
 
-__LIBC_HIDDEN__ void (*__cleanup)();
-
 #ifdef __arm__
 extern "C" __LIBC_HIDDEN__ void __libc_android_abort()
 #else
@@ -47,11 +45,6 @@
   sigdelset(&mask, SIGABRT);
   sigprocmask(SIG_SETMASK, &mask, NULL);
 
-  // POSIX requires we flush stdio buffers on abort.
-  if (__cleanup) {
-    (*__cleanup)();
-  }
-
   raise(SIGABRT);
 
   // If SIGABRT ignored, or caught and the handler returns,
diff --git a/libc/bionic/dlmalloc.c b/libc/bionic/dlmalloc.c
index 66a825b..3a615d2 100644
--- a/libc/bionic/dlmalloc.c
+++ b/libc/bionic/dlmalloc.c
@@ -34,10 +34,7 @@
 // Ugly inclusion of C file so that bionic specific #defines configure dlmalloc.
 #include "../upstream-dlmalloc/malloc.c"
 
-extern void (*__cleanup)();
-
 static void __bionic_heap_corruption_error(const char* function) {
-  __cleanup = NULL; // The heap is corrupt. We can forget trying to shut down stdio.
   __libc_fatal("heap corruption detected by %s", function);
 }
 
diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c
index b051e22..c607206 100644
--- a/libc/stdlib/atexit.c
+++ b/libc/stdlib/atexit.c
@@ -104,10 +104,10 @@
 {
 	struct atexit *p = __atexit;
 	struct atexit_fn *fnp;
-	int pgsize = getpagesize();
+	size_t pgsize = sysconf(_SC_PAGESIZE);
 	int ret = -1;
 
-	if (pgsize < (int)sizeof(*p))
+	if (pgsize < sizeof(*p))
 		return (-1);
 	_ATEXIT_LOCK();
 	p = __atexit;
@@ -216,3 +216,41 @@
 	}
 	_ATEXIT_UNLOCK();
 }
+
+/*
+ * Register the cleanup function
+ */
+void
+__atexit_register_cleanup(void (*func)(void))
+{
+        struct atexit *p;
+        size_t pgsize = sysconf(_SC_PAGESIZE);
+
+        if (pgsize < sizeof(*p))
+                return;
+        _ATEXIT_LOCK();
+        p = __atexit;
+        while (p != NULL && p->next != NULL)
+                p = p->next;
+        if (p == NULL) {
+                p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
+                    MAP_ANON | MAP_PRIVATE, -1, 0);
+                if (p == MAP_FAILED)
+                        goto unlock;
+                p->ind = 1;
+                p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
+                    sizeof(p->fns[0]);
+                p->next = NULL;
+                __atexit = p;
+        } else {
+                if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
+                        goto unlock;
+        }
+        p->fns[0].cxa_func = (void (*)(void*))func;
+        p->fns[0].fn_arg = NULL;
+        p->fns[0].fn_dso = NULL;
+        mprotect(p, pgsize, PROT_READ);
+unlock:
+        _ATEXIT_UNLOCK();
+}
+
diff --git a/libc/stdio/findfp.c b/libc/upstream-openbsd/lib/libc/stdio/findfp.c
similarity index 84%
rename from libc/stdio/findfp.c
rename to libc/upstream-openbsd/lib/libc/stdio/findfp.c
index 2dd32c9..b8c7dc1 100644
--- a/libc/stdio/findfp.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/findfp.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: findfp.c,v 1.9 2005/08/08 08:05:36 espie Exp $ */
+/*	$OpenBSD: findfp.c,v 1.15 2013/12/17 16:33:27 deraadt Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -54,7 +54,7 @@
 /*	 p r w flags file _bf z  cookie      close    read    seek    write
 	 ext */
 
-/* the usual - (stdin + stdout + stderr) */
+				/* the usual - (stdin + stdout + stderr) */
 static FILE usual[FOPEN_MAX - 3];
 static struct __sfileext usualext[FOPEN_MAX - 3];
 static struct glue uglue = { 0, FOPEN_MAX - 3, usual };
@@ -62,7 +62,6 @@
 _THREAD_PRIVATE_MUTEX(__sfp_mutex);
 
 static struct __sfileext __sFext[3];
-
 FILE __sF[3] = {
 	std(__SRD, STDIN_FILENO),		/* stdin */
 	std(__SWR, STDOUT_FILENO),		/* stdout */
@@ -144,35 +143,11 @@
 	return (fp);
 }
 
-#if 0
-#define getdtablesize()	sysconf(_SC_OPEN_MAX)
-
 /*
- * XXX.  Force immediate allocation of internal memory.  Not used by stdio,
- * but documented historically for certain applications.  Bad applications.
- */
-void
-f_prealloc(void)
-{
-	struct glue *g;
-	int n;
-
-	n = getdtablesize() - FOPEN_MAX + 20;		/* 20 for slop. */
-	for (g = &__sglue; (n -= g->niobs) > 0 && g->next; g = g->next)
-		/* void */;
-	if (n > 0 && ((g = moreglue(n)) != NULL)) {
-		_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
-		lastglue->next = g;
-		lastglue = g;
-		_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
-	}
-}
-#endif
-
-/*
- * exit() calls _cleanup() through *__cleanup, set whenever we
- * open or buffer a file.  This chicanery is done so that programs
- * that do not use stdio need not link it all in.
+ * exit() and abort() call _cleanup() through the callback registered
+ * with __atexit_register_cleanup(), set whenever we open or buffer a
+ * file. This chicanery is done so that programs that do not use stdio
+ * need not link it all in.
  *
  * The name `_cleanup' is, alas, fairly well known outside stdio.
  */
@@ -199,7 +174,7 @@
 		_FILEEXT_SETUP(usual+i, usualext+i);
 	}
 	/* make sure we clean up on exit */
-	__cleanup = _cleanup; /* conservative */
+	__atexit_register_cleanup(_cleanup); /* conservative */
 	__sdidinit = 1;
 out:
 	_THREAD_PRIVATE_MUTEX_UNLOCK(__sinit_mutex);
diff --git a/libc/upstream-freebsd/lib/libc/stdio/makebuf.c b/libc/upstream-openbsd/lib/libc/stdio/makebuf.c
similarity index 86%
rename from libc/upstream-freebsd/lib/libc/stdio/makebuf.c
rename to libc/upstream-openbsd/lib/libc/stdio/makebuf.c
index a92087e..d47e27c 100644
--- a/libc/upstream-freebsd/lib/libc/stdio/makebuf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/makebuf.c
@@ -1,3 +1,4 @@
+/*	$OpenBSD: makebuf.c,v 1.8 2005/12/28 18:50:22 millert Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -30,21 +31,11 @@
  * SUCH DAMAGE.
  */
 
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)makebuf.c	8.1 (Berkeley) 6/4/93";
-#endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include "namespace.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include "un-namespace.h"
-
-#include "libc_private.h"
 #include "local.h"
 
 /*
@@ -52,7 +43,7 @@
  * Per the ANSI C standard, ALL tty devices default to line buffered.
  *
  * As a side effect, we set __SOPT or __SNPT (en/dis-able fseek
- * optimisation) right after the _fstat() that finds the buffer size.
+ * optimisation) right after the fstat() that finds the buffer size.
  */
 void
 __smakebuf(FILE *fp)
@@ -74,7 +65,7 @@
 		fp->_bf._size = 1;
 		return;
 	}
-	__cleanup = _cleanup;
+	__atexit_register_cleanup(_cleanup);
 	flags |= __SMBF;
 	fp->_bf._base = fp->_p = p;
 	fp->_bf._size = size;
@@ -91,15 +82,15 @@
 {
 	struct stat st;
 
-	if (fp->_file < 0 || _fstat(fp->_file, &st) < 0) {
+	if (fp->_file < 0 || fstat(fp->_file, &st) < 0) {
 		*couldbetty = 0;
 		*bufsize = BUFSIZ;
 		return (__SNPT);
 	}
 
 	/* could be a tty iff it is a character device */
-	*couldbetty = (st.st_mode & S_IFMT) == S_IFCHR;
-	if (st.st_blksize <= 0) {
+	*couldbetty = S_ISCHR(st.st_mode);
+	if (st.st_blksize == 0) {
 		*bufsize = BUFSIZ;
 		return (__SNPT);
 	}
diff --git a/libc/upstream-freebsd/lib/libc/stdio/setvbuf.c b/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c
similarity index 90%
rename from libc/upstream-freebsd/lib/libc/stdio/setvbuf.c
rename to libc/upstream-openbsd/lib/libc/stdio/setvbuf.c
index d396960..6c49f7a 100644
--- a/libc/upstream-freebsd/lib/libc/stdio/setvbuf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c
@@ -1,3 +1,4 @@
+/*	$OpenBSD: setvbuf.c,v 1.11 2009/11/09 00:18:27 kurt Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -30,25 +31,16 @@
  * SUCH DAMAGE.
  */
 
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)setvbuf.c	8.2 (Berkeley) 11/16/93";
-#endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include "namespace.h"
 #include <stdio.h>
 #include <stdlib.h>
-#include "un-namespace.h"
 #include "local.h"
-#include "libc_private.h"
 
 /*
  * Set one of the three kinds of buffering, optionally including
  * a buffer.
  */
 int
-setvbuf(FILE * __restrict fp, char * __restrict buf, int mode, size_t size)
+setvbuf(FILE *fp, char *buf, int mode, size_t size)
 {
 	int ret, flags;
 	size_t iosize;
@@ -63,22 +55,23 @@
 		if ((mode != _IOFBF && mode != _IOLBF) || (int)size < 0)
 			return (EOF);
 
-	FLOCKFILE(fp);
 	/*
 	 * Write current buffer, if any.  Discard unread input (including
 	 * ungetc data), cancel line buffering, and free old buffer if
 	 * malloc()ed.  We also clear any eof condition, as if this were
 	 * a seek.
 	 */
+	FLOCKFILE(fp);
 	ret = 0;
 	(void)__sflush(fp);
 	if (HASUB(fp))
 		FREEUB(fp);
+	WCIO_FREE(fp);
 	fp->_r = fp->_lbfsize = 0;
 	flags = fp->_flags;
 	if (flags & __SMBF)
 		free((void *)fp->_bf._base);
-	flags &= ~(__SLBF | __SNBF | __SMBF | __SOPT | __SOFF | __SNPT | __SEOF);
+	flags &= ~(__SLBF | __SNBF | __SMBF | __SOPT | __SNPT | __SEOF);
 
 	/* If setting unbuffered mode, skip all the hard work. */
 	if (mode == _IONBF)
@@ -154,8 +147,8 @@
 		/* begin/continue reading, or stay in intermediate state */
 		fp->_w = 0;
 	}
-	__cleanup = _cleanup;
-
 	FUNLOCKFILE(fp);
+	__atexit_register_cleanup(_cleanup);
+
 	return (ret);
 }
diff --git a/libc/upstream-netbsd/lib/libc/stdlib/exit.c b/libc/upstream-openbsd/lib/libc/stdlib/exit.c
similarity index 76%
rename from libc/upstream-netbsd/lib/libc/stdlib/exit.c
rename to libc/upstream-openbsd/lib/libc/stdlib/exit.c
index 67e6adf..ef8b335 100644
--- a/libc/upstream-netbsd/lib/libc/stdlib/exit.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/exit.c
@@ -1,8 +1,7 @@
-/*	$NetBSD: exit.c,v 1.15 2011/05/18 19:36:36 dsl Exp $	*/
-
+/*	$OpenBSD: exit.c,v 1.12 2007/09/03 14:40:16 millert Exp $ */
 /*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,23 +28,11 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)exit.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: exit.c,v 1.15 2011/05/18 19:36:36 dsl Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
+#include <sys/types.h>
+#include <sys/mman.h>
 #include <stdlib.h>
 #include <unistd.h>
-#ifdef _LIBC
-#include "reentrant.h"
 #include "atexit.h"
-#endif
-
-void (*__cleanup)(void);
 
 /*
  * Exit, flushing stdio buffers if necessary.
@@ -53,11 +40,10 @@
 void
 exit(int status)
 {
-
-#ifdef _LIBC
+	/*
+	 * Call functions registered by atexit() or _cxa_atexit()
+	 * (including the stdio cleanup routine) and then _exit().
+	 */
 	__cxa_finalize(NULL);
-#endif
-	if (__cleanup)
-		(*__cleanup)();
 	_exit(status);
 }