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

Commit 247d682f authored by Mark Salyzyn's avatar Mark Salyzyn
Browse files

logd: sepolicy dynamic rate limiting

Processing overhead for selinux violation messages is costly. We want
to deal with bursts of violations, but we have no intent of allowing
that sustained burst to go unabated as there is a cost of processing
and battery usage.

Tunables in libaudit.h are:

AUDIT_RATE_LIMIT_DEFAULT 20        /* acceptable burst rate      */
AUDIT_RATE_LIMIT_BURST_DURATION 10 /* number of seconds of burst */
AUDIT_RATE_LIMIT_MAX     5         /* acceptable sustained rate  */

Since we can only asymptotically handle DEFAULT rate, we set an upper
threshold of half way between the MAX and DEFAULT rate.

Default kernel audit subsystem message rate is set to 20 a second.
If sepolicy exceeds 125 violation messages over up to ten seconds
(>=~12/s), tell kernel audit subsystem to drop the rate to 5 messages
a second.  If rate drops below 50 messages over the past ten seconds
(<5/s), tell kernel it is ok to increase the burst rate back to 20
messages a second.

Test: gTest logd-unit-tests --gtest_filter=logd.sepolicy_rate_limiter_*
Bug: 27878170
Change-Id: I843f8dcfbb3ecfbbe94a4865ea332c858e3be7f2
parent fe05f1cd
Loading
Loading
Loading
Loading
+47 −4
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@
    '>'

LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) :
        SocketListener(getLogSocket(), false),
        SocketListener(mSock = getLogSocket(), false),
        logbuf(buf),
        reader(reader),
        fdDmesg(fdDmesg),
@@ -51,19 +51,62 @@ LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) :
                                                BOOL_DEFAULT_TRUE)),
        events(__android_logger_property_get_bool("ro.logd.auditd.events",
                                                  BOOL_DEFAULT_TRUE)),
        initialized(false) {
        initialized(false),
        tooFast(false) {
    static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
        'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
        ' ', 's', 't', 'a', 'r', 't', '\n' };
    write(fdDmesg, auditd_message, sizeof(auditd_message));
}

void LogAudit::checkRateLimit() {

    // trim list for AUDIT_RATE_LIMIT_BURST_DURATION of history
    log_time oldest(AUDIT_RATE_LIMIT_BURST_DURATION, 0);
    bucket.emplace(android_log_clockid());
    oldest = bucket.back() - oldest;
    while (bucket.front() < oldest) bucket.pop();

    static const size_t upperThreshold =
        ((AUDIT_RATE_LIMIT_BURST_DURATION *
          (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) + 1) /
                              2;
    if (bucket.size() >= upperThreshold) {
        // Hit peak, slow down source
        if (!tooFast) {
            tooFast = true;
            audit_rate_limit(mSock, AUDIT_RATE_LIMIT_MAX);
        }

        // We do not need to hold on to the full set of timing data history,
        // let's ensure it does not grow without bounds.  This also ensures
        // that std::dequeue underneath behaves almost like a ring buffer.
        do {
            bucket.pop();
        } while (bucket.size() >= upperThreshold);
        return;
    }

    if (!tooFast) return;

    static const size_t lowerThreshold = AUDIT_RATE_LIMIT_BURST_DURATION *
                                         AUDIT_RATE_LIMIT_MAX;

    if (bucket.size() >= lowerThreshold) return;

    tooFast = false;
    // Went below max sustained rate, allow source to speed up
    audit_rate_limit(mSock, AUDIT_RATE_LIMIT_DEFAULT);
}

bool LogAudit::onDataAvailable(SocketClient *cli) {
    if (!initialized) {
        prctl(PR_SET_NAME, "logd.auditd");
        initialized = true;
    }

    checkRateLimit();

    struct audit_message rep;

    rep.nlh.nlmsg_type = 0;
@@ -75,8 +118,7 @@ bool LogAudit::onDataAvailable(SocketClient *cli) {
        return false;
    }

    logPrint("type=%d %.*s",
        rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
    logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);

    return true;
}
@@ -351,5 +393,6 @@ int LogAudit::getLogSocket() {
        audit_close(fd);
        fd = -1;
    }
    (void)audit_rate_limit(fd, AUDIT_RATE_LIMIT_DEFAULT);
    return fd;
}
+7 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#ifndef _LOGD_LOG_AUDIT_H__
#define _LOGD_LOG_AUDIT_H__

#include <queue>

#include <sysutils/SocketListener.h>

#include "LogBuffer.h"
@@ -31,6 +33,11 @@ class LogAudit : public SocketListener {
    bool events;
    bool initialized;

    bool tooFast;
    int mSock;
    std::queue<log_time> bucket;
    void checkRateLimit();

public:
    LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
    int log(char *buf, size_t len);
+23 −3
Original line number Diff line number Diff line
@@ -149,7 +149,7 @@ out:
    return rc;
}

int audit_setup(int fd, uint32_t pid)
int audit_setup(int fd, pid_t pid)
{
    int rc;
    struct audit_message rep;
@@ -163,8 +163,7 @@ int audit_setup(int fd, uint32_t pid)
     * and the the mask set to AUDIT_STATUS_PID
     */
    status.pid = pid;
    status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
    status.rate_limit = 20; // audit entries per second
    status.mask = AUDIT_STATUS_PID;

    /* Let the kernel know this pid will be registering for audit events */
    rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
@@ -187,6 +186,27 @@ int audit_setup(int fd, uint32_t pid)
    return 0;
}

int audit_rate_limit(int fd, unsigned rate_limit)
{
    int rc;
    struct audit_message rep;
    struct audit_status status;

    memset(&status, 0, sizeof(status));

    status.mask = AUDIT_STATUS_RATE_LIMIT;
    status.rate_limit = rate_limit; /* audit entries per second */

    rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
    if (rc < 0) {
        return rc;
    }

    audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);

    return 0;
}

int audit_open()
{
    return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
+24 −7
Original line number Diff line number Diff line
@@ -82,15 +82,32 @@ extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block,
                           int peek);

/**
 * Sets a pid to recieve audit netlink events from the kernel
 * Sets a pid to receive audit netlink events from the kernel
 * @param fd
 *  The fd returned by a call to audit_open()
 * @param pid
 *  The pid whom to set as the reciever of audit messages
 *  The pid whom to set as the receiver of audit messages
 * @return
 *  This function returns 0 on success, -errno on error.
 */
extern int  audit_setup(int fd, uint32_t pid);
extern int audit_setup(int fd, pid_t pid);

/**
 * Sets the rate limit to receive audit netlink events from the kernel
 * @param fd
 *  The fd returned by a call to audit_open()
 * @param max_rate
 *  The cap of the maximum number of audit messages a second
 * @return
 *  This function returns 0 on success, -errno on error.
 */

/* Guidelines to follow for dynamic rate_limit */
#define AUDIT_RATE_LIMIT_DEFAULT 20        /* acceptable burst rate      */
#define AUDIT_RATE_LIMIT_BURST_DURATION 10 /* number of seconds of burst */
#define AUDIT_RATE_LIMIT_MAX     5         /* acceptable sustained rate  */

extern int audit_rate_limit(int fd, unsigned rate_limit);

__END_DECLS

+4 −1
Original line number Diff line number Diff line
@@ -27,12 +27,15 @@ test_tags := tests
# Unit tests.
# -----------------------------------------------------------------------------

event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004

test_c_flags := \
    -fstack-protector-all \
    -g \
    -Wall -Wextra \
    -Werror \
    -fno-builtin \
    $(event_flag)

test_src_files := \
    logd_test.cpp
@@ -43,6 +46,6 @@ include $(CLEAR_VARS)
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
Loading