Fix posix_spawn signal defaulting.

Add a new stress test, and fix the code to pass it. We need to ensure that
we reset signal handlers for caught signals before unblocking signals in
the child, we need to ensure that this happens even if you haven't passed
a pthread_spawn_attr_t, and we need to ensure that this happens if you
pass in an empty sigdefault set.

Bug: http://b/68707996
Test: ran tests
Change-Id: I348e9b17b1bdf221591da42c0ada133d98471d66
diff --git a/tests/spawn_test.cpp b/tests/spawn_test.cpp
index 6a3920e..d2e4ea1 100644
--- a/tests/spawn_test.cpp
+++ b/tests/spawn_test.cpp
@@ -20,6 +20,7 @@
 #include <fcntl.h>
 #include <gtest/gtest.h>
 
+#include "ScopedSignalHandler.h"
 #include "utils.h"
 
 #include <android-base/file.h>
@@ -386,3 +387,41 @@
 
   ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
 }
+
+TEST(spawn, signal_stress) {
+  // Ensure that posix_spawn doesn't restore the caller's signal mask in the
+  // child without first defaulting any caught signals (http://b/68707996).
+  static pid_t parent = getpid();
+
+  pid_t pid = fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    for (size_t i = 0; i < 1024; ++i) {
+      kill(0, SIGWINCH);
+      usleep(10);
+    }
+    return;
+  }
+
+  // We test both with and without attributes, because they used to be
+  // different codepaths. We also test with an empty `sigdefault` set.
+  posix_spawnattr_t attr1;
+  posix_spawnattr_init(&attr1);
+
+  sigset_t empty_mask = {};
+  posix_spawnattr_t attr2;
+  posix_spawnattr_init(&attr2);
+  posix_spawnattr_setflags(&attr2, POSIX_SPAWN_SETSIGDEF);
+  posix_spawnattr_setsigdefault(&attr2, &empty_mask);
+
+  posix_spawnattr_t* attrs[] = { nullptr, &attr1, &attr2 };
+
+  ScopedSignalHandler ssh(SIGWINCH, [](int) { ASSERT_EQ(getpid(), parent); });
+
+  ExecTestHelper eth;
+  eth.SetArgs({"true", nullptr});
+  for (size_t i = 0; i < 128; ++i) {
+    posix_spawn(nullptr, "true", nullptr, attrs[i % 3], eth.GetArgs(), nullptr);
+  }
+}