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_host/service_process_tracker.cc b/content/browser/service_host/service_process_tracker.cc
index fb41c8dfd147a90d7d581b49ad7f909d947cb214..ae0dce9d1dde14f1562adac7d324635983bb4c09 100644
--- a/content/browser/service_host/service_process_tracker.cc
+++ b/content/browser/service_host/service_process_tracker.cc
@@ -51,7 +51,8 @@ void ServiceProcessTracker::NotifyTerminated(ServiceProcessId id) {
 
 void ServiceProcessTracker::NotifyCrashed(
     ServiceProcessId id,
-    UtilityProcessHost::Client::CrashType crash_type) {
+    UtilityProcessHost::Client::CrashType crash_type,
+    int exit_code) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto iter = processes_.find(id);
   CHECK(iter != processes_.end());
@@ -65,7 +66,9 @@ void ServiceProcessTracker::NotifyCrashed(
       break;
   }
   for (auto& observer : observers_) {
-    observer.OnServiceProcessCrashed(iter->second.Duplicate());
+    auto params = iter->second.Duplicate();
+    params.set_exit_code(exit_code);
+    observer.OnServiceProcessCrashed(params);
   }
   processes_.erase(iter);
 }
diff --git a/content/browser/service_host/service_process_tracker.h b/content/browser/service_host/service_process_tracker.h
index 9a5179c4eeacf8bbfb2d831b4301836df490f3a8..511fc9b9d5ce14a1b5ef457a1047e40d3bb64273 100644
--- a/content/browser/service_host/service_process_tracker.h
+++ b/content/browser/service_host/service_process_tracker.h
@@ -36,7 +36,8 @@ class ServiceProcessTracker {
   void NotifyTerminated(ServiceProcessId id);
 
   void NotifyCrashed(ServiceProcessId id,
-                     UtilityProcessHost::Client::CrashType type);
+                     UtilityProcessHost::Client::CrashType type,
+                     int exit_code);
 
   void AddObserver(ServiceProcessHost::Observer* observer);
 
diff --git a/content/browser/service_host/utility_process_client.cc b/content/browser/service_host/utility_process_client.cc
index 99da7eee3cb1a09b984832211bceee385147aec2..5e4bd537d94ad26c8d309ebfb63845a8d3d11dae 100644
--- a/content/browser/service_host/utility_process_client.cc
+++ b/content/browser/service_host/utility_process_client.cc
@@ -40,7 +40,7 @@ void UtilityProcessClient::OnProcessTerminatedNormally() {
       process_info_->service_process_id());
 }
 
-void UtilityProcessClient::OnProcessCrashed(CrashType type) {
+void UtilityProcessClient::OnProcessCrashed(CrashType type, int exit_code) {
   // 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.
@@ -49,6 +49,6 @@ void UtilityProcessClient::OnProcessCrashed(CrashType type) {
   }
 
   GetServiceProcessTracker().NotifyCrashed(process_info_->service_process_id(),
-                                           type);
+                                           type, exit_code);
 }
 }  // namespace content
diff --git a/content/browser/service_host/utility_process_client.h b/content/browser/service_host/utility_process_client.h
index 2648adb1cf38ab557b66ffd0e3034b26b04d76d6..98eab587f343f6ca472efc3d4e7b31b2b8821417 100644
--- a/content/browser/service_host/utility_process_client.h
+++ b/content/browser/service_host/utility_process_client.h
@@ -36,7 +36,7 @@ class UtilityProcessClient : public UtilityProcessHost::Client {
 
   void OnProcessTerminatedNormally() override;
 
-  void OnProcessCrashed(CrashType type) override;
+  void OnProcessCrashed(CrashType type, int exit_code) override;
 
  private:
   const std::string service_interface_name_;
diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc
index 136fa55e7945f1d6abf5731c9cbaa92148f8117d..ba6dbc588c809c482770d2f78d1cd97e91af3362 100644
--- a/content/browser/service_host/utility_process_host.cc
+++ b/content/browser/service_host/utility_process_host.cc
@@ -628,7 +628,7 @@ void UtilityProcessHost::OnProcessCrashed(int exit_code) {
                : Client::CrashType::kPreIpcInitialization;
   }
 #endif  // BUILDFLAG(IS_WIN)
-  client->OnProcessCrashed(type);
+  client->OnProcessCrashed(type, exit_code);
 }
 
 std::optional<std::string> UtilityProcessHost::GetServiceName() {
diff --git a/content/browser/service_host/utility_process_host.h b/content/browser/service_host/utility_process_host.h
index f39af3df87786a472f987309ac0dea699b9f0d9f..6f470d19fa5fa0bd4d2bdb1be4aa0cb3a70d3936 100644
--- a/content/browser/service_host/utility_process_host.h
+++ b/content/browser/service_host/utility_process_host.h
@@ -88,7 +88,7 @@ class CONTENT_EXPORT UtilityProcessHost final
     virtual void OnProcessTerminatedNormally() {}
     // Called when the process has terminated due to a crash. The `type` field
     // indicates the type of crash. See above.
-    virtual void OnProcessCrashed(CrashType type) {}
+    virtual void OnProcessCrashed(CrashType type, int exit_code) {}
   };
 
   struct CONTENT_EXPORT Options {
diff --git a/content/public/browser/service_process_info.h b/content/public/browser/service_process_info.h
index 21f2c71d2323a70491034678a4dc0029d8d53e63..ce76dc8cadd5cbee5114d7106389803c3d7e8238 100644
--- a/content/public/browser/service_process_info.h
+++ b/content/public/browser/service_process_info.h
@@ -68,7 +68,13 @@ class CONTENT_EXPORT ServiceProcessInfo {
     crashed_pre_ipc_ = crashed_pre_ipc;
   }
 
+  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_;
 
