Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
logd: sepolicy dynamic rate limiting
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Mark Salyzyn committed Jan 4, 2017
1 parent fe05f1c commit 247d682
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 17 deletions.
51 changes: 47 additions & 4 deletions logd/LogAudit.cpp
Expand Up @@ -43,27 +43,70 @@
'>'

LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) :
SocketListener(getLogSocket(), false),
SocketListener(mSock = getLogSocket(), false),
logbuf(buf),
reader(reader),
fdDmesg(fdDmesg),
main(__android_logger_property_get_bool("ro.logd.auditd.main",
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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -351,5 +393,6 @@ int LogAudit::getLogSocket() {
audit_close(fd);
fd = -1;
}
(void)audit_rate_limit(fd, AUDIT_RATE_LIMIT_DEFAULT);
return fd;
}
7 changes: 7 additions & 0 deletions logd/LogAudit.h
Expand Up @@ -17,6 +17,8 @@
#ifndef _LOGD_LOG_AUDIT_H__
#define _LOGD_LOG_AUDIT_H__

#include <queue>

#include <sysutils/SocketListener.h>

#include "LogBuffer.h"
Expand All @@ -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);
Expand Down
26 changes: 23 additions & 3 deletions logd/libaudit.c
Expand Up @@ -149,7 +149,7 @@ static int audit_send(int fd, int type, const void *data, size_t size)
return rc;
}

int audit_setup(int fd, uint32_t pid)
int audit_setup(int fd, pid_t pid)
{
int rc;
struct audit_message rep;
Expand All @@ -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));
Expand All @@ -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);
Expand Down
31 changes: 24 additions & 7 deletions logd/libaudit.h
Expand Up @@ -33,7 +33,7 @@ __BEGIN_DECLS
#define MAX_AUDIT_MESSAGE_LENGTH 8970

typedef enum {
GET_REPLY_BLOCKING=0,
GET_REPLY_BLOCKING = 0,
GET_REPLY_NONBLOCKING
} reply_t;

Expand All @@ -55,7 +55,7 @@ struct audit_message {
* A valid fd on success or < 0 on error with errno set.
* Returns the same errors as man 2 socket.
*/
extern int audit_open(void);
extern int audit_open(void);

/**
* Closes the fd returned from audit_open()
Expand All @@ -78,19 +78,36 @@ extern void audit_close(int fd);
* @return
* This function returns 0 on success, else -errno.
*/
extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block,
int peek);
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

Expand Down
5 changes: 4 additions & 1 deletion logd/tests/Android.mk
Expand Up @@ -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
Expand All @@ -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)

0 comments on commit 247d682

Please sign in to comment.