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

Commit df345a8a authored by Mark Salyzyn's avatar Mark Salyzyn Committed by Gerrit Code Review
Browse files

Merge changes Icc984f40,Ie4c46bc9,I0f238985,I63a729d2,I2b5900a2

* changes:
  logd: clearAll by UID speedup
  logd: clear return and deal with busy if readers locked
  logd: update region lock after entry has passed to reader socket
  logcat: do not exit buffer loop on error
  logd: clientHasLogCredentials false negatives
parents b3fbdf55 2b25c660
Loading
Loading
Loading
Loading
+40 −23
Original line number Diff line number Diff line
@@ -832,49 +832,66 @@ int main(int argc, char **argv)
    } else {
        logger_list = android_logger_list_alloc(mode, tail_lines, 0);
    }
    const char *openDeviceFail = NULL;
    const char *clearFail = NULL;
    const char *setSizeFail = NULL;
    const char *getSizeFail = NULL;
    // We have three orthogonal actions below to clear, set log size and
    // get log size. All sharing the same iteration loop.
    while (dev) {
        dev->logger_list = logger_list;
        dev->logger = android_logger_open(logger_list,
                                          android_name_to_log_id(dev->device));
        if (!dev->logger) {
            logcat_panic(false, "Unable to open log device '%s'\n",
                         dev->device);
            openDeviceFail = openDeviceFail ?: dev->device;
            dev = dev->next;
            continue;
        }

        if (clearLog) {
            int ret;
            ret = android_logger_clear(dev->logger);
            if (ret) {
                logcat_panic(false, "failed to clear the log");
            if (android_logger_clear(dev->logger)) {
                clearFail = clearFail ?: dev->device;
            }
        }

        if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
            logcat_panic(false, "failed to set the log size");
        if (setLogSize) {
            if (android_logger_set_log_size(dev->logger, setLogSize)) {
                setSizeFail = setSizeFail ?: dev->device;
            }

        if (getLogSize) {
            long size, readable;

            size = android_logger_get_log_size(dev->logger);
            if (size < 0) {
                logcat_panic(false, "failed to get the log size");
        }

            readable = android_logger_get_log_readable_size(dev->logger);
            if (readable < 0) {
                logcat_panic(false, "failed to get the readable log size");
            }
        if (getLogSize) {
            long size = android_logger_get_log_size(dev->logger);
            long readable = android_logger_get_log_readable_size(dev->logger);

            if ((size < 0) || (readable < 0)) {
                getSizeFail = getSizeFail ?: dev->device;
            } else {
                printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
                       "max entry is %db, max payload is %db\n", dev->device,
                       value_of_size(size), multiplier_of_size(size),
                       value_of_size(readable), multiplier_of_size(readable),
                   (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
                       (int) LOGGER_ENTRY_MAX_LEN,
                       (int) LOGGER_ENTRY_MAX_PAYLOAD);
            }
        }

        dev = dev->next;
    }
    // report any errors in the above loop and exit
    if (openDeviceFail) {
        logcat_panic(false, "Unable to open log device '%s'\n", openDeviceFail);
    }
    if (clearFail) {
        logcat_panic(false, "failed to clear the '%s' log\n", clearFail);
    }
    if (setSizeFail) {
        logcat_panic(false, "failed to set the '%s' log size\n", setSizeFail);
    }
    if (getSizeFail) {
        logcat_panic(false, "failed to get the readable '%s' log size",
                     getSizeFail);
    }

    if (setPruneList) {
        size_t len = strlen(setPruneList);
+1 −2
Original line number Diff line number Diff line
@@ -96,8 +96,7 @@ int CommandListener::ClearCmd::runCommand(SocketClient *cli,
        return 0;
    }

    mBuf.clear((log_id_t) id, uid);
    cli->sendMsg("success");
    cli->sendMsg(mBuf.clear((log_id_t) id, uid) ? "busy" : "success");
    return 0;
}

+60 −21
Original line number Diff line number Diff line
@@ -374,8 +374,10 @@ public:
//
// mLogElementsLock must be held when this function is called.
//
void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
    LogTimeEntry *oldest = NULL;
    bool busy = false;
    bool clearAll = pruneRows == ULONG_MAX;

    LogTimeEntry::lock();

@@ -393,35 +395,31 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
    LogBufferElementCollection::iterator it;

    if (caller_uid != AID_ROOT) {
        // Only here if clearAll condition (pruneRows == ULONG_MAX)
        for(it = mLogElements.begin(); it != mLogElements.end();) {
            LogBufferElement *e = *it;

            if (oldest && (oldest->mStart <= e->getSequence())) {
                break;
            }

            if (e->getLogId() != id) {
            if ((e->getLogId() != id) || (e->getUid() != caller_uid)) {
                ++it;
                continue;
            }

            if (e->getUid() == caller_uid) {
                it = erase(it);
                pruneRows--;
                if (pruneRows == 0) {
            if (oldest && (oldest->mStart <= e->getSequence())) {
                oldest->triggerSkip_Locked(id, pruneRows);
                busy = true;
                break;
            }
            } else {
                ++it;
            }

            it = erase(it);
            pruneRows--;
        }
        LogTimeEntry::unlock();
        return;
        return busy;
    }

    // prune by worst offender by uid
    bool hasBlacklist = mPrune.naughty();
    while (pruneRows > 0) {
    while (!clearAll && (pruneRows > 0)) {
        // recalculate the worst offender on every batched pass
        uid_t worst = (uid_t) -1;
        size_t worst_sizes = 0;
@@ -478,6 +476,7 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
            LogBufferElement *e = *it;

            if (oldest && (oldest->mStart <= e->getSequence())) {
                busy = true;
                break;
            }

@@ -585,7 +584,7 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
    }

    bool whitelist = false;
    bool hasWhitelist = mPrune.nice();
    bool hasWhitelist = mPrune.nice() && !clearAll;
    it = mLogElements.begin();
    while((pruneRows > 0) && (it != mLogElements.end())) {
        LogBufferElement *e = *it;
@@ -596,6 +595,8 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
        }

        if (oldest && (oldest->mStart <= e->getSequence())) {
            busy = true;

            if (whitelist) {
                break;
            }
@@ -631,6 +632,7 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
            }

            if (oldest && (oldest->mStart <= e->getSequence())) {
                busy = true;
                if (stats.sizes(id) > (2 * log_buffer_size(id))) {
                    // kick a misbehaving log reader client off the island
                    oldest->release_Locked();
@@ -646,13 +648,50 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
    }

    LogTimeEntry::unlock();

    return (pruneRows > 0) && busy;
}

// clear all rows of type "id" from the buffer.
void LogBuffer::clear(log_id_t id, uid_t uid) {
bool LogBuffer::clear(log_id_t id, uid_t uid) {
    bool busy = true;
    // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
    for (int retry = 4;;) {
        if (retry == 1) { // last pass
            // Check if it is still busy after the sleep, we say prune
            // one entry, not another clear run, so we are looking for
            // the quick side effect of the return value to tell us if
            // we have a _blocked_ reader.
            pthread_mutex_lock(&mLogElementsLock);
            busy = prune(id, 1, uid);
            pthread_mutex_unlock(&mLogElementsLock);
            // It is still busy, blocked reader(s), lets kill them all!
            // otherwise, lets be a good citizen and preserve the slow
            // readers and let the clear run (below) deal with determining
            // if we are still blocked and return an error code to caller.
            if (busy) {
                LogTimeEntry::lock();
                LastLogTimes::iterator times = mTimes.begin();
                while (times != mTimes.end()) {
                    LogTimeEntry *entry = (*times);
                    // Killer punch
                    if (entry->owned_Locked() && entry->isWatching(id)) {
                        entry->release_Locked();
                    }
                    times++;
                }
                LogTimeEntry::unlock();
            }
        }
        pthread_mutex_lock(&mLogElementsLock);
    prune(id, ULONG_MAX, uid);
        busy = prune(id, ULONG_MAX, uid);
        pthread_mutex_unlock(&mLogElementsLock);
        if (!busy || !--retry) {
            break;
        }
        sleep (1); // Let reader(s) catch up after notification
    }
    return busy;
}

// get the used space associated with "id".
+2 −2
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@ public:
                     int (*filter)(const LogBufferElement *element, void *arg) = NULL,
                     void *arg = NULL);

    void clear(log_id_t id, uid_t uid = AID_ROOT);
    bool clear(log_id_t id, uid_t uid = AID_ROOT);
    unsigned long getSize(log_id_t id);
    int setSize(log_id_t id, unsigned long size);
    unsigned long getSizeUsed(log_id_t id);
@@ -86,7 +86,7 @@ public:

private:
    void maybePrune(log_id_t id);
    void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
    bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
    LogBufferElementCollection::iterator erase(
        LogBufferElementCollection::iterator it, bool engageStats = true);
};
+68 −51
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@ LogCommand::LogCommand(const char *cmd) : FrameworkCommand(cmd) {
static bool groupIsLog(char *buf) {
    char *ptr;
    static const char ws[] = " \n";
    bool ret = false;

    for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
        errno = 0;
@@ -51,10 +50,10 @@ static bool groupIsLog(char *buf) {
            return false;
        }
        if (Gid == AID_LOG) {
            ret = true;
            return true;
        }
    }
    return ret;
    return false;
}

bool clientHasLogCredentials(SocketClient * cli) {
@@ -69,61 +68,79 @@ bool clientHasLogCredentials(SocketClient * cli) {
    }

    // FYI We will typically be here for 'adb logcat'
    bool ret = false;
    char filename[256];
    snprintf(filename, sizeof(filename), "/proc/%u/status", cli->getPid());

    char filename[1024];
    snprintf(filename, sizeof(filename), "/proc/%d/status", cli->getPid());
    bool ret;
    bool foundLog = false;
    bool foundGid = false;
    bool foundUid = false;

    //
    // Reading /proc/<pid>/status is rife with race conditions. All of /proc
    // suffers from this and its use should be minimized. However, we have no
    // choice.
    //
    // Notably the content from one 4KB page to the next 4KB page can be from a
    // change in shape even if we are gracious enough to attempt to read
    // atomically. getline can not even guarantee a page read is not split up
    // and in effect can read from different vintages of the content.
    //
    // We are finding out in the field that a 'logcat -c' via adb occasionally
    // is returned with permission denied when we did only one pass and thus
    // breaking scripts. For security we still err on denying access if in
    // doubt, but we expect the falses  should be reduced significantly as
    // three times is a charm.
    //
    for (int retry = 3;
            !(ret = foundGid && foundUid && foundLog) && retry;
            --retry) {
        FILE *file = fopen(filename, "r");
        if (!file) {
        return ret;
            continue;
        }

    bool foundGid = false;
    bool foundUid = false;

    char line[1024];
    while (fgets(line, sizeof(line), file)) {
        char *line = NULL;
        size_t len = 0;
        while (getline(&line, &len, file) > 0) {
            static const char groups_string[] = "Groups:\t";
            static const char uid_string[] = "Uid:\t";
            static const char gid_string[] = "Gid:\t";

        if (strncmp(groups_string, line, strlen(groups_string)) == 0) {
            ret = groupIsLog(line + strlen(groups_string));
            if (!ret) {
                break;
            if (strncmp(groups_string, line, sizeof(groups_string) - 1) == 0) {
                if (groupIsLog(line + sizeof(groups_string) - 1)) {
                    foundLog = true;
                }
        } else if (strncmp(uid_string, line, strlen(uid_string)) == 0) {
            } else if (strncmp(uid_string, line, sizeof(uid_string) - 1) == 0) {
                uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};

            sscanf(line + strlen(uid_string), "%u\t%u\t%u\t%u",
                sscanf(line + sizeof(uid_string) - 1, "%u\t%u\t%u\t%u",
                       &u[0], &u[1], &u[2], &u[3]);

            // Protect against PID reuse by checking that the UID is the same
            if ((uid != u[0]) || (uid != u[1]) || (uid != u[2]) || (uid != u[3])) {
                ret = false;
                break;
            }
                // Protect against PID reuse by checking that UID is the same
                if ((uid == u[0])
                        && (uid == u[1])
                        && (uid == u[2])
                        && (uid == u[3])) {
                    foundUid = true;
        } else if (strncmp(gid_string, line, strlen(gid_string)) == 0) {
                }
            } else if (strncmp(gid_string, line, sizeof(gid_string) - 1) == 0) {
                gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};

            sscanf(line + strlen(gid_string), "%u\t%u\t%u\t%u",
                sscanf(line + sizeof(gid_string) - 1, "%u\t%u\t%u\t%u",
                       &g[0], &g[1], &g[2], &g[3]);

            // Protect against PID reuse by checking that the GID is the same
            if ((gid != g[0]) || (gid != g[1]) || (gid != g[2]) || (gid != g[3])) {
                ret = false;
                break;
            }
                // Protect against PID reuse by checking that GID is the same
                if ((gid == g[0])
                        && (gid == g[1])
                        && (gid == g[2])
                        && (gid == g[3])) {
                    foundGid = true;
                }
            }

        }
        free(line);
        fclose(file);

    if (!foundGid || !foundUid) {
        ret = false;
    }

    return ret;
Loading