internal/mobileinit: preserve stdout and stderr file descriptors

To ensure correct command line behavior, the Go runtime crashes
when a SIGPIPE is received on either fd 1 or fd 2 (CL 18151).

At the same time, go mobile redirects stdout and stderr to the
Android logcat facility by replacing os.Stderr and os.Stdout with
the writer ends of two pipes.

This in turn allows the original os.Stderr and os.Stdout files to
be garbage finalized, closing fd 1 and 2.

If an Android app then happens to open a pipe or socket, fd 1 and 2
might be reused. If the pipe or socket ever receives a SIGPIPE, the
Go runtime will think the signal was for stdout or stderr and crash
the program.

This CL preserves fd 1 and fd 2 by using dup3 to redirect the file
descriptors.

Change-Id: I5058d729eca52503a43f0e8c87a9fd296ed3667e
Reviewed-on: https://go-review.googlesource.com/35961
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/internal/mobileinit/mobileinit_android.go b/internal/mobileinit/mobileinit_android.go
index c3186ad..049b18e 100644
--- a/internal/mobileinit/mobileinit_android.go
+++ b/internal/mobileinit/mobileinit_android.go
@@ -27,11 +27,15 @@
 	"bufio"
 	"log"
 	"os"
+	"syscall"
 	"unsafe"
 )
 
 var (
 	ctag = C.CString("GoLog")
+	// Store the writer end of the redirected stderr and stdout
+	// so that they are not garbage collected and closed.
+	stderr, stdout *os.File
 )
 
 type infoWriter struct{}
@@ -70,13 +74,19 @@
 	if err != nil {
 		panic(err)
 	}
-	os.Stderr = w
+	stderr = w
+	if err := syscall.Dup3(int(w.Fd()), int(os.Stderr.Fd()), 0); err != nil {
+		panic(err)
+	}
 	go lineLog(r, C.ANDROID_LOG_ERROR)
 
 	r, w, err = os.Pipe()
 	if err != nil {
 		panic(err)
 	}
-	os.Stdout = w
+	stdout = w
+	if err := syscall.Dup3(int(w.Fd()), int(os.Stdout.Fd()), 0); err != nil {
+		panic(err)
+	}
 	go lineLog(r, C.ANDROID_LOG_INFO)
 }