Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab34199e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Found out it is not possible to exec procrank as non-root/shell. But it...

Merge "Found out it is not possible to exec procrank as non-root/shell. But it is ok to disable procrank since the information can be obtain via dumpsys meminfo" into pi-dev
parents e1003296 c36e91dd
Loading
Loading
Loading
Loading
+19 −59
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@

#include <dirent.h>
#include <errno.h>
#include <wait.h>

#include <mutex>
#include <set>
@@ -53,48 +52,14 @@ const int FIELD_ID_INCIDENT_HEADER = 1;
const int FIELD_ID_INCIDENT_METADATA = 2;

// incident section parameters
const int WAIT_MAX = 5;
const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
const char INCIDENT_HELPER[] = "/system/bin/incident_helper";
const char GZIP[] = "/system/bin/gzip";
const char* GZIP[] = {"/system/bin/gzip", NULL};

static pid_t fork_execute_incident_helper(const int id, Fpipe* p2cPipe, Fpipe* c2pPipe) {
    const char* ihArgs[]{INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL};
    return fork_execute_cmd(INCIDENT_HELPER, const_cast<char**>(ihArgs), p2cPipe, c2pPipe);
    return fork_execute_cmd(const_cast<char**>(ihArgs), p2cPipe, c2pPipe);
}

// ================================================================================
static status_t statusCode(int status) {
    if (WIFSIGNALED(status)) {
        VLOG("return by signal: %s", strerror(WTERMSIG(status)));
        return -WTERMSIG(status);
    } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
        VLOG("return by exit: %s", strerror(WEXITSTATUS(status)));
        return -WEXITSTATUS(status);
    }
    return NO_ERROR;
}

static status_t kill_child(pid_t pid) {
    int status;
    VLOG("try to kill child process %d", pid);
    kill(pid, SIGKILL);
    if (waitpid(pid, &status, 0) == -1) return -1;
    return statusCode(status);
}

static status_t wait_child(pid_t pid) {
    int status;
    bool died = false;
    // wait for child to report status up to 1 seconds
    for (int loop = 0; !died && loop < WAIT_MAX; loop++) {
        if (waitpid(pid, &status, WNOHANG) == pid) died = true;
        // sleep for 0.2 second
        nanosleep(&WAIT_INTERVAL_NS, NULL);
    }
    if (!died) return kill_child(pid);
    return statusCode(status);
}
// ================================================================================
static status_t write_section_header(int fd, int sectionId, size_t size) {
    uint8_t buf[20];
@@ -328,12 +293,15 @@ status_t FileSection::Execute(ReportRequestSet* requests) const {
}
// ================================================================================
GZipSection::GZipSection(int id, const char* filename, ...) : Section(id) {
    name = "gzip ";
    name += filename;
    va_list args;
    va_start(args, filename);
    mFilenames = varargs(filename, args);
    va_end(args);
    name = "gzip";
    for (int i = 0; mFilenames[i] != NULL; i++) {
        name += " ";
        name += mFilenames[i];
    }
}

GZipSection::~GZipSection() {}
@@ -362,8 +330,7 @@ status_t GZipSection::Execute(ReportRequestSet* requests) const {
        return -errno;
    }

    const char* gzipArgs[]{GZIP, NULL};
    pid_t pid = fork_execute_cmd(GZIP, const_cast<char**>(gzipArgs), &p2cPipe, &c2pPipe);
    pid_t pid = fork_execute_cmd((char* const*)GZIP, &p2cPipe, &c2pPipe);
    if (pid == -1) {
        ALOGW("GZipSection '%s' failed to fork", this->name.string());
        return -errno;
@@ -559,19 +526,27 @@ status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const {
// ================================================================================
CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
    : Section(id, timeoutMs) {
    name = command;
    va_list args;
    va_start(args, command);
    mCommand = varargs(command, args);
    va_end(args);
    name = "cmd";
    for (int i = 0; mCommand[i] != NULL; i++) {
        name += " ";
        name += mCommand[i];
    }
}

CommandSection::CommandSection(int id, const char* command, ...) : Section(id) {
    name = command;
    va_list args;
    va_start(args, command);
    mCommand = varargs(command, args);
    va_end(args);
    name = "cmd";
    for (int i = 0; mCommand[i] != NULL; i++) {
        name += " ";
        name += mCommand[i];
    }
}

CommandSection::~CommandSection() { free(mCommand); }
@@ -586,26 +561,11 @@ status_t CommandSection::Execute(ReportRequestSet* requests) const {
        return -errno;
    }

    pid_t cmdPid = fork();
    pid_t cmdPid = fork_execute_cmd((char* const*)mCommand, NULL, &cmdPipe);
    if (cmdPid == -1) {
        ALOGW("CommandSection '%s' failed to fork", this->name.string());
        return -errno;
    }
    // child process to execute the command as root
    if (cmdPid == 0) {
        // replace command's stdout with ihPipe's write Fd
        if (dup2(cmdPipe.writeFd().get(), STDOUT_FILENO) != 1 || !ihPipe.close() ||
            !cmdPipe.close()) {
            ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(),
                  strerror(errno));
            _exit(EXIT_FAILURE);
        }
        execvp(this->mCommand[0], (char* const*)this->mCommand);
        int err = errno;  // record command error code
        ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(),
              strerror(errno));
        _exit(err);  // exit with command error code
    }
    pid_t ihPid = fork_execute_incident_helper(this->id, &cmdPipe, &ihPipe);
    if (ihPid == -1) {
        ALOGW("CommandSection '%s' failed to fork", this->name.string());
+49 −11
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include "incidentd_util.h"

#include <sys/prctl.h>
#include <wait.h>

#include "section_list.h"

@@ -57,27 +58,28 @@ unique_fd& Fpipe::readFd() { return mRead; }

unique_fd& Fpipe::writeFd() { return mWrite; }

pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output) {
pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output) {
    // fork used in multithreaded environment, avoid adding unnecessary code in child process
    pid_t pid = fork();
    if (pid == 0) {
        if (TEMP_FAILURE_RETRY(dup2(input->readFd().get(), STDIN_FILENO)) < 0 || !input->close() ||
            TEMP_FAILURE_RETRY(dup2(output->writeFd().get(), STDOUT_FILENO)) < 0 ||
        VLOG("[In child]cmd %s", argv[0]);
        if (input != NULL && (TEMP_FAILURE_RETRY(dup2(input->readFd().get(), STDIN_FILENO)) < 0 ||
                              !input->close())) {
            ALOGW("Failed to dup2 stdin.");
            _exit(EXIT_FAILURE);
        }
        if (TEMP_FAILURE_RETRY(dup2(output->writeFd().get(), STDOUT_FILENO)) < 0 ||
            !output->close()) {
            ALOGW("Can't setup stdin and stdout for command %s", cmd);
            ALOGW("Failed to dup2 stdout.");
            _exit(EXIT_FAILURE);
        }

        /* make sure the child dies when incidentd dies */
        prctl(PR_SET_PDEATHSIG, SIGKILL);

        execv(cmd, argv);

        ALOGW("%s failed in the child process: %s", cmd, strerror(errno));
        _exit(EXIT_FAILURE);  // always exits with failure if any
        execvp(argv[0], argv);
        _exit(errno);  // always exits with failure if any
    }
    // close the fds used in child process.
    input->readFd().reset();
    if (input != NULL) input->readFd().reset();
    output->writeFd().reset();
    return pid;
}
@@ -111,3 +113,39 @@ uint64_t Nanotime() {
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return static_cast<uint64_t>(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec);
}

// ================================================================================
const int WAIT_MAX = 5;
const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};

static status_t statusCode(int status) {
    if (WIFSIGNALED(status)) {
        VLOG("return by signal: %s", strerror(WTERMSIG(status)));
        return -WTERMSIG(status);
    } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
        VLOG("return by exit: %s", strerror(WEXITSTATUS(status)));
        return -WEXITSTATUS(status);
    }
    return NO_ERROR;
}

status_t kill_child(pid_t pid) {
    int status;
    VLOG("try to kill child process %d", pid);
    kill(pid, SIGKILL);
    if (waitpid(pid, &status, 0) == -1) return -1;
    return statusCode(status);
}

status_t wait_child(pid_t pid) {
    int status;
    bool died = false;
    // wait for child to report status up to 1 seconds
    for (int loop = 0; !died && loop < WAIT_MAX; loop++) {
        if (waitpid(pid, &status, WNOHANG) == pid) died = true;
        // sleep for 0.2 second
        nanosleep(&WAIT_INTERVAL_NS, NULL);
    }
    if (!died) return kill_child(pid);
    return statusCode(status);
}
+13 −3
Original line number Diff line number Diff line
@@ -18,12 +18,15 @@
#ifndef INCIDENTD_UTIL_H
#define INCIDENTD_UTIL_H

#include <android-base/unique_fd.h>

#include <stdarg.h>
#include <unistd.h>

#include <android-base/unique_fd.h>
#include <utils/Errors.h>

#include "Privacy.h"

using namespace android;
using namespace android::base;

/**
@@ -52,8 +55,9 @@ private:
/**
 * Forks and exec a command with two pipes, one connects stdin for input,
 * one connects stdout for output. It returns the pid of the child.
 * Input pipe can be NULL to indicate child process doesn't read stdin.
 */
pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output);
pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output);

/**
 * Grabs varargs from stack and stores them in heap with NULL-terminated array.
@@ -65,4 +69,10 @@ const char** varargs(const char* first, va_list rest);
 */
uint64_t Nanotime();

/**
 * Methods to wait or kill child process, return exit status code.
 */
status_t kill_child(pid_t pid);
status_t wait_child(pid_t pid);

#endif  // INCIDENTD_UTIL_H
+4 −4
Original line number Diff line number Diff line
@@ -173,12 +173,12 @@ TEST_F(SectionTest, CommandSectionConstructor) {
    CommandSection cs3(1, 3123, "echo", "\"this is a test\"", "ooo", NULL);
    CommandSection cs4(2, 43214, "single_command", NULL);

    EXPECT_THAT(cs1.name.string(), StrEq("echo"));
    EXPECT_THAT(cs2.name.string(), StrEq("single_command"));
    EXPECT_THAT(cs1.name.string(), StrEq("cmd echo \"this is a test\" ooo"));
    EXPECT_THAT(cs2.name.string(), StrEq("cmd single_command"));
    EXPECT_EQ(3123, cs3.timeoutMs);
    EXPECT_EQ(43214, cs4.timeoutMs);
    EXPECT_THAT(cs3.name.string(), StrEq("echo"));
    EXPECT_THAT(cs4.name.string(), StrEq("single_command"));
    EXPECT_THAT(cs3.name.string(), StrEq("cmd echo \"this is a test\" ooo"));
    EXPECT_THAT(cs4.name.string(), StrEq("cmd single_command"));
}

TEST_F(SectionTest, CommandSectionEcho) {
+8 −2
Original line number Diff line number Diff line
@@ -134,8 +134,14 @@ message IncidentProto {

    // Linux services
    optional ProcrankProto procrank = 2000 [
        (section).type = SECTION_NONE, // disable procrank until figure out permission
        (section).args = "/system/xbin/procrank"
        // Disable procrank for reasons below:
        // 1. incidentd can't execute `procrank` because it don't have DAC perms
        //    since it is running as its own uid, no root access.
        // 2. the same information is able to be accessed by meminfo dumpsys.
        // 3. leave this one here to show case of how to disable a section
        //    (no removal allowed if you are familiar with PROTOBUF).
        (section).type = SECTION_NONE,
        (section).args = "procrank"
    ];

    optional PageTypeInfoProto page_type_info = 2001 [