From 8fc6115c2a04099a6e846dc0b2d85cba43821b54 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 6 May 2005 15:54:17 +0100 Subject: AUDIT: expand audit tmp buffer as needed Introduce audit_expand and make the audit_buffer use a dynamic buffer which can be resized. When audit buffer is moved to skb it will not be fragmented across skb's, so we can eliminate the sklist in the audit_buffer. During audit_log_move, we simply copy the full buffer into a single skb, and then audit_log_drain sends it on. Signed-off-by: Chris Wright Signed-off-by: David Woodhouse --- kernel/audit.c | 139 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index e5bdba3e3ae1..c6e31d209c41 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -136,14 +136,11 @@ static DECLARE_MUTEX(audit_netlink_sem); * use simultaneously. */ struct audit_buffer { struct list_head list; - struct sk_buff_head sklist; /* formatted skbs ready to send */ + struct sk_buff *skb; /* formatted skb ready to send */ struct audit_context *ctx; /* NULL or associated context */ int len; /* used area of tmp */ - char tmp[AUDIT_BUFSIZ]; - - /* Pointer to header and contents */ - struct nlmsghdr *nlh; - int total; + int size; /* size of tmp */ + char *tmp; int type; int pid; }; @@ -488,55 +485,47 @@ static void audit_receive(struct sock *sk, int length) static void audit_log_move(struct audit_buffer *ab) { struct sk_buff *skb; + struct nlmsghdr *nlh; char *start; - int extra = ab->nlh ? 0 : NLMSG_SPACE(0); + int len = NLMSG_SPACE(0) + ab->len + 1; /* possible resubmission */ - if (ab->len == 0) + if (ab->skb) return; - skb = skb_peek_tail(&ab->sklist); - if (!skb || skb_tailroom(skb) <= ab->len + extra) { - skb = alloc_skb(2 * ab->len + extra, GFP_ATOMIC); - if (!skb) { - ab->len = 0; /* Lose information in ab->tmp */ - audit_log_lost("out of memory in audit_log_move"); - return; - } - __skb_queue_tail(&ab->sklist, skb); - if (!ab->nlh) - ab->nlh = (struct nlmsghdr *)skb_put(skb, - NLMSG_SPACE(0)); + skb = alloc_skb(len, GFP_ATOMIC); + if (!skb) { + /* Lose information in ab->tmp */ + audit_log_lost("out of memory in audit_log_move"); + return; } + ab->skb = skb; + nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(0)); + nlh->nlmsg_type = ab->type; + nlh->nlmsg_len = ab->len; + nlh->nlmsg_flags = 0; + nlh->nlmsg_pid = ab->pid; + nlh->nlmsg_seq = 0; start = skb_put(skb, ab->len); memcpy(start, ab->tmp, ab->len); - ab->len = 0; } /* Iterate over the skbuff in the audit_buffer, sending their contents * to user space. */ static inline int audit_log_drain(struct audit_buffer *ab) { - struct sk_buff *skb; + struct sk_buff *skb = ab->skb; - while ((skb = skb_dequeue(&ab->sklist))) { + if (skb) { int retval = 0; if (audit_pid) { - if (ab->nlh) { - ab->nlh->nlmsg_len = ab->total; - ab->nlh->nlmsg_type = ab->type; - ab->nlh->nlmsg_flags = 0; - ab->nlh->nlmsg_seq = 0; - ab->nlh->nlmsg_pid = ab->pid; - } skb_get(skb); /* because netlink_* frees */ retval = netlink_unicast(audit_sock, skb, audit_pid, MSG_DONTWAIT); } if (retval == -EAGAIN && (atomic_read(&audit_backlog)) < audit_backlog_limit) { - skb_queue_head(&ab->sklist, skb); audit_log_end_irq(ab); return 1; } @@ -550,13 +539,12 @@ static inline int audit_log_drain(struct audit_buffer *ab) audit_log_lost("netlink socket too busy"); } if (!audit_pid) { /* No daemon */ - int offset = ab->nlh ? NLMSG_SPACE(0) : 0; + int offset = NLMSG_SPACE(0); int len = skb->len - offset; skb->data[offset + len] = '\0'; printk(KERN_ERR "%s\n", skb->data + offset); } kfree_skb(skb); - ab->nlh = NULL; } return 0; } @@ -624,6 +612,10 @@ static void audit_buffer_free(struct audit_buffer *ab) { unsigned long flags; + if (!ab) + return; + + kfree(ab->tmp); atomic_dec(&audit_backlog); spin_lock_irqsave(&audit_freelist_lock, flags); if (++audit_freelist_count > AUDIT_MAXFREE) @@ -633,7 +625,8 @@ static void audit_buffer_free(struct audit_buffer *ab) spin_unlock_irqrestore(&audit_freelist_lock, flags); } -static struct audit_buffer * audit_buffer_alloc(int gfp_mask) +static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, + int gfp_mask) { unsigned long flags; struct audit_buffer *ab = NULL; @@ -650,11 +643,24 @@ static struct audit_buffer * audit_buffer_alloc(int gfp_mask) if (!ab) { ab = kmalloc(sizeof(*ab), GFP_ATOMIC); if (!ab) - goto out; + goto err; } atomic_inc(&audit_backlog); -out: + + ab->tmp = kmalloc(AUDIT_BUFSIZ, GFP_ATOMIC); + if (!ab->tmp) + goto err; + + ab->skb = NULL; + ab->ctx = ctx; + ab->len = 0; + ab->size = AUDIT_BUFSIZ; + ab->type = AUDIT_KERNEL; + ab->pid = 0; return ab; +err: + audit_buffer_free(ab); + return NULL; } /* Obtain an audit buffer. This routine does locking to obtain the @@ -684,21 +690,12 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) return NULL; } - ab = audit_buffer_alloc(GFP_ATOMIC); + ab = audit_buffer_alloc(ctx, GFP_ATOMIC); if (!ab) { audit_log_lost("out of memory in audit_log_start"); return NULL; } - skb_queue_head_init(&ab->sklist); - - ab->ctx = ctx; - ab->len = 0; - ab->nlh = NULL; - ab->total = 0; - ab->type = AUDIT_KERNEL; - ab->pid = 0; - #ifdef CONFIG_AUDITSYSCALL if (ab->ctx) audit_get_stamp(ab->ctx, &t, &serial); @@ -713,6 +710,27 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) return ab; } +/** + * audit_expand - expand tmp buffer in the audit buffer + * @ab: audit_buffer + * + * Returns 0 (no space) on failed expansion, or available space if + * successful. + */ +static inline int audit_expand(struct audit_buffer *ab) +{ + char *tmp; + int len = ab->size + AUDIT_BUFSIZ; + + tmp = kmalloc(len, GFP_ATOMIC); + if (!tmp) + return 0; + memcpy(tmp, ab->tmp, ab->len); + kfree(ab->tmp); + ab->tmp = tmp; + ab->size = len; + return ab->size - ab->len; +} /* Format an audit message into the audit buffer. If there isn't enough * room in the audit buffer, more room will be allocated and vsnprint @@ -726,22 +744,25 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, if (!ab) return; - avail = sizeof(ab->tmp) - ab->len; + avail = ab->size - ab->len; if (avail <= 0) { - audit_log_move(ab); - avail = sizeof(ab->tmp) - ab->len; + avail = audit_expand(ab); + if (!avail) + goto out; } - len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); + len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); if (len >= avail) { /* The printk buffer is 1024 bytes long, so if we get * here and AUDIT_BUFSIZ is at least 1024, then we can * log everything that printk could have logged. */ - audit_log_move(ab); - avail = sizeof(ab->tmp) - ab->len; - len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); + avail = audit_expand(ab); + if (!avail) + goto out; + len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); } ab->len += (len < avail) ? len : avail; - ab->total += (len < avail) ? len : avail; +out: + return; } /* Format a message into the audit buffer. All the work is done in @@ -789,21 +810,19 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, char *p; int len, avail; - if (prefix) audit_log_format(ab, " %s", prefix); + if (prefix) + audit_log_format(ab, " %s", prefix); - if (ab->len > 128) - audit_log_move(ab); - avail = sizeof(ab->tmp) - ab->len; + avail = ab->size - ab->len; p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail); if (IS_ERR(p)) { /* FIXME: can we save some information here? */ audit_log_format(ab, ""); } else { /* path isn't at start of buffer */ - len = (ab->tmp + sizeof(ab->tmp) - 1) - p; + len = (ab->tmp + ab->size - 1) - p; memmove(ab->tmp + ab->len, p, len); ab->len += len; - ab->total += len; } } -- cgit v1.2.3-59-g8ed1b