Add tests for SSL_set_fd and friends.

Their implementations expose a lot of really weird SSL_set_bio behavior. Note
that one test must be disabled as it doesn't even work. The subsequent commit
will re-enable it.

Change-Id: I4b7acadd710b3be056951886fc3e073a5aa816de
Reviewed-on: https://boringssl-review.googlesource.com/8272
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index ef38902..619b4b2 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -1251,6 +1251,109 @@
   return true;
 }
 
+static bool ExpectFDs(const SSL *ssl, int rfd, int wfd) {
+  if (SSL_get_rfd(ssl) != rfd || SSL_get_wfd(ssl) != wfd) {
+    fprintf(stderr, "Got fds %d and %d, wanted %d and %d.\n", SSL_get_rfd(ssl),
+            SSL_get_wfd(ssl), rfd, wfd);
+    return false;
+  }
+
+  // The wrapper BIOs are always equal when fds are equal, even if set
+  // individually.
+  if (rfd == wfd && SSL_get_rbio(ssl) != SSL_get_wbio(ssl)) {
+    fprintf(stderr, "rbio and wbio did not match.\n");
+    return false;
+  }
+
+  return true;
+}
+
+static bool TestSetFD() {
+  ScopedSSL_CTX ctx(SSL_CTX_new(TLS_method()));
+  if (!ctx) {
+    return false;
+  }
+
+  // Test setting different read and write FDs.
+  ScopedSSL ssl(SSL_new(ctx.get()));
+  if (!ssl ||
+      !SSL_set_rfd(ssl.get(), 1) ||
+      !SSL_set_wfd(ssl.get(), 2) ||
+      !ExpectFDs(ssl.get(), 1, 2)) {
+    return false;
+  }
+
+  // Test setting the same FD.
+  ssl.reset(SSL_new(ctx.get()));
+  if (!ssl ||
+      !SSL_set_fd(ssl.get(), 1) ||
+      !ExpectFDs(ssl.get(), 1, 1)) {
+    return false;
+  }
+
+  // Test setting the same FD one side at a time.
+  ssl.reset(SSL_new(ctx.get()));
+  if (!ssl ||
+      !SSL_set_rfd(ssl.get(), 1) ||
+      !SSL_set_wfd(ssl.get(), 1) ||
+      !ExpectFDs(ssl.get(), 1, 1)) {
+    return false;
+  }
+
+  // Test setting the same FD in the other order.
+  ssl.reset(SSL_new(ctx.get()));
+  if (!ssl ||
+      !SSL_set_wfd(ssl.get(), 1) ||
+      !SSL_set_rfd(ssl.get(), 1) ||
+      !ExpectFDs(ssl.get(), 1, 1)) {
+    return false;
+  }
+
+  // TODO(davidben): This one is broken.
+#if 0
+  // Test changing the read FD partway through.
+  ssl.reset(SSL_new(ctx.get()));
+  if (!ssl ||
+      !SSL_set_fd(ssl.get(), 1) ||
+      !SSL_set_rfd(ssl.get(), 2) ||
+      !ExpectFDs(ssl.get(), 2, 1)) {
+    return false;
+  }
+#endif
+
+  // Test changing the write FD partway through.
+  ssl.reset(SSL_new(ctx.get()));
+  if (!ssl ||
+      !SSL_set_fd(ssl.get(), 1) ||
+      !SSL_set_wfd(ssl.get(), 2) ||
+      !ExpectFDs(ssl.get(), 1, 2)) {
+    return false;
+  }
+
+  // Test a no-op change to the read FD partway through.
+  ssl.reset(SSL_new(ctx.get()));
+  if (!ssl ||
+      !SSL_set_fd(ssl.get(), 1) ||
+      !SSL_set_rfd(ssl.get(), 1) ||
+      !ExpectFDs(ssl.get(), 1, 1)) {
+    return false;
+  }
+
+  // Test a no-op change to the write FD partway through.
+  ssl.reset(SSL_new(ctx.get()));
+  if (!ssl ||
+      !SSL_set_fd(ssl.get(), 1) ||
+      !SSL_set_wfd(ssl.get(), 1) ||
+      !ExpectFDs(ssl.get(), 1, 1)) {
+    return false;
+  }
+
+  // ASan builds will implicitly test that the internal |BIO| reference-counting
+  // is correct.
+
+  return true;
+}
+
 int main() {
   CRYPTO_library_init();
 
@@ -1275,7 +1378,8 @@
       !TestInternalSessionCache() ||
       !TestSequenceNumber(false /* TLS */) ||
       !TestSequenceNumber(true /* DTLS */) ||
-      !TestOneSidedShutdown()) {
+      !TestOneSidedShutdown() ||
+      !TestSetFD()) {
     ERR_print_errors_fp(stderr);
     return 1;
   }