Revert handshaker fd numbers and make StartProcess more flexible.

b571e77773 changed these fd numbers, but that interacts poorly with
cross-version tests. Instead, remove the assumptions StartProcess() was
making about the relationship between the two sets of fds.

Change-Id: If8fe62e4d20d22776e79e05e82cb5920cbb545ec
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47044
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/handshake_util.cc b/ssl/test/handshake_util.cc
index 0fdf47f..34cee47 100644
--- a/ssl/test/handshake_util.cc
+++ b/ssl/test/handshake_util.cc
@@ -27,6 +27,7 @@
 #endif
 
 #include <functional>
+#include <map>
 #include <vector>
 
 #include "async_bio.h"
@@ -379,9 +380,16 @@
   posix_spawn_file_actions_t *actions_;
 };
 
+// StartHandshaker starts the handshaker process and, on success, returns a
+// handle to the process in |*out|. It sets |*out_control| to a control pipe to
+// the process. |map_fds| maps from desired fd number in the child process to
+// the source fd in the calling process. |close_fds| is the list of additional
+// fds to close, which may overlap with |map_fds|. Other than stdin, stdout, and
+// stderr, the status of fds not listed in either set is undefined.
 static bool StartHandshaker(ScopedProcess *out, ScopedFD *out_control,
                             const TestConfig *config, bool is_resume,
-                            posix_spawn_file_actions_t *actions) {
+                            std::map<int, int> map_fds,
+                            std::vector<int> close_fds) {
   if (config->handshaker_path.empty()) {
     fprintf(stderr, "no -handshaker-path specified\n");
     return false;
@@ -411,15 +419,55 @@
     return false;
   }
   ScopedFD scoped_control0(control[0]), scoped_control1(control[1]);
+  close_fds.push_back(control[0]);
+  map_fds[kFdControl] = control[1];
 
-  assert(control[1] != kFdHandshakerToProxy);
-  assert(control[1] != kFdProxyToHandshaker);
-  if (posix_spawn_file_actions_addclose(actions, control[0]) != 0) {
+  posix_spawn_file_actions_t actions;
+  if (posix_spawn_file_actions_init(&actions) != 0) {
     return false;
   }
-  if (control[1] != kFdControl &&
-      posix_spawn_file_actions_adddup2(actions, control[1], kFdControl) != 0) {
-    return false;
+  FileActionsDestroyer actions_destroyer(&actions);
+  for (int fd : close_fds) {
+    if (posix_spawn_file_actions_addclose(&actions, fd) != 0) {
+      return false;
+    }
+  }
+  if (!map_fds.empty()) {
+    int max_fd = STDERR_FILENO;
+    for (const auto &pair : map_fds) {
+      max_fd = std::max(max_fd, pair.first);
+      max_fd = std::max(max_fd, pair.second);
+    }
+    // |map_fds| may contain cycles, so make a copy of all the source fds.
+    // |posix_spawn| can only use |dup2|, not |dup|, so we assume |max_fd| is
+    // the last fd we care about inheriting. |temp_fds| maps from fd number in
+    // the parent process to a temporary fd number in the child process.
+    std::map<int, int> temp_fds;
+    int next_fd = max_fd + 1;
+    for (const auto &pair : map_fds) {
+      if (temp_fds.count(pair.second)) {
+        continue;
+      }
+      temp_fds[pair.second] = next_fd;
+      if (posix_spawn_file_actions_adddup2(&actions, pair.second, next_fd) !=
+          0 ||
+          posix_spawn_file_actions_addclose(&actions, pair.second) != 0) {
+        return false;
+      }
+      next_fd++;
+    }
+    for (const auto &pair : map_fds) {
+      if (posix_spawn_file_actions_adddup2(&actions, temp_fds[pair.second],
+                                           pair.first) != 0) {
+        return false;
+      }
+    }
+    // Clean up temporary fds.
+    for (int fd = max_fd + 1; fd < next_fd; fd++) {
+      if (posix_spawn_file_actions_addclose(&actions, fd) != 0) {
+        return false;
+      }
+    }
   }
 
   fflush(stdout);
@@ -428,7 +476,7 @@
   // MSan doesn't know that |posix_spawn| initializes its output, so initialize
   // it to -1.
   pid_t pid = -1;
-  if (posix_spawn(&pid, args[0], actions, nullptr,
+  if (posix_spawn(&pid, args[0], &actions, nullptr,
                   const_cast<char *const *>(args.data()), environ) != 0) {
     return false;
   }
@@ -443,12 +491,6 @@
 static bool RunHandshaker(BIO *bio, const TestConfig *config, bool is_resume,
                           Span<const uint8_t> input,
                           std::vector<uint8_t> *out) {
-  posix_spawn_file_actions_t actions;
-  if (posix_spawn_file_actions_init(&actions) != 0) {
-    return false;
-  }
-  FileActionsDestroyer actions_destroyer(&actions);
-
   int rfd[2], wfd[2];
   // We use pipes, rather than some other mechanism, for their buffers.  During
   // the handshake, this process acts as a dumb proxy until receiving the
@@ -472,29 +514,12 @@
   }
   ScopedFD wfd0_closer(wfd[0]), wfd1_closer(wfd[1]);
 
-  assert(kFdControl != rfd[0]);
-  assert(kFdControl != wfd[1]);
-  assert(kFdHandshakerToProxy != rfd[0]);
-  assert(kFdProxyToHandshaker != wfd[1]);
-
-  if (posix_spawn_file_actions_addclose(&actions, rfd[1]) != 0 ||
-      posix_spawn_file_actions_addclose(&actions, wfd[0]) != 0) {
-    return false;
-  }
-  if (rfd[0] != kFdProxyToHandshaker &&
-      posix_spawn_file_actions_adddup2(&actions, rfd[0],
-                                       kFdProxyToHandshaker) != 0) {
-    return false;
-  }
-  if (wfd[1] != kFdHandshakerToProxy &&
-      posix_spawn_file_actions_adddup2(&actions, wfd[1],
-                                       kFdHandshakerToProxy) != 0) {
-    return false;
-  }
-
   ScopedProcess handshaker;
   ScopedFD control;
-  if (!StartHandshaker(&handshaker, &control, config, is_resume, &actions)) {
+  if (!StartHandshaker(
+          &handshaker, &control, config, is_resume,
+          {{kFdProxyToHandshaker, rfd[0]}, {kFdHandshakerToProxy, wfd[1]}},
+          {rfd[1], wfd[0]})) {
     return false;
   }
 
@@ -534,14 +559,9 @@
 static bool RequestHandshakeHint(const TestConfig *config, bool is_resume,
                                  Span<const uint8_t> input, bool *out_has_hints,
                                  std::vector<uint8_t> *out_hints) {
-  posix_spawn_file_actions_t actions;
-  if (posix_spawn_file_actions_init(&actions) != 0) {
-    return false;
-  }
-  FileActionsDestroyer actions_destroyer(&actions);
   ScopedProcess handshaker;
   ScopedFD control;
-  if (!StartHandshaker(&handshaker, &control, config, is_resume, &actions)) {
+  if (!StartHandshaker(&handshaker, &control, config, is_resume, {}, {})) {
     return false;
   }
 
diff --git a/ssl/test/handshake_util.h b/ssl/test/handshake_util.h
index 7fee20b..dda9206 100644
--- a/ssl/test/handshake_util.h
+++ b/ssl/test/handshake_util.h
@@ -59,9 +59,9 @@
 constexpr char kControlMsgError = 'E';           // Handshaker hit an error
 
 // The protocol between the proxy and handshaker uses these file descriptors.
-constexpr int kFdControl = 20;                   // Bi-directional dgram socket.
-constexpr int kFdProxyToHandshaker = 21;         // Uni-directional pipe.
-constexpr int kFdHandshakerToProxy = 22;         // Uni-directional pipe.
+constexpr int kFdControl = 3;            // Bi-directional dgram socket.
+constexpr int kFdProxyToHandshaker = 4;  // Uni-directional pipe.
+constexpr int kFdHandshakerToProxy = 5;  // Uni-directional pipe.
 #endif  // HANDSHAKER_SUPPORTED
 
 #endif  // HEADER_TEST_HANDSHAKE