diff --git a/Utils.cpp b/Utils.cpp index 17fee28..e077ead 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -754,7 +754,53 @@ status_t ForkExecvp(const std::vector& args, std::vector& args) { +status_t ForkExecvpTimeout(const std::vector& args, std::chrono::seconds timeout, + char* context) { + int status; + + pid_t wait_timeout_pid = fork(); + if (wait_timeout_pid == 0) { + pid_t pid = ForkExecvpAsync(args, context); + if (pid == -1) { + _exit(EXIT_FAILURE); + } + pid_t timer_pid = fork(); + if (timer_pid == 0) { + sleep(timeout.count()); + _exit(ETIMEDOUT); + } + if (timer_pid == -1) { + PLOG(ERROR) << "fork in ForkExecvpAsync_timeout"; + kill(pid, SIGTERM); + _exit(EXIT_FAILURE); + } + 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 ForkExecvpAsync_timeout"; + 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; +} + +pid_t ForkExecvpAsync(const std::vector& args, char* context) { auto argv = ConvertToArgv(args); pid_t pid = fork(); @@ -762,6 +808,12 @@ pid_t ForkExecvpAsync(const std::vector& args) { close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); + if (context) { + if (setexeccon(context)) { + LOG(ERROR) << "Failed to setexeccon in ForkExecvpAsync"; + abort(); + } + } execvp(argv[0], const_cast(argv.data())); PLOG(ERROR) << "exec in ForkExecvpAsync"; diff --git a/Utils.h b/Utils.h index df322b8..b5bb4c2 100644 --- a/Utils.h +++ b/Utils.h @@ -38,6 +38,8 @@ static const char* kVoldAppDataIsolationEnabled = "persist.sys.vold_app_data_iso static const char* kExternalStorageSdcardfs = "external_storage.sdcardfs.enabled"; static const char* kFuseBpfEnabled = "persist.sys.fuse.bpf.enable"; +static constexpr std::chrono::seconds kUntrustedFsckSleepTime(45); + /* SELinux contexts used depending on the block device type */ extern char* sBlkidContext; extern char* sBlkidUntrustedContext; @@ -107,8 +109,10 @@ status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType, std /* Returns either WEXITSTATUS() status, or a negative errno */ status_t ForkExecvp(const std::vector& args, std::vector* output = nullptr, char* context = nullptr); +status_t ForkExecvpTimeout(const std::vector& args, std::chrono::seconds timeout, + char* context = nullptr); -pid_t ForkExecvpAsync(const std::vector& args); +pid_t ForkExecvpAsync(const std::vector& args, char* context = nullptr); /* Gets block device size in bytes */ status_t GetBlockDevSize(int fd, uint64_t* size); diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp index 7782dd3..c8b19e0 100644 --- a/fs/Exfat.cpp +++ b/fs/Exfat.cpp @@ -44,7 +44,7 @@ status_t Check(const std::string& source) { cmd.push_back("-y"); cmd.push_back(source); - int rc = ForkExecvp(cmd, nullptr, sFsckUntrustedContext); + int rc = ForkExecvpTimeout(cmd, kUntrustedFsckSleepTime, sFsckUntrustedContext); if (rc == 0) { LOG(INFO) << "Check OK"; return 0; diff --git a/fs/Vfat.cpp b/fs/Vfat.cpp index 4f1e982..f3f04d8 100644 --- a/fs/Vfat.cpp +++ b/fs/Vfat.cpp @@ -68,10 +68,9 @@ status_t Check(const std::string& source) { cmd.push_back(source); // Fat devices are currently always untrusted - rc = ForkExecvp(cmd, nullptr, sFsckUntrustedContext); - + rc = ForkExecvpTimeout(cmd, kUntrustedFsckSleepTime, sFsckUntrustedContext); if (rc < 0) { - LOG(ERROR) << "Filesystem check failed due to logwrap error"; + LOG(ERROR) << "Filesystem check failed due to fork error"; errno = EIO; return -1; } @@ -81,6 +80,10 @@ status_t Check(const std::string& source) { LOG(INFO) << "Filesystem check completed OK"; return 0; + case 1: + LOG(INFO) << "Failed to check filesystem"; + return -1; + case 2: LOG(ERROR) << "Filesystem check failed (not a FAT filesystem)"; errno = ENODATA; @@ -100,6 +103,11 @@ status_t Check(const std::string& source) { errno = ENODATA; return -1; + case ETIMEDOUT: + LOG(ERROR) << "Filesystem check timed out"; + errno = ETIMEDOUT; + return -1; + default: LOG(ERROR) << "Filesystem check failed (unknown exit code " << rc << ")"; errno = EIO;