Ensure we initialize stdin/stdout/stderr's recursive mutexes.

(cherry-pick of 6a03abcfd23f31d1df06eb0059830e22621282bb.)

Bug: 18208568
Change-Id: I9da16ce0f9375bc363d1d02be706d73fd3b1e150
diff --git a/libc/Android.mk b/libc/Android.mk
index 61bdf7e..045556e 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -60,6 +60,7 @@
     bionic/siginterrupt.c \
     bionic/sigsetmask.c \
     bionic/system_properties_compat.c \
+    stdio/findfp.c \
     stdio/snprintf.c\
     stdio/sprintf.c \
 
@@ -389,7 +390,6 @@
     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 \
diff --git a/libc/upstream-openbsd/lib/libc/stdio/findfp.c b/libc/stdio/findfp.c
similarity index 90%
rename from libc/upstream-openbsd/lib/libc/stdio/findfp.c
rename to libc/stdio/findfp.c
index b8c7dc1..0c2ee7c 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/findfp.c
+++ b/libc/stdio/findfp.c
@@ -49,10 +49,8 @@
 #define	NDYNAMIC 10		/* add ten more whenever necessary */
 
 #define	std(flags, file) \
-	{0,0,0,flags,file,{0,0},0,__sF+file,__sclose,__sread,__sseek,__swrite, \
-	 {(unsigned char *)(__sFext+file), 0},NULL,0,{0,0,0},{0},{0,0},0,0}
-/*	 p r w flags file _bf z  cookie      close    read    seek    write
-	 ext */
+	{0,0,0,flags,file,{0},0,__sF+file,__sclose,__sread,__sseek,__swrite, \
+	    {(unsigned char *)(__sFext+file), 0},NULL,0,{0},{0},{0},0,0}
 
 				/* the usual - (stdin + stdout + stderr) */
 static FILE usual[FOPEN_MAX - 3];
@@ -165,17 +163,26 @@
 __sinit(void)
 {
 	_THREAD_PRIVATE_MUTEX(__sinit_mutex);
-	int i;
 
 	_THREAD_PRIVATE_MUTEX_LOCK(__sinit_mutex);
-	if (__sdidinit)
-		goto out;	/* bail out if caller lost the race */
-	for (i = 0; i < FOPEN_MAX - 3; i++) {
+	if (__sdidinit) {
+		/* bail out if caller lost the race */
+		_THREAD_PRIVATE_MUTEX_UNLOCK(__sinit_mutex);
+		return;
+	}
+
+	/* Initialize stdin/stdout/stderr (for the recursive mutex). http://b/18208568. */
+	for (size_t i = 0; i < 3; ++i) {
+		_FILEEXT_SETUP(__sF+i, __sFext+i);
+	}
+	/* Initialize the pre-allocated (but initially unused) streams. */
+	for (size_t i = 0; i < FOPEN_MAX - 3; ++i) {
 		_FILEEXT_SETUP(usual+i, usualext+i);
 	}
+
 	/* make sure we clean up on exit */
 	__atexit_register_cleanup(_cleanup); /* conservative */
 	__sdidinit = 1;
-out:
+
 	_THREAD_PRIVATE_MUTEX_UNLOCK(__sinit_mutex);
 }
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 8c8c235..6a2991f 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -29,6 +29,23 @@
 
 #include "TemporaryFile.h"
 
+TEST(stdio, flockfile_18208568_stderr) {
+  // Check that we have a _recursive_ mutex for flockfile.
+  flockfile(stderr);
+  feof(stderr); // We don't care about the result, but this needs to take the lock.
+  funlockfile(stderr);
+}
+
+TEST(stdio, flockfile_18208568_regular) {
+  // We never had a bug for streams other than stdin/stdout/stderr, but test anyway.
+  FILE* fp = fopen("/dev/null", "w");
+  ASSERT_TRUE(fp != NULL);
+  flockfile(fp);
+  feof(fp);
+  funlockfile(fp);
+  fclose(fp);
+}
+
 TEST(stdio, tmpfile_fileno_fprintf_rewind_fgets) {
   FILE* fp = tmpfile();
   ASSERT_TRUE(fp != NULL);