Rule listing deadlock.

David Woodhouse dwmw2 at infradead.org
Mon Jun 20 18:39:49 UTC 2005


As discussed, we need to send rules back to auditctl from a separate
context, because otherwise we'll just fill the available socket buffer
space and then deadlock waiting for userspace to read from it... but
without ever actually returning to userspace to allow it to do that.

This spawns a kernel thread to do the job for listing filtering rules --
we need to do the same for watches. Tim? Watch the locking (no pun
intended).

--- linux-2.6.9/kernel/audit.c	2005-06-20 17:27:16.000000000 +0100
+++ linux-2.6.9/kernel/audit.c	2005-06-20 17:27:23.000000000 +0100
@@ -116,7 +116,7 @@ static DECLARE_WAIT_QUEUE_HEAD(audit_bac
  * there is still a chance of watch removal via a hook.  In this case, the
  * semaphore is not enough enough.
  */
-static DECLARE_MUTEX(audit_netlink_sem);
+DECLARE_MUTEX(audit_netlink_sem);
 
 /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
  * audit records.  Since printk uses a 1024 byte buffer, this buffer
--- linux-2.6.9/kernel/auditsc.c	2005-06-20 16:46:53.000000000 +0100
+++ linux-2.6.9/kernel/auditsc.c	2005-06-20 17:29:23.000000000 +0100
@@ -39,6 +39,7 @@
 #include <linux/audit.h>
 #include <linux/personality.h>
 #include <linux/time.h>
+#include <linux/kthread.h>
 #include <asm/unistd.h>
 
 /* 0 = no checking
@@ -291,24 +292,61 @@ static int audit_copy_rule(struct audit_
 	return 0;
 }
 
+extern struct semaphore audit_netlink_sem;
+
+struct audit_reply_dest {
+	int pid;
+	int seq;
+};
+
+int audit_list_rules(void *_dest)
+{
+	int pid, seq;
+	struct audit_reply_dest *dest = _dest;
+	struct audit_entry *entry;
+	int i;
+
+	pid = dest->pid;
+	seq = dest->seq;
+	kfree(dest);
+
+	down(&audit_netlink_sem);
+
+	/* The *_rcu iterators not needed here because we are
+	   always called with audit_netlink_sem held. */
+	for (i=0; i<AUDIT_NR_FILTERS; i++) {
+		list_for_each_entry(entry, &audit_filter_list[i], list)
+			audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
+					 &entry->rule, sizeof(entry->rule));
+	}
+	audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
+	
+	up(&audit_netlink_sem);
+	return 0;
+}
+
 int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
 							uid_t loginuid)
 {
 	struct audit_entry *entry;
+	struct task_struct *tsk;
+	struct audit_reply_dest *dest;
 	int		   err = 0;
-	int i;
 	unsigned listnr;
 
 	switch (type) {
 	case AUDIT_LIST:
-		/* The *_rcu iterators not needed here because we are
-		   always called with audit_netlink_sem held. */
-		for (i=0; i<AUDIT_NR_FILTERS; i++) {
-			list_for_each_entry(entry, &audit_filter_list[i], list)
-				audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
-						 &entry->rule, sizeof(entry->rule));
+		dest = kmalloc(sizeof (*dest), GFP_KERNEL);
+		if (!dest)
+			return -ENOMEM;
+		dest->pid = pid;
+		dest->seq = seq;
+
+		tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
+		if (IS_ERR(tsk)) {
+			kfree(dest);
+			err = PTR_ERR(tsk);
 		}
-		audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
 		break;
 	case AUDIT_ADD:
 		if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))


-- 
dwmw2




More information about the Linux-audit mailing list