Merge "storageproxyd: Add watchdog to log stuck requests"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 15b5813..5da1b40 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -98,6 +98,7 @@
         "libbase_headers",
         "libdebuggerd_common_headers",
         "bionic_libc_platform_headers",
+        "gwp_asan_headers",
     ],
 
     whole_static_libs: [
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index a8d7de6..895c111 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -65,6 +65,7 @@
 
 #include "crash_test.h"
 #include "debuggerd/handler.h"
+#include "gtest/gtest.h"
 #include "libdebuggerd/utility.h"
 #include "protocol.h"
 #include "tombstoned/tombstoned.h"
@@ -111,19 +112,6 @@
   ASSERT_MATCH(result,                             \
                R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)");
 
-// Enable GWP-ASan at the start of this process. GWP-ASan is enabled using
-// process sampling, so we need to ensure we force GWP-ASan on.
-__attribute__((constructor)) static void enable_gwp_asan() {
-  android_mallopt_gwp_asan_options_t opts;
-  // No, we're not an app, but let's turn ourselves on without sampling.
-  // Technically, if someone's using the *.default_app sysprops, they'll adjust
-  // our settings, but I don't think this will be common on a device that's
-  // running debuggerd_tests.
-  opts.desire = android_mallopt_gwp_asan_options_t::Action::TURN_ON_FOR_APP;
-  opts.program_name = "";
-  android_mallopt(M_INITIALIZE_GWP_ASAN, &opts, sizeof(android_mallopt_gwp_asan_options_t));
-}
-
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
                                  InterceptStatus* status, DebuggerdDumpType intercept_type) {
   intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
@@ -469,76 +457,6 @@
 }
 #endif
 
-// Number of iterations required to reliably guarantee a GWP-ASan crash.
-// GWP-ASan's sample rate is not truly nondeterministic, it initialises a
-// thread-local counter at 2*SampleRate, and decrements on each malloc(). Once
-// the counter reaches zero, we provide a sampled allocation. Then, double that
-// figure to allow for left/right allocation alignment, as this is done randomly
-// without bias.
-#define GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH (0x20000)
-
-struct GwpAsanTestParameters {
-  size_t alloc_size;
-  bool free_before_access;
-  int access_offset;
-  std::string cause_needle; // Needle to be found in the "Cause: [GWP-ASan]" line.
-};
-
-struct GwpAsanCrasherTest : CrasherTest, testing::WithParamInterface<GwpAsanTestParameters> {};
-
-GwpAsanTestParameters gwp_asan_tests[] = {
-  {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 0, "Use After Free, 0 bytes into a 7-byte allocation"},
-  {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 1, "Use After Free, 1 byte into a 7-byte allocation"},
-  {/* alloc_size */ 7, /* free_before_access */ false, /* access_offset */ 16, "Buffer Overflow, 9 bytes right of a 7-byte allocation"},
-  {/* alloc_size */ 16, /* free_before_access */ false, /* access_offset */ -1, "Buffer Underflow, 1 byte left of a 16-byte allocation"},
-};
-
-INSTANTIATE_TEST_SUITE_P(GwpAsanTests, GwpAsanCrasherTest, testing::ValuesIn(gwp_asan_tests));
-
-TEST_P(GwpAsanCrasherTest, gwp_asan_uaf) {
-  if (mte_supported()) {
-    // Skip this test on MTE hardware, as MTE will reliably catch these errors
-    // instead of GWP-ASan.
-    GTEST_SKIP() << "Skipped on MTE.";
-  }
-  // Skip this test on HWASan, which will reliably catch test errors as well.
-  SKIP_WITH_HWASAN;
-
-  GwpAsanTestParameters params = GetParam();
-  LogcatCollector logcat_collector;
-
-  int intercept_result;
-  unique_fd output_fd;
-  StartProcess([&params]() {
-    for (unsigned i = 0; i < GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH; ++i) {
-      volatile char* p = reinterpret_cast<volatile char*>(malloc(params.alloc_size));
-      if (params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
-      p[params.access_offset] = 42;
-      if (!params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
-    }
-  });
-
-  StartIntercept(&output_fd);
-  FinishCrasher();
-  AssertDeath(SIGSEGV);
-  FinishIntercept(&intercept_result);
-
-  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
-  std::vector<std::string> log_sources(2);
-  ConsumeFd(std::move(output_fd), &log_sources[0]);
-  logcat_collector.Collect(&log_sources[1]);
-
-  for (const auto& result : log_sources) {
-    ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))");
-    ASSERT_MATCH(result, R"(Cause: \[GWP-ASan\]: )" + params.cause_needle);
-    if (params.free_before_access) {
-      ASSERT_MATCH(result, R"(deallocated by thread .*\n.*#00 pc)");
-    }
-    ASSERT_MATCH(result, R"((^|\s)allocated by thread .*\n.*#00 pc)");
-  }
-}
-
 struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {};
 
 INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(0, 16, 131072));
@@ -1279,7 +1197,11 @@
 static const char* const kDebuggerdSeccompPolicy =
     "/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy";
 
-static pid_t seccomp_fork_impl(void (*prejail)()) {
+static void setup_jail(minijail* jail) {
+  if (!jail) {
+    LOG(FATAL) << "failed to create minijail";
+  }
+
   std::string policy;
   if (!android::base::ReadFileToString(kDebuggerdSeccompPolicy, &policy)) {
     PLOG(FATAL) << "failed to read policy file";
@@ -1306,15 +1228,15 @@
     PLOG(FATAL) << "failed to seek tmp_fd";
   }
 
-  ScopedMinijail jail{minijail_new()};
-  if (!jail) {
-    LOG(FATAL) << "failed to create minijail";
-  }
+  minijail_no_new_privs(jail);
+  minijail_log_seccomp_filter_failures(jail);
+  minijail_use_seccomp_filter(jail);
+  minijail_parse_seccomp_filters_from_fd(jail, tmp_fd.release());
+}
 
-  minijail_no_new_privs(jail.get());
-  minijail_log_seccomp_filter_failures(jail.get());
-  minijail_use_seccomp_filter(jail.get());
-  minijail_parse_seccomp_filters_from_fd(jail.get(), tmp_fd.release());
+static pid_t seccomp_fork_impl(void (*prejail)()) {
+  ScopedMinijail jail{minijail_new()};
+  setup_jail(jail.get());
 
   pid_t result = fork();
   if (result == -1) {
@@ -1628,6 +1550,138 @@
   AssertDeath(SIGABRT);
 }
 
+struct GwpAsanTestParameters {
+  size_t alloc_size;
+  bool free_before_access;
+  int access_offset;
+  std::string cause_needle;  // Needle to be found in the "Cause: [GWP-ASan]" line.
+};
+
+struct GwpAsanCrasherTest
+    : CrasherTest,
+      testing::WithParamInterface<
+          std::tuple<GwpAsanTestParameters, /* recoverable */ bool, /* seccomp */ bool>> {};
+
+GwpAsanTestParameters gwp_asan_tests[] = {
+    {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 0,
+     "Use After Free, 0 bytes into a 7-byte allocation"},
+    {/* alloc_size */ 15, /* free_before_access */ true, /* access_offset */ 1,
+     "Use After Free, 1 byte into a 15-byte allocation"},
+    {/* alloc_size */ 4096, /* free_before_access */ false, /* access_offset */ 4098,
+     "Buffer Overflow, 2 bytes right of a 4096-byte allocation"},
+    {/* alloc_size */ 4096, /* free_before_access */ false, /* access_offset */ -1,
+     "Buffer Underflow, 1 byte left of a 4096-byte allocation"},
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    GwpAsanTests, GwpAsanCrasherTest,
+    testing::Combine(testing::ValuesIn(gwp_asan_tests),
+                     /* recoverable */ testing::Bool(),
+                     /* seccomp */ testing::Bool()),
+    [](const testing::TestParamInfo<
+        std::tuple<GwpAsanTestParameters, /* recoverable */ bool, /* seccomp */ bool>>& info) {
+      const GwpAsanTestParameters& params = std::get<0>(info.param);
+      std::string name = params.free_before_access ? "UseAfterFree" : "Overflow";
+      name += testing::PrintToString(params.alloc_size);
+      name += "Alloc";
+      if (params.access_offset < 0) {
+        name += "Left";
+        name += testing::PrintToString(params.access_offset * -1);
+      } else {
+        name += "Right";
+        name += testing::PrintToString(params.access_offset);
+      }
+      name += "Bytes";
+      if (std::get<1>(info.param)) name += "Recoverable";
+      if (std::get<2>(info.param)) name += "Seccomp";
+      return name;
+    });
+
+TEST_P(GwpAsanCrasherTest, run_gwp_asan_test) {
+  if (mte_supported()) {
+    // Skip this test on MTE hardware, as MTE will reliably catch these errors
+    // instead of GWP-ASan.
+    GTEST_SKIP() << "Skipped on MTE.";
+  }
+  // Skip this test on HWASan, which will reliably catch test errors as well.
+  SKIP_WITH_HWASAN;
+
+  GwpAsanTestParameters params = std::get<0>(GetParam());
+  bool recoverable = std::get<1>(GetParam());
+  LogcatCollector logcat_collector;
+
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([&recoverable]() {
+    const char* env[] = {"GWP_ASAN_SAMPLE_RATE=1", "GWP_ASAN_PROCESS_SAMPLING=1",
+                         "GWP_ASAN_MAX_ALLOCS=40000", nullptr, nullptr};
+    if (recoverable) {
+      env[3] = "GWP_ASAN_RECOVERABLE=true";
+    }
+    std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name();
+    test_name = std::regex_replace(test_name, std::regex("run_gwp_asan_test"),
+                                   "DISABLED_run_gwp_asan_test");
+    std::string test_filter = "--gtest_filter=*";
+    test_filter += test_name;
+    std::string this_binary = android::base::GetExecutablePath();
+    const char* args[] = {this_binary.c_str(), "--gtest_also_run_disabled_tests",
+                          test_filter.c_str(), nullptr};
+    // We check the crash report from a debuggerd handler and from logcat. The
+    // echo from stdout/stderr of the subprocess trips up atest, because it
+    // doesn't like that two tests started in a row without the first one
+    // finishing (even though the second one is in a subprocess).
+    close(STDOUT_FILENO);
+    close(STDERR_FILENO);
+    execve(this_binary.c_str(), const_cast<char**>(args), const_cast<char**>(env));
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  if (recoverable) {
+    AssertDeath(0);
+  } else {
+    AssertDeath(SIGSEGV);
+  }
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::vector<std::string> log_sources(2);
+  ConsumeFd(std::move(output_fd), &log_sources[0]);
+  logcat_collector.Collect(&log_sources[1]);
+
+  // seccomp forces the fallback handler, which doesn't print GWP-ASan debugging
+  // information. Make sure the recovery still works, but the report won't be
+  // hugely useful, it looks like a regular SEGV.
+  bool seccomp = std::get<2>(GetParam());
+  if (!seccomp) {
+    for (const auto& result : log_sources) {
+      ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))");
+      ASSERT_MATCH(result, R"(Cause: \[GWP-ASan\]: )" + params.cause_needle);
+      if (params.free_before_access) {
+        ASSERT_MATCH(result, R"(deallocated by thread .*\n.*#00 pc)");
+      }
+      ASSERT_MATCH(result, R"((^|\s)allocated by thread .*\n.*#00 pc)");
+    }
+  }
+}
+
+TEST_P(GwpAsanCrasherTest, DISABLED_run_gwp_asan_test) {
+  GwpAsanTestParameters params = std::get<0>(GetParam());
+  bool seccomp = std::get<2>(GetParam());
+  if (seccomp) {
+    ScopedMinijail jail{minijail_new()};
+    setup_jail(jail.get());
+    minijail_enter(jail.get());
+  }
+
+  // Use 'volatile' to prevent a very clever compiler eliminating the store.
+  char* volatile p = reinterpret_cast<char* volatile>(malloc(params.alloc_size));
+  if (params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
+  p[params.access_offset] = 42;
+  if (!params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
+}
+
 TEST_F(CrasherTest, fdsan_warning_abort_message) {
   int intercept_result;
   unique_fd output_fd;
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 7120d73..d2bf0d7 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -565,17 +565,38 @@
     process_info = g_callbacks.get_process_info();
   }
 
+  // GWP-ASan catches use-after-free and heap-buffer-overflow by using PROT_NONE
+  // guard pages, which lead to SEGV. Normally, debuggerd prints a bug report
+  // and the process terminates, but in some cases, we actually want to print
+  // the bug report and let the signal handler return, and restart the process.
+  // In order to do that, we need to disable GWP-ASan's guard pages. The
+  // following callbacks handle this case.
+  gwp_asan_callbacks_t gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks();
+  bool gwp_asan_recoverable = false;
+  if (signal_number == SIGSEGV && signal_has_si_addr(info) &&
+      gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery &&
+      gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report &&
+      gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report &&
+      gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery(info->si_addr)) {
+    gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report(info->si_addr);
+    gwp_asan_recoverable = true;
+  }
+
   // If sival_int is ~0, it means that the fallback handler has been called
   // once before and this function is being called again to dump the stack
   // of a specific thread. It is possible that the prctl call might return 1,
   // then return 0 in subsequent calls, so check the sival_int to determine if
   // the fallback handler should be called first.
-  if (si_val == kDebuggerdFallbackSivalUintptrRequestDump ||
-      prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+  bool no_new_privs = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1;
+  if (si_val == kDebuggerdFallbackSivalUintptrRequestDump || no_new_privs) {
     // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
     // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
     // ANR trace.
     debuggerd_fallback_handler(info, ucontext, process_info.abort_msg);
+    if (no_new_privs && gwp_asan_recoverable) {
+      gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);
+      return;
+    }
     resend_signal(info);
     return;
   }
@@ -649,6 +670,9 @@
     // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
     // starting to dump right before our death.
     pthread_mutex_unlock(&crash_mutex);
+  } else if (gwp_asan_recoverable) {
+    gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);
+    pthread_mutex_unlock(&crash_mutex);
   }
 #ifdef __aarch64__
   else if (info->si_signo == SIGSEGV &&
@@ -727,3 +751,52 @@
 
   debuggerd_register_handlers(&action);
 }
+
+// When debuggerd's signal handler is the first handler called, it's great at
+// handling the recoverable GWP-ASan mode. For apps, sigchain (from libart) is
+// always the first signal handler, and so the following function is what
+// sigchain must call before processing the signal. This allows for processing
+// of a potentially recoverable GWP-ASan crash. If the signal requires GWP-ASan
+// recovery, then dump a report (via the regular debuggerd hanndler), and patch
+// up the allocator, and allow the process to continue (indicated by returning
+// 'true'). If the crash has nothing to do with GWP-ASan, or recovery isn't
+// possible, return 'false'.
+bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context) {
+  if (signal_number != SIGSEGV || !signal_has_si_addr(info)) return false;
+
+  gwp_asan_callbacks_t gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks();
+  if (gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery == nullptr ||
+      gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report == nullptr ||
+      gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report == nullptr ||
+      !gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery(info->si_addr)) {
+    return false;
+  }
+
+  // Only dump a crash report for the first GWP-ASan crash. ActivityManager
+  // doesn't like it when an app crashes multiple times, and is even more strict
+  // about an app crashing multiple times in a short time period. While the app
+  // won't crash fully when we do GWP-ASan recovery, ActivityManager still gets
+  // the information about the crash through the DropBoxManager service. If an
+  // app has multiple back-to-back GWP-ASan crashes, this would lead to the app
+  // being killed, which defeats the purpose of having the recoverable mode. To
+  // mitigate against this, only generate a debuggerd crash report for the first
+  // GWP-ASan crash encountered. We still need to do the patching up of the
+  // allocator though, so do that.
+  static pthread_mutex_t first_crash_mutex = PTHREAD_MUTEX_INITIALIZER;
+  pthread_mutex_lock(&first_crash_mutex);
+  static bool first_crash = true;
+
+  if (first_crash) {
+    // `debuggerd_signal_handler` will call
+    // `debuggerd_gwp_asan_(pre|post)_crash_report`, so no need to manually call
+    // them here.
+    debuggerd_signal_handler(signal_number, info, context);
+    first_crash = false;
+  } else {
+    gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report(info->si_addr);
+    gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);
+  }
+
+  pthread_mutex_unlock(&first_crash_mutex);
+  return true;
+}
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 1f9f4e2..de88be5 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -46,14 +46,25 @@
   size_t scudo_ring_buffer_size;
 };
 
+// GWP-ASan calbacks to support the recoverable mode. Separate from the
+// debuggerd_callbacks_t because these values aren't available at debuggerd_init
+// time, and have to be synthesized on request.
+typedef struct {
+  bool (*debuggerd_needs_gwp_asan_recovery)(void* fault_addr);
+  void (*debuggerd_gwp_asan_pre_crash_report)(void* fault_addr);
+  void (*debuggerd_gwp_asan_post_crash_report)(void* fault_addr);
+} gwp_asan_callbacks_t;
+
 // These callbacks are called in a signal handler, and thus must be async signal safe.
 // If null, the callbacks will not be called.
 typedef struct {
   debugger_process_info (*get_process_info)();
+  gwp_asan_callbacks_t (*get_gwp_asan_callbacks)();
   void (*post_dump)();
 } debuggerd_callbacks_t;
 
 void debuggerd_init(debuggerd_callbacks_t* callbacks);
+bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context);
 
 // DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both
 // triggered via BIONIC_SIGNAL_DEBUGGER. The debugger_action_t is sent via si_value
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index a801900..9512e7e 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -286,6 +286,7 @@
         "fastboot.cpp",
         "fs.cpp",
         "socket.cpp",
+        "super_flash_helper.cpp",
         "tcp.cpp",
         "udp.cpp",
         "util.cpp",
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7a5a782..8f7cced 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -63,6 +63,7 @@
 #include <build/version.h>
 #include <libavb/libavb.h>
 #include <liblp/liblp.h>
+#include <liblp/super_layout_builder.h>
 #include <platform_tools_version.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
@@ -72,6 +73,7 @@
 #include "diagnose_usb.h"
 #include "fastboot_driver.h"
 #include "fs.h"
+#include "super_flash_helper.h"
 #include "tcp.h"
 #include "transport.h"
 #include "udp.h"
@@ -1405,20 +1407,30 @@
 
   private:
     void CheckRequirements();
-    void DetermineSecondarySlot();
+    void DetermineSlot();
     void CollectImages();
     void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
     void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
     void UpdateSuperPartition();
+    bool OptimizedFlashSuper();
+
+    // If the image uses the default slot, or the user specified "all", then
+    // the paired string will be empty. If the image requests a specific slot
+    // (for example, system_other) it is specified instead.
+    using ImageEntry = std::pair<const Image*, std::string>;
+
+    std::string GetPartitionName(const ImageEntry& entry);
 
     const ImageSource& source_;
     std::string slot_override_;
     bool skip_secondary_;
     bool wipe_;
     bool force_flash_;
+    std::string current_slot_;
     std::string secondary_slot_;
-    std::vector<std::pair<const Image*, std::string>> boot_images_;
-    std::vector<std::pair<const Image*, std::string>> os_images_;
+
+    std::vector<ImageEntry> boot_images_;
+    std::vector<ImageEntry> os_images_;
 };
 
 FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override,
@@ -1441,7 +1453,7 @@
         set_active(slot_override_);
     }
 
-    DetermineSecondarySlot();
+    DetermineSlot();
     CollectImages();
 
     CancelSnapshotIfNeeded();
@@ -1450,24 +1462,92 @@
     // or in bootloader fastboot.
     FlashImages(boot_images_);
 
-    // Sync the super partition. This will reboot to userspace fastboot if needed.
-    UpdateSuperPartition();
+    if (!OptimizedFlashSuper()) {
+        // Sync the super partition. This will reboot to userspace fastboot if needed.
+        UpdateSuperPartition();
 
-    // Resize any logical partition to 0, so each partition is reset to 0
-    // extents, and will achieve more optimal allocation.
-    for (const auto& [image, slot] : os_images_) {
-        auto resize_partition = [](const std::string& partition) -> void {
-            if (is_logical(partition)) {
-                fb->ResizePartition(partition, "0");
-            }
-        };
-        do_for_partitions(image->part_name, slot, resize_partition, false);
+        // Resize any logical partition to 0, so each partition is reset to 0
+        // extents, and will achieve more optimal allocation.
+        for (const auto& [image, slot] : os_images_) {
+            auto resize_partition = [](const std::string& partition) -> void {
+                if (is_logical(partition)) {
+                    fb->ResizePartition(partition, "0");
+                }
+            };
+            do_for_partitions(image->part_name, slot, resize_partition, false);
+        }
     }
 
     // Flash OS images, resizing logical partitions as needed.
     FlashImages(os_images_);
 }
 
+bool FlashAllTool::OptimizedFlashSuper() {
+    if (!supports_AB()) {
+        LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
+        return false;
+    }
+    if (slot_override_ == "all") {
+        LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
+        return false;
+    }
+
+    // Does this device use dynamic partitions at all?
+    unique_fd fd = source_.OpenFile("super_empty.img");
+    if (fd < 0) {
+        LOG(VERBOSE) << "could not open super_empty.img";
+        return false;
+    }
+
+    // Try to find whether there is a super partition.
+    std::string super_name;
+    if (fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
+        super_name = "super";
+    }
+    std::string partition_size_str;
+    if (fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
+        LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
+        return false;
+    }
+
+    SuperFlashHelper helper(source_);
+    if (!helper.Open(fd)) {
+        return false;
+    }
+
+    for (const auto& entry : os_images_) {
+        auto partition = GetPartitionName(entry);
+        auto image = entry.first;
+
+        if (!helper.AddPartition(partition, image->img_name, image->optional_if_no_image)) {
+            return false;
+        }
+    }
+
+    auto s = helper.GetSparseLayout();
+    if (!s) {
+        return false;
+    }
+
+    std::vector<SparsePtr> files;
+    if (int limit = get_sparse_limit(sparse_file_len(s.get(), false, false))) {
+        files = resparse_file(s.get(), limit);
+    } else {
+        files.emplace_back(std::move(s));
+    }
+
+    // Send the data to the device.
+    flash_partition_files(super_name, files);
+
+    // Remove images that we already flashed, just in case we have non-dynamic OS images.
+    auto remove_if_callback = [&, this](const ImageEntry& entry) -> bool {
+        return helper.WillFlash(GetPartitionName(entry));
+    };
+    os_images_.erase(std::remove_if(os_images_.begin(), os_images_.end(), remove_if_callback),
+                     os_images_.end());
+    return true;
+}
+
 void FlashAllTool::CheckRequirements() {
     std::vector<char> contents;
     if (!source_.ReadFile("android-info.txt", &contents)) {
@@ -1476,7 +1556,13 @@
     ::CheckRequirements({contents.data(), contents.size()}, force_flash_);
 }
 
-void FlashAllTool::DetermineSecondarySlot() {
+void FlashAllTool::DetermineSlot() {
+    if (slot_override_.empty()) {
+        current_slot_ = get_current_slot();
+    } else {
+        current_slot_ = slot_override_;
+    }
+
     if (skip_secondary_) {
         return;
     }
@@ -1575,6 +1661,20 @@
     }
 }
 
+std::string FlashAllTool::GetPartitionName(const ImageEntry& entry) {
+    auto slot = entry.second;
+    if (slot.empty()) {
+        slot = current_slot_;
+    }
+    if (slot.empty()) {
+        return entry.first->part_name;
+    }
+    if (slot == "all") {
+        LOG(FATAL) << "Cannot retrieve a singular name when using all slots";
+    }
+    return entry.first->part_name + "_" + slot;
+}
+
 class ZipImageSource final : public ImageSource {
   public:
     explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index bccd668..b422c91 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -32,6 +32,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/endian.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
diff --git a/fastboot/super_flash_helper.cpp b/fastboot/super_flash_helper.cpp
new file mode 100644
index 0000000..b617ce8
--- /dev/null
+++ b/fastboot/super_flash_helper.cpp
@@ -0,0 +1,125 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "super_flash_helper.h"
+
+#include <android-base/logging.h>
+
+#include "util.h"
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using android::fs_mgr::SuperImageExtent;
+
+SuperFlashHelper::SuperFlashHelper(const ImageSource& source) : source_(source) {}
+
+bool SuperFlashHelper::Open(borrowed_fd fd) {
+    if (!builder_.Open(fd)) {
+        LOG(VERBOSE) << "device does not support optimized super flashing";
+        return false;
+    }
+
+    base_metadata_ = builder_.Export();
+    return !!base_metadata_;
+}
+
+bool SuperFlashHelper::IncludeInSuper(const std::string& partition) {
+    return should_flash_in_userspace(*base_metadata_.get(), partition);
+}
+
+bool SuperFlashHelper::AddPartition(const std::string& partition, const std::string& image_name,
+                                    bool optional) {
+    if (!IncludeInSuper(partition)) {
+        return true;
+    }
+    auto iter = image_fds_.find(image_name);
+    if (iter == image_fds_.end()) {
+        unique_fd fd = source_.OpenFile(image_name);
+        if (fd < 0) {
+            if (!optional) {
+                LOG(VERBOSE) << "could not find partition image: " << image_name;
+                return false;
+            }
+            return true;
+        }
+        if (is_sparse_file(fd)) {
+            LOG(VERBOSE) << "cannot optimize dynamic partitions with sparse images";
+            return false;
+        }
+        iter = image_fds_.emplace(image_name, std::move(fd)).first;
+    }
+
+    if (!builder_.AddPartition(partition, image_name, get_file_size(iter->second))) {
+        return false;
+    }
+
+    will_flash_.emplace(partition);
+    return true;
+}
+
+SparsePtr SuperFlashHelper::GetSparseLayout() {
+    // Cache extents since the sparse ptr depends on data pointers.
+    if (extents_.empty()) {
+        extents_ = builder_.GetImageLayout();
+        if (extents_.empty()) {
+            LOG(VERBOSE) << "device does not support optimized super flashing";
+            return {nullptr, nullptr};
+        }
+    }
+
+    unsigned int block_size = base_metadata_->geometry.logical_block_size;
+    int64_t flashed_size = extents_.back().offset + extents_.back().size;
+    SparsePtr s(sparse_file_new(block_size, flashed_size), sparse_file_destroy);
+
+    for (const auto& extent : extents_) {
+        if (extent.offset / block_size > UINT_MAX) {
+            // Super image is too big to send via sparse files (>8TB).
+            LOG(VERBOSE) << "super image is too big to flash";
+            return {nullptr, nullptr};
+        }
+        unsigned int block = extent.offset / block_size;
+
+        int rv = 0;
+        switch (extent.type) {
+            case SuperImageExtent::Type::DONTCARE:
+                break;
+            case SuperImageExtent::Type::ZERO:
+                rv = sparse_file_add_fill(s.get(), 0, extent.size, block);
+                break;
+            case SuperImageExtent::Type::DATA:
+                rv = sparse_file_add_data(s.get(), extent.blob->data(), extent.size, block);
+                break;
+            case SuperImageExtent::Type::PARTITION: {
+                auto iter = image_fds_.find(extent.image_name);
+                if (iter == image_fds_.end()) {
+                    LOG(FATAL) << "image added but not found: " << extent.image_name;
+                    return {nullptr, nullptr};
+                }
+                rv = sparse_file_add_fd(s.get(), iter->second.get(), extent.image_offset,
+                                        extent.size, block);
+                break;
+            }
+            default:
+                LOG(VERBOSE) << "unrecognized extent type in super image layout";
+                return {nullptr, nullptr};
+        }
+        if (rv) {
+            LOG(VERBOSE) << "sparse failure building super image layout";
+            return {nullptr, nullptr};
+        }
+    }
+    return s;
+}
diff --git a/fastboot/super_flash_helper.h b/fastboot/super_flash_helper.h
new file mode 100644
index 0000000..29c15d0
--- /dev/null
+++ b/fastboot/super_flash_helper.h
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <android-base/unique_fd.h>
+#include <liblp/liblp.h>
+#include <liblp/super_layout_builder.h>
+
+#include "util.h"
+
+class SuperFlashHelper final {
+  public:
+    explicit SuperFlashHelper(const ImageSource& source);
+
+    bool Open(android::base::borrowed_fd fd);
+    bool IncludeInSuper(const std::string& partition);
+    bool AddPartition(const std::string& partition, const std::string& image_name, bool optional);
+
+    // Note: the SparsePtr if non-null should not outlive SuperFlashHelper, since
+    // it depends on open fds and data pointers.
+    SparsePtr GetSparseLayout();
+
+    bool WillFlash(const std::string& partition) const {
+        return will_flash_.find(partition) != will_flash_.end();
+    }
+
+  private:
+    const ImageSource& source_;
+    android::fs_mgr::SuperLayoutBuilder builder_;
+    std::unique_ptr<android::fs_mgr::LpMetadata> base_metadata_;
+    std::vector<android::fs_mgr::SuperImageExtent> extents_;
+
+    // Cache open image fds. This keeps them alive while we flash the sparse
+    // file.
+    std::unordered_map<std::string, android::base::unique_fd> image_fds_;
+    std::unordered_set<std::string> will_flash_;
+};
diff --git a/init/OWNERS b/init/OWNERS
index 4604d06..68b9396 100644
--- a/init/OWNERS
+++ b/init/OWNERS
@@ -1,2 +1,3 @@
+# Bug component: 1312227
 dvander@google.com
 jiyong@google.com
diff --git a/init/selinux.cpp b/init/selinux.cpp
index ea308aa..4cc00fe 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -629,7 +629,7 @@
 }
 
 Result<void> SepolicyFsVerityCheck() {
-    return Error() << "TODO implementent support for fsverity SEPolicy.";
+    return Error() << "TODO implement support for fsverity SEPolicy.";
 }
 
 Result<void> SepolicyCheckSignature(const std::string& dir) {