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