From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Tue, 28 May 2024 10:44:06 +0200
Subject: feat: enable passing exit code on service process crash

This patch enables plumbing the exit code of the service process to the
browser process when the service process crashes. The process can perform cleanup
after the message pipe disconnection, which previously led to racy and incorrect
exit codes in some crashing scenarios. To mitigate this, we can rely on
ServiceProcessHost::Observer functions, but we need to pass the exit code to
the observer.

diff --git a/content/browser/service_process_host_impl.cc b/content/browser/service_process_host_impl.cc
index f6082bada22c5f4e70af60ea6f555b0f363919c5..f691676a629bf82f81117599ae0bd0a4870c9f61 100644
--- a/content/browser/service_process_host_impl.cc
+++ b/content/browser/service_process_host_impl.cc
@@ -73,12 +73,15 @@ class ServiceProcessTracker {
     processes_.erase(iter);
   }
 
-  void NotifyCrashed(ServiceProcessId id) {
+  void NotifyCrashed(ServiceProcessId id, int exit_code) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     auto iter = processes_.find(id);
     CHECK(iter != processes_.end(), base::NotFatalUntil::M130);
-    for (auto& observer : observers_)
-      observer.OnServiceProcessCrashed(iter->second.Duplicate());
+    for (auto& observer : observers_) {
+      auto params = iter->second.Duplicate();
+      params.set_exit_code(exit_code);
+      observer.OnServiceProcessCrashed(params);
+    }
     processes_.erase(iter);
   }
 
@@ -154,7 +157,7 @@ class UtilityProcessClient : public UtilityProcessHost::Client {
         process_info_->service_process_id());
   }
 
-  void OnProcessCrashed() override {
+  void OnProcessCrashed(int exit_code) override {
     // TODO(crbug.com/40654042): It is unclear how we can observe
     // |OnProcessCrashed()| without observing |OnProcessLaunched()| first, but
     // it can happen on Android. Ignore the notification in this case.
@@ -162,7 +165,7 @@ class UtilityProcessClient : public UtilityProcessHost::Client {
       return;
 
     GetServiceProcessTracker().NotifyCrashed(
-        process_info_->service_process_id());
+        process_info_->service_process_id(), exit_code);
   }
 
  private:
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 918d1f92476277cd8f463a7aa1ceda008815c1d7..cdebe04babbd9682f97923149261bbe23c9d9701 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -518,7 +518,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) {
   // Take ownership of |client_| so the destructor doesn't notify it of
   // termination.
   auto client = std::move(client_);
-  client->OnProcessCrashed();
+  client->OnProcessCrashed(exit_code);
 }
 
 std::optional<std::string> UtilityProcessHost::GetServiceName() {
diff --git a/content/browser/utility_process_host.h b/content/browser/utility_process_host.h
index c1809298c830b814f886859c2626d6bce7b9ac8c..8d202e7e47c08c33394eb49e87b48225f72af445 100644
--- a/content/browser/utility_process_host.h
+++ b/content/browser/utility_process_host.h
@@ -78,7 +78,7 @@ class CONTENT_EXPORT UtilityProcessHost
 
     virtual void OnProcessLaunched(const base::Process& process) {}
     virtual void OnProcessTerminatedNormally() {}
-    virtual void OnProcessCrashed() {}
+    virtual void OnProcessCrashed(int exit_code) {}
   };
 
   // This class is self-owned. It must be instantiated using new, and shouldn't
diff --git a/content/public/browser/service_process_info.h b/content/public/browser/service_process_info.h
index 1a8656aef341cd3b23af588fb00569b79d6cd100..6af523eb27a8c1e5529721c029e5b3ba0708b9fc 100644
--- a/content/public/browser/service_process_info.h
+++ b/content/public/browser/service_process_info.h
@@ -64,7 +64,13 @@ class CONTENT_EXPORT ServiceProcessInfo {
   const std::optional<GURL>& site() const { return site_; }
   const base::Process& GetProcess() const { return process_; }
 
+  void set_exit_code(int exit_code) { exit_code_ = exit_code; }
+  int exit_code() const { return exit_code_; }
+
  private:
+  // The exit code of the process, if it has exited.
+  int exit_code_ = 0;
+
   // The name of the service interface for which the process was launched.
   std::string service_interface_name_;
 
