Add timeout for mount on untrusted media

Under some circumstances it seems mounting a device can take a long
time. We already abort fsck if it runs for too long, so this is the last
remaining spot this could happen.

Bug: 339740354
Test: Mount a very slow device, observe no Android restart
Change-Id: I3607be269d3aeb4c2391d1d8e2372e3a6795e7c4
diff --git a/Utils.cpp b/Utils.cpp
index a3db43e..c4070d1 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -754,6 +754,57 @@
     return OK;
 }
 
+status_t ForkTimeout(int (*func)(void*), void* args, std::chrono::seconds timeout) {
+    int status;
+
+    // We're waiting on either the timeout or workload process to finish, so we're
+    // initially forking to get away from any other vold children
+    pid_t wait_timeout_pid = fork();
+    if (wait_timeout_pid == 0) {
+        pid_t pid = fork();
+        if (pid == 0) {
+            _exit(func(args));
+        }
+        if (pid == -1) {
+            _exit(EXIT_FAILURE);
+        }
+        pid_t timer_pid = fork();
+        if (timer_pid == 0) {
+            std::this_thread::sleep_for(timeout);
+            _exit(ETIMEDOUT);
+        }
+        if (timer_pid == -1) {
+            PLOG(ERROR) << "fork in ForkTimeout failed";
+            kill(pid, SIGTERM);
+            _exit(EXIT_FAILURE);
+        }
+        // Preserve the exit code of the first process to finish, and end the other
+        pid_t finished = wait(&status);
+        if (finished == pid) {
+            kill(timer_pid, SIGTERM);
+        } else {
+            kill(pid, SIGTERM);
+        }
+        if (!WIFEXITED(status)) {
+            _exit(ECHILD);
+        }
+        _exit(WEXITSTATUS(status));
+    }
+    if (waitpid(wait_timeout_pid, &status, 0) == -1) {
+        PLOG(ERROR) << "waitpid in ForkTimeout failed";
+        return -errno;
+    }
+    if (!WIFEXITED(status)) {
+        LOG(ERROR) << "Process did not exit normally, status: " << status;
+        return -ECHILD;
+    }
+    if (WEXITSTATUS(status)) {
+        LOG(ERROR) << "Process exited with code: " << WEXITSTATUS(status);
+        return WEXITSTATUS(status);
+    }
+    return OK;
+}
+
 status_t ForkExecvpTimeout(const std::vector<std::string>& args, std::chrono::seconds timeout,
                            char* context) {
     int status;
diff --git a/Utils.h b/Utils.h
index 39723ec..0eca902 100644
--- a/Utils.h
+++ b/Utils.h
@@ -39,6 +39,7 @@
 static const char* kExternalStorageSdcardfs = "external_storage.sdcardfs.enabled";
 
 static constexpr std::chrono::seconds kUntrustedFsckSleepTime(45);
+static constexpr std::chrono::seconds kUntrustedMountSleepTime(20);
 
 /* SELinux contexts used depending on the block device type */
 extern char* sBlkidContext;
@@ -107,6 +108,7 @@
                                std::string* fsLabel);
 
 /* Returns either WEXITSTATUS() status, or a negative errno */
+status_t ForkTimeout(int (*func)(void*), void* args, std::chrono::seconds timeout);
 status_t ForkExecvp(const std::vector<std::string>& args,
                     std::vector<std::string>* output = nullptr, char* context = nullptr);
 status_t ForkExecvpTimeout(const std::vector<std::string>& args, std::chrono::seconds timeout,
diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp
index ed53921..48fa6a3 100644
--- a/fs/Exfat.cpp
+++ b/fs/Exfat.cpp
@@ -58,8 +58,8 @@
     }
 }
 
-status_t Mount(const std::string& source, const std::string& target, int ownerUid, int ownerGid,
-               int permMask) {
+status_t DoMount(const std::string& source, const std::string& target, int ownerUid, int ownerGid,
+                 int permMask) {
     int mountFlags = MS_NODEV | MS_NOSUID | MS_DIRSYNC | MS_NOATIME | MS_NOEXEC;
     auto mountData = android::base::StringPrintf("uid=%d,gid=%d,fmask=%o,dmask=%o", ownerUid,
                                                  ownerGid, permMask, permMask);
@@ -77,6 +77,27 @@
     return -1;
 }
 
+struct mount_args {
+    const std::string& source;
+    const std::string& target;
+    int ownerUid;
+    int ownerGid;
+    int permMask;
+};
+
+int DoMountWrapper(void* args) {
+    struct mount_args* m_args = (struct mount_args*)args;
+
+    return DoMount(m_args->source, m_args->target, m_args->ownerUid, m_args->ownerGid,
+                   m_args->permMask);
+}
+
+status_t Mount(const std::string& source, const std::string& target, int ownerUid, int ownerGid,
+               int permMask) {
+    struct mount_args args = {source, target, ownerUid, ownerGid, permMask};
+    return ForkTimeout(DoMountWrapper, &args, kUntrustedMountSleepTime);
+}
+
 status_t Format(const std::string& source) {
     std::vector<std::string> cmd;
     cmd.push_back(kMkfsPath);
diff --git a/fs/Vfat.cpp b/fs/Vfat.cpp
index d9e2713..3bab02f 100644
--- a/fs/Vfat.cpp
+++ b/fs/Vfat.cpp
@@ -129,8 +129,8 @@
     return (int16_t)(utcOffsetSeconds / 60);
 }
 
-status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount,
-               bool executable, int ownerUid, int ownerGid, int permMask, bool createLost) {
+status_t DoMount(const std::string& source, const std::string& target, bool ro, bool remount,
+                 bool executable, int ownerUid, int ownerGid, int permMask, bool createLost) {
     int rc;
     unsigned long flags;
 
@@ -198,6 +198,32 @@
     return rc;
 }
 
+struct mount_args {
+    const std::string& source;
+    const std::string& target;
+    bool ro;
+    bool remount;
+    bool executable;
+    int ownerUid;
+    int ownerGid;
+    int permMask;
+    bool createLost;
+};
+
+int DoMountWrapper(void* args) {
+    struct mount_args* m_args = (struct mount_args*)args;
+
+    return DoMount(m_args->source, m_args->target, m_args->ro, m_args->remount, m_args->executable,
+                   m_args->ownerUid, m_args->ownerGid, m_args->permMask, m_args->createLost);
+}
+
+status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount,
+               bool executable, int ownerUid, int ownerGid, int permMask, bool createLost) {
+    struct mount_args args = {source,   target,   ro,       remount,   executable,
+                              ownerUid, ownerGid, permMask, createLost};
+    return ForkTimeout(DoMountWrapper, &args, kUntrustedMountSleepTime);
+}
+
 status_t Format(const std::string& source, unsigned long numSectors) {
     std::vector<std::string> cmd;
     cmd.push_back(kMkfsPath);