diff options
author | Eric Paris <eparis@redhat.com> | 2008-04-18 10:11:04 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2008-04-28 06:19:13 -0400 |
commit | f09ac9db2aafe36fde9ebd63c8c5d776f6e7bd41 (patch) | |
tree | ae2123e2bd6c054d82d5d2a3b81fdfb30c53e46e | |
parent | f3d357b092956959563398b59ef2fdd10aea387d (diff) |
Audit: stop deadlock from signals under load
A deadlock is possible between kauditd and auditd under load if auditd
receives a signal. When auditd receives a signal it sends a netlink
message to the kernel asking for information about the sender of the
signal. In that same context the audit system will attempt to send a
netlink message back to the userspace auditd. If kauditd has already
filled the socket buffer (see netlink_attachskb()) auditd will now put
itself to sleep waiting for room to send the message. Since auditd is
responsible for draining that socket we have a deadlock. The fix, since
the response from the kernel does not need to be synchronous is to send
the signal information back to auditd in a separate thread. And thus
auditd can continue to drain the audit queue normally.
Signed-off-by: Eric Paris <eparis@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | kernel/audit.c | 40 |
1 files changed, 35 insertions, 5 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index fee9052eb5cf..520583d8ca18 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -156,6 +156,11 @@ struct audit_buffer { | |||
156 | gfp_t gfp_mask; | 156 | gfp_t gfp_mask; |
157 | }; | 157 | }; |
158 | 158 | ||
159 | struct audit_reply { | ||
160 | int pid; | ||
161 | struct sk_buff *skb; | ||
162 | }; | ||
163 | |||
159 | static void audit_set_pid(struct audit_buffer *ab, pid_t pid) | 164 | static void audit_set_pid(struct audit_buffer *ab, pid_t pid) |
160 | { | 165 | { |
161 | if (ab) { | 166 | if (ab) { |
@@ -528,6 +533,19 @@ nlmsg_failure: /* Used by NLMSG_PUT */ | |||
528 | return NULL; | 533 | return NULL; |
529 | } | 534 | } |
530 | 535 | ||
536 | static int audit_send_reply_thread(void *arg) | ||
537 | { | ||
538 | struct audit_reply *reply = (struct audit_reply *)arg; | ||
539 | |||
540 | mutex_lock(&audit_cmd_mutex); | ||
541 | mutex_unlock(&audit_cmd_mutex); | ||
542 | |||
543 | /* Ignore failure. It'll only happen if the sender goes away, | ||
544 | because our timeout is set to infinite. */ | ||
545 | netlink_unicast(audit_sock, reply->skb, reply->pid, 0); | ||
546 | kfree(reply); | ||
547 | return 0; | ||
548 | } | ||
531 | /** | 549 | /** |
532 | * audit_send_reply - send an audit reply message via netlink | 550 | * audit_send_reply - send an audit reply message via netlink |
533 | * @pid: process id to send reply to | 551 | * @pid: process id to send reply to |
@@ -544,14 +562,26 @@ nlmsg_failure: /* Used by NLMSG_PUT */ | |||
544 | void audit_send_reply(int pid, int seq, int type, int done, int multi, | 562 | void audit_send_reply(int pid, int seq, int type, int done, int multi, |
545 | void *payload, int size) | 563 | void *payload, int size) |
546 | { | 564 | { |
547 | struct sk_buff *skb; | 565 | struct sk_buff *skb; |
566 | struct task_struct *tsk; | ||
567 | struct audit_reply *reply = kmalloc(sizeof(struct audit_reply), | ||
568 | GFP_KERNEL); | ||
569 | |||
570 | if (!reply) | ||
571 | return; | ||
572 | |||
548 | skb = audit_make_reply(pid, seq, type, done, multi, payload, size); | 573 | skb = audit_make_reply(pid, seq, type, done, multi, payload, size); |
549 | if (!skb) | 574 | if (!skb) |
550 | return; | 575 | return; |
551 | /* Ignore failure. It'll only happen if the sender goes away, | 576 | |
552 | because our timeout is set to infinite. */ | 577 | reply->pid = pid; |
553 | netlink_unicast(audit_sock, skb, pid, 0); | 578 | reply->skb = skb; |
554 | return; | 579 | |
580 | tsk = kthread_run(audit_send_reply_thread, reply, "audit_send_reply"); | ||
581 | if (IS_ERR(tsk)) { | ||
582 | kfree(reply); | ||
583 | kfree_skb(skb); | ||
584 | } | ||
555 | } | 585 | } |
556 | 586 | ||
557 | /* | 587 | /* |