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 /kernel | |
| 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>
Diffstat (limited to 'kernel')
| -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 | /* |
