Add regression test for CVE-2021-39689 (b/206090748).

This is to ensure that a later change won't introduce a security
regression.

Bug: 280776418
Test: atest odsign_e2e_tests_full
Change-Id: Ib1c9320e272cf051f285393332e47b8c1843ff54
diff --git a/odrefresh/odrefresh_main.cc b/odrefresh/odrefresh_main.cc
index 2871a99..c0e5ade 100644
--- a/odrefresh/odrefresh_main.cc
+++ b/odrefresh/odrefresh_main.cc
@@ -20,6 +20,7 @@
 #include <string_view>
 #include <unordered_map>
 
+#include "android-base/parsebool.h"
 #include "android-base/properties.h"
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
@@ -37,6 +38,8 @@
 namespace {
 
 using ::android::base::GetProperty;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
 using ::android::base::StartsWith;
 using ::art::odrefresh::CompilationOptions;
 using ::art::odrefresh::ExitCode;
@@ -155,8 +158,8 @@
       config->SetStagingDir(value);
     } else if (ArgumentEquals(arg, "--dry-run")) {
       config->SetDryRun();
-    } else if (ArgumentEquals(arg, "--partial-compilation")) {
-      config->SetPartialCompilation(true);
+    } else if (ArgumentMatches(arg, "--partial-compilation=", &value)) {
+      config->SetPartialCompilation(ParseBool(value) == ParseBoolResult::kTrue);
     } else if (ArgumentEquals(arg, "--no-refresh")) {
       config->SetRefresh(false);
     } else if (ArgumentEquals(arg, "--minimal")) {
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
index c5af9d8..88bc465 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
@@ -413,6 +413,45 @@
         mTestUtils.assertModifiedAfter(Set.of(OdsignTestUtils.CACHE_INFO_FILE), timeMs);
     }
 
+    /**
+     * Regression test of CVE-2021-39689 (b/206090748): if the device doesn't have the odsign
+     * security fix, there's a risk that the existing artifacts may be manipulated, and odsign will
+     * mistakenly sign them. Therefore, odrefresh should clear all artifacts and regenerate them.
+     * I.e., no matter the compilation succeeds or not, no existing artifacts should be left.
+     *
+     * On contrary, if the device has the odsign security fix, odrefresh should keep existing
+     * artifacts (see {@link #verifyMissingArtifactTriggersCompilation}).
+     */
+    @Test
+    public void verifyArtifactsClearedWhenNoPartialCompilation() throws Exception {
+        // Remove arbitrary system server artifacts to trigger compilation.
+        simulateMissingArtifacts();
+
+        // The successful case.
+        mTestUtils.removeCompilationLogToAvoidBackoff();
+        long timeMs = mTestUtils.getCurrentTimeMs();
+        mTestUtils.runOdrefreshNoPartialCompilation();
+
+        // Existing artifacts should be replaced with new ones.
+        mTestUtils.assertModifiedAfter(mTestUtils.getExpectedPrimaryBootImage(), timeMs);
+        mTestUtils.assertModifiedAfter(mTestUtils.getExpectedBootImageMainlineExtension(), timeMs);
+        mTestUtils.assertModifiedAfter(mTestUtils.getSystemServerExpectedArtifacts(), timeMs);
+
+        // Remove arbitrary system server artifacts to trigger compilation again.
+        simulateMissingArtifacts();
+
+        // The failed case.
+        mDeviceState.makeDex2oatFail();
+        mTestUtils.removeCompilationLogToAvoidBackoff();
+        timeMs = mTestUtils.getCurrentTimeMs();
+        mTestUtils.runOdrefreshNoPartialCompilation();
+
+        // Existing artifacts should be gone.
+        mTestUtils.assertFilesNotExist(mTestUtils.getExpectedPrimaryBootImage());
+        mTestUtils.assertFilesNotExist(mTestUtils.getExpectedBootImageMainlineExtension());
+        mTestUtils.assertFilesNotExist(mTestUtils.getSystemServerExpectedArtifacts());
+    }
+
     private Set<String> simulateMissingArtifacts() throws Exception {
         Set<String> missingArtifacts = new HashSet<>();
         String sample = mTestUtils.getSystemServerExpectedArtifacts().iterator().next();
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
index 5471c1f..41e45ea 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
@@ -482,8 +482,18 @@
 
     public void runOdrefresh(String extraArgs) throws Exception {
         mTestInfo.getDevice().executeShellV2Command(ODREFRESH_BIN + " --check");
+        mTestInfo.getDevice().executeShellV2Command(ODREFRESH_BIN
+                + " --partial-compilation=true --no-refresh " + extraArgs + " --compile");
+    }
+
+    /**
+     * Simulates how odsign invokes odrefresh on a device that doesn't have the security fix for
+     * CVE-2021-39689 (b/206090748).
+     */
+    public void runOdrefreshNoPartialCompilation() throws Exception {
+        // Note that odsign doesn't call `odrefresh --check` on such a device.
         mTestInfo.getDevice().executeShellV2Command(
-                ODREFRESH_BIN + " --partial-compilation --no-refresh " + extraArgs + " --compile");
+                ODREFRESH_BIN + " --partial-compilation=false --no-refresh --compile");
     }
 
     public boolean areAllApexesFactoryInstalled() throws Exception {