[RFC,v3,1/1] audit: speed up syscall rule filtering

yang.yang29 at zte.com.cn yang.yang29 at zte.com.cn
Sun Jan 24 13:04:01 UTC 2021


From 85b3eccf7f12b091b78cc5ba8abfaf759cf0334e Mon Sep 17 00:00:00 2001
From: Yang Yang <yang.yang29 at zte.com.cn>
Date: Sun, 24 Jan 2021 20:40:50 +0800
Subject: [PATCH] audit: speed up syscall rule filtering
audit_filter_syscall() traverses struct list_head audit_filter_list to find
out whether current syscall match one rule. This takes o(n), which is not
necessary, specially for user who add a very few syscall rules. On the other
hand, user may not much care about rule add/delete speed. So do o(n)
calculates when rule changes, and ease the burden of audit_filter_syscall().

Define audit_rule_syscall_mask[NR_syscalls], every element stands for
one syscall.audit_rule_syscall_mask[n] == 0 indicates no rule cares about
syscall n, so we can avoid unnecessary calling audit_filter_syscall().
audit_rule_syscall_mask[n] > 0 indicates at least one rule cares about
syscall n, then calls audit_filter_syscall(). Update
audit_rule_syscall_mask[n] when syscall rule changes.

Signed-off-by: Yang Yang <yang.yang29 at zte.com.cn>
---
 include/linux/audit.h |  3 +++
 kernel/auditfilter.c  |  4 ++++
 kernel/auditsc.c      | 36 ++++++++++++++++++++++++++++++++----
 3 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 82b7c11..867284c 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -302,6 +302,7 @@ extern void audit_seccomp(unsigned long syscall, long signr, int code);
 extern void audit_seccomp_actions_logged(const char *names,
                                         const char *old_names, int res);
 extern void __audit_ptrace(struct task_struct *t);
+extern void audit_rule_syscall_mask_update(struct audit_krule rule, bool add);

 static inline void audit_set_context(struct task_struct *task, struct audit_context *ctx)
 {
@@ -599,6 +600,8 @@ static inline void audit_seccomp(unsigned long syscall, long signr, int code)
 static inline void audit_seccomp_actions_logged(const char *names,
                                                const char *old_names, int res)
 { }
+static inline void audit_rule_syscall_mask_update(struct audit_krule rule, bool add)
+{}
 static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
 { }
 static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid,
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 333b3bc..0b2a8d5 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -957,6 +957,8 @@ static inline int audit_add_rule(struct audit_entry *entry)
                return err;
        }

+       audit_rule_syscall_mask_update(entry->rule, true);
+
        if (watch) {
                /* audit_filter_mutex is dropped and re-taken during this call */
                err = audit_add_watch(&entry->rule, &list);
@@ -1035,6 +1037,8 @@ int audit_del_rule(struct audit_entry *entry)
                goto out;
        }

+       audit_rule_syscall_mask_update(e->rule, false);
+
        if (e->rule.watch)
                audit_remove_watch_rule(&e->rule);

diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index ce8c9e2..1b8ff4e 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -97,6 +97,9 @@ int audit_n_rules;
 /* determines whether we collect data for signals sent */
 int audit_signals;

+/* whether one syscall needs to be audited */
+u32 audit_rule_syscall_mask[NR_syscalls] = {0};
+
 struct audit_aux_data {
        struct audit_aux_data   *next;
        int                     type;
@@ -799,6 +802,29 @@ static int audit_in_mask(const struct audit_krule *rule, unsigned long val)
        return rule->mask[word] & bit;
 }

+/**
+ * audit_rule_syscall_mask_update - update syscall mask when audit rule changes
+ * @rule: audit rule
+ * @add: add rule or delete
+ *
+ * Caller must hold audit_filter_mutex to prevent stale data.
+ */
+void audit_rule_syscall_mask_update(struct audit_krule rule, bool add)
+{
+       int i;
+
+       if (rule.listnr == AUDIT_FILTER_EXIT && !rule.watch && !rule.tree) {
+               for (i = 0; i < NR_syscalls; i++) {
+                       if (unlikely(audit_in_mask(&rule, i))) {
+                               if (add == true)
+                                       audit_rule_syscall_mask[i]++;
+                               else
+                                       audit_rule_syscall_mask[i]--;
+                       }
+               }
+       }
+}
+
 /* At syscall entry and exit time, this filter is called if the
  * audit_state is not low enough that auditing cannot take place, but is
  * also not high enough that we already know we have to write an audit
@@ -1627,8 +1653,9 @@ void __audit_free(struct task_struct *tsk)
                context->return_valid = AUDITSC_INVALID;
                context->return_code = 0;

-               audit_filter_syscall(tsk, context,
-                                    &audit_filter_list[AUDIT_FILTER_EXIT]);
+               if (unlikely(audit_rule_syscall_mask[context->major]))
+                       audit_filter_syscall(tsk, context,
+                                            &audit_filter_list[AUDIT_FILTER_EXIT]);
                audit_filter_inodes(tsk, context);
                if (context->current_state == AUDIT_RECORD_CONTEXT)
                        audit_log_exit();
@@ -1735,8 +1762,9 @@ void __audit_syscall_exit(int success, long return_code)
                else
                        context->return_code  = return_code;

-               audit_filter_syscall(current, context,
-                                    &audit_filter_list[AUDIT_FILTER_EXIT]);
+               if (unlikely(audit_rule_syscall_mask[context->major]))
+                       audit_filter_syscall(current, context,
+                                            &audit_filter_list[AUDIT_FILTER_EXIT]);
                audit_filter_inodes(current, context);
                if (context->current_state == AUDIT_RECORD_CONTEXT)
                        audit_log_exit();
--
2.15.2


More information about the Linux-audit mailing list