Use subprocess API in the Centipede adaptor to replace popen, which is unsafe.

PiperOrigin-RevId: 764368879
diff --git a/fuzztest/BUILD b/fuzztest/BUILD
index d7a6eeb..933f539 100644
--- a/fuzztest/BUILD
+++ b/fuzztest/BUILD
@@ -221,6 +221,7 @@
         ":io",
         ":logging",
         ":runtime",
+        ":subprocess",
         ":table_of_recent_compares",
         "@abseil-cpp//absl/algorithm:container",
         "@abseil-cpp//absl/base:no_destructor",
diff --git a/fuzztest/internal/centipede_adaptor.cc b/fuzztest/internal/centipede_adaptor.cc
index 273afa5..7eb0e87 100644
--- a/fuzztest/internal/centipede_adaptor.cc
+++ b/fuzztest/internal/centipede_adaptor.cc
@@ -84,6 +84,7 @@
 #include "./fuzztest/internal/io.h"
 #include "./fuzztest/internal/logging.h"
 #include "./fuzztest/internal/runtime.h"
+#include "./fuzztest/internal/subprocess.h"
 #include "./fuzztest/internal/table_of_recent_compares.h"
 
 namespace fuzztest::internal {
@@ -339,14 +340,26 @@
       absl::StrAppend(&cmdline, ShellEscape(flag));
     }
     absl::FPrintF(GetStderr(), "[.] Running Centipede command %s\n", cmdline);
-    FILE* pipe = popen(cmdline.c_str(), "r");
-    FUZZTEST_INTERNAL_CHECK(pipe != nullptr, "popen failed with errno %d",
-                            errno);
-    char buf[1024];
-    while (std::fgets(buf, sizeof(buf), pipe)) {
-      std::fputs(buf, GetStderr());
+    const std::vector<std::string> shell_cmd = {"/bin/sh", "-c",
+                                                std::move(cmdline)};
+    const TerminationStatus status = RunCommandWithOutputCallbacks(
+        shell_cmd,
+        [](absl::string_view stdout_output) {
+          std::fwrite(stdout_output.data(), 1, stdout_output.size(),
+                      GetStderr());
+        },
+        [](absl::string_view stderr_output) {
+          std::fwrite(stderr_output.data(), 1, stderr_output.size(),
+                      GetStderr());
+        },
+        /*environment=*/std::nullopt);
+    if (status.Signaled()) {
+      // Encoding signaled exit similarly as Bash.
+      return 128 + static_cast<int>(std::get<SignalT>(status.Status()));
     }
-    return pclose(pipe);
+    FUZZTEST_INTERNAL_CHECK(
+        status.Exited(), "Termination status must be Exited if not Signaled");
+    return static_cast<int>(std::get<ExitCodeT>(status.Status()));
   }
   static absl::NoDestructor<DefaultCallbacksFactory<CentipedeDefaultCallbacks>>
       factory;